50 Commits

Author SHA1 Message Date
3452cc1c8c Fix macro 2018-02-23 09:23:53 +00:00
2ab4f77498 Test C++11 mode 2018-02-23 09:17:53 +00:00
883df6b427 Publish docs 2017-12-18 10:55:36 +00:00
f621ca663e Standardese 2017-12-18 10:55:12 +00:00
8b408f5fd6 More docs 2017-12-18 10:51:48 +00:00
632bc4e1ad Update readme 2017-12-18 10:21:46 +00:00
717027db9b More tests 2017-12-18 10:10:11 +00:00
8c3f042b12 More tests 2017-12-18 09:56:36 +00:00
c4e3b3ee14 Get references compiling, initial tests 2017-12-14 14:53:35 +00:00
bdaa41ce8c Initial support for references 2017-12-13 16:03:01 +00:00
e589ae4321 Add static asserts 2017-12-13 11:34:33 +00:00
6c73b8113f Bases fix 2017-12-13 09:13:05 +00:00
32656c4556 Fix CMake 2017-12-05 19:53:49 +00:00
b3dc6d1a52 Put header in 'tl' folder 2017-12-05 19:51:42 +00:00
247677394f Support mapping functions which return references 2017-12-05 19:51:20 +00:00
5a3f094ec7 Update docs 2017-11-27 15:12:44 +00:00
26c00afe27 Fix bases test 2017-11-27 15:08:34 +00:00
4308924a38 Fix GCC issues 2017-11-27 14:51:32 +00:00
6c4178a947 More testing 2017-11-27 14:36:59 +00:00
c67aaff948 Update mutexes 2017-11-27 12:24:47 +00:00
b9486ce776 Clang format, support cohabitation with tl::expected 2017-11-16 11:17:26 +00:00
a1d27b2aba Merge branch 'master' of github.com:TartanLlama/monadic-optional-new 2017-11-06 08:51:46 +00:00
d9ce8cf5c5 Update readme 2017-11-06 08:51:33 +00:00
e62348c128 More testing 2017-11-05 20:31:18 +00:00
39030e6a00 Fix ifdef 2017-11-03 14:29:35 +00:00
4d5f9bd785 Fix ifdef 2017-11-03 14:29:21 +00:00
f6802daada Clang format 2017-11-03 14:29:02 +00:00
59cf950873 Upgrade to Catch 2 2017-11-03 14:19:44 +00:00
e4e32ea56d Fix assignment chaining 2017-11-02 08:35:04 +00:00
18a2500d55 Update doc markdown 2017-11-01 20:28:07 +00:00
fe07dfaabb More docs 2017-11-01 20:27:45 +00:00
97cdb4e046 Update docs 2017-11-01 20:24:11 +00:00
35c7af0b67 Remove some hard resets 2017-11-01 20:06:03 +00:00
6e9eadc2bd Fix clang 2017-11-01 20:01:46 +00:00
08550f0959 Clean up code 2017-11-01 19:54:18 +00:00
0bb694cce8 Fix GCC4.9 2017-11-01 19:46:46 +00:00
adca4b597b Fix noexcept 2017-11-01 19:03:45 +00:00
8e7ed1d83c Merge branch 'master' of github.com:TartanLlama/monadic-optional-new 2017-11-01 15:44:08 +00:00
3720227b2d Propagate triviality, conditionally delete special members 2017-11-01 15:43:58 +00:00
45b6e55233 Add version defines 2017-10-29 21:55:27 +00:00
7cfe5f71e4 Fix tests 2017-10-29 20:22:55 +00:00
4e00a30e7b Fix GCC 5.4 bug 2017-10-29 20:07:54 +00:00
9a6f22d811 Ffal constexpr fixes 2017-10-25 09:30:54 +01:00
31da15a391 Constexpr fixes 2017-10-24 16:04:31 +01:00
09249f3be0 More constexpr fixes 2017-10-24 15:59:38 +01:00
ae88cda44a More constexpr fixes 2017-10-24 15:55:40 +01:00
1b9d589024 Constexpr fixes 2017-10-24 15:46:07 +01:00
0df06afe8d Fix #2 2017-10-24 15:32:22 +01:00
98f34bed66 Fix #1 2017-10-24 15:07:29 +01:00
9fae73d55b Change namespace in README 2017-10-24 09:10:58 +01:00
14 changed files with 12606 additions and 10683 deletions

View File

@ -12,7 +12,7 @@ matrix:
- ubuntu-toolchain-r-test
packages:
- g++-5
env: COMPILER=g++-5
env: COMPILER=g++-5 CXXSTD=11
- compiler: gcc
addons:
apt:
@ -20,7 +20,7 @@ matrix:
- ubuntu-toolchain-r-test
packages:
- g++-4.9
env: COMPILER=g++-4.9
env: COMPILER=g++-4.9 CXXSTD=11
- compiler: gcc
addons:
apt:
@ -28,7 +28,7 @@ matrix:
- ubuntu-toolchain-r-test
packages:
- g++-4.8
env: COMPILER=g++-4.8
env: COMPILER=g++-4.8 CXXSTD=11
- compiler: clang
addons:
apt:
@ -38,7 +38,7 @@ matrix:
packages:
- clang++-3.5
- libc++-dev
env: COMPILER=clang++-3.5
env: COMPILER=clang++-3.5 CXXSTD=11
- compiler: clang
addons:
apt:
@ -48,7 +48,7 @@ matrix:
packages:
- clang++-3.6
- libc++-dev
env: COMPILER=clang++-3.6
env: COMPILER=clang++-3.6 CXXSTD=11
- compiler: clang
addons:
apt:
@ -58,7 +58,7 @@ matrix:
packages:
- clang++-3.7
- libc++-dev
env: COMPILER=clang++-3.7
env: COMPILER=clang++-3.7 CXXSTD=11
- compiler: clang
addons:
apt:
@ -68,7 +68,7 @@ matrix:
packages:
- clang++-3.8
- libc++-dev
env: COMPILER=clang++-3.8
env: COMPILER=clang++-3.8 CXXSTD=11
- compiler: clang
addons:
apt:
@ -79,11 +79,70 @@ matrix:
packages:
- clang++-3.9
- libc++-dev
env: COMPILER=clang++-3.9
env: COMPILER=clang++-3.9 CXXSTD=11
- compiler: gcc
addons:
apt:
sources:
- ubuntu-toolchain-r-test
packages:
- g++-5
env: COMPILER=g++-5 CXXSTD=14
- compiler: clang
addons:
apt:
sources:
- llvm-toolchain-precise-3.5
- ubuntu-toolchain-r-test
packages:
- clang++-3.5
- libc++-dev
env: COMPILER=clang++-3.5 CXXSTD=14
- compiler: clang
addons:
apt:
sources:
- llvm-toolchain-precise-3.6
- ubuntu-toolchain-r-test
packages:
- clang++-3.6
- libc++-dev
env: COMPILER=clang++-3.6 CXXSTD=14
- compiler: clang
addons:
apt:
sources:
- llvm-toolchain-precise-3.7
- ubuntu-toolchain-r-test
packages:
- clang++-3.7
- libc++-dev
env: COMPILER=clang++-3.7 CXXSTD=14
- compiler: clang
addons:
apt:
sources:
- llvm-toolchain-precise-3.8
- ubuntu-toolchain-r-test
packages:
- clang++-3.8
- libc++-dev
env: COMPILER=clang++-3.8 CXXSTD=14
- compiler: clang
addons:
apt:
sources:
- sourceline: "deb http://apt.llvm.org/trusty/ llvm-toolchain-trusty-3.9 main"
key_url: "http://apt.llvm.org/llvm-snapshot.gpg.key"
- ubuntu-toolchain-r-test
packages:
- clang++-3.9
- libc++-dev
env: COMPILER=clang++-3.9 CXXSTD=14
install:
- if [ "$CXX" = "clang++" ]; then export CXX="$COMPILER -stdlib=libc++"; fi
- if [ "$CXX" = "g++" ]; then export CXX="$COMPILER"; fi
script: mkdir build && cd build && cmake .. && make && ./tests
script: mkdir build && cd build && cmake -DCXXSTD=$CXXSTD .. && make && ./tests

View File

@ -18,24 +18,26 @@ set(TEST_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/tests/main.cpp
${CMAKE_CURRENT_SOURCE_DIR}/tests/constexpr.cpp
${CMAKE_CURRENT_SOURCE_DIR}/tests/constructors.cpp
${CMAKE_CURRENT_SOURCE_DIR}/tests/assignment.cpp
${CMAKE_CURRENT_SOURCE_DIR}/tests/bases.cpp
${CMAKE_CURRENT_SOURCE_DIR}/tests/nullopt.cpp)
add_executable(tests ${TEST_SOURCES})
add_library(optional INTERFACE)
target_sources(optional INTERFACE ${CMAKE_SOURCE_DIR}/optional.hpp)
target_include_directories(optional INTERFACE ${CMAKE_SOURCE_DIR})
target_sources(optional INTERFACE ${CMAKE_SOURCE_DIR}/tl/optional.hpp)
target_include_directories(optional INTERFACE ${CMAKE_SOURCE_DIR}/tl)
target_link_libraries(tests Catch optional)
set_property(TARGET tests PROPERTY CXX_STANDARD 14)
set(CXXSTD 14 CACHE STRING "C++ standard to use, default C++14")
set_property(TARGET tests PROPERTY CXX_STANDARD ${CXXSTD})
find_package(standardese) # find standardese after installation
# generates a custom target that will run standardese to generate the documentation
if (standardese_FOUND)
standardese_generate(optional
INCLUDE_DIRECTORY .
INCLUDE_DIRECTORY tl
CONFIG ${CMAKE_SOURCE_DIR}/standardese.config
INPUT optional.hpp)
INPUT tl/optional.hpp)
endif ()

View File

@ -1,5 +1,5 @@
# optional
Single header, work-in-progress implementation of `std::optional` with functional-style extensions.
Single header implementation of `std::optional` with functional-style extensions and support for references.
Clang + GCC: [![Linux Build Status](https://travis-ci.org/TartanLlama/optional.png?branch=master)](https://travis-ci.org/TartanLlama/optional)
MSVC: [![Windows Build Status](https://ci.appveyor.com/api/projects/status/k5x00xa11y3s5wsg?svg=true)](https://ci.appveyor.com/project/TartanLlama/optional)
@ -32,7 +32,7 @@ std::optional<image> get_cute_cat (const image& img) {
You can do this:
```
std::optional<image> get_cute_cat (const image& img) {
tl::optional<image> get_cute_cat (const image& img) {
return crop_to_cat(img)
.and_then(add_bow_tie)
.and_then(make_eyes_sparkle)
@ -66,6 +66,26 @@ The interface is the same as `std::optional`, but the following member functions
- `take`: returns the current value, leaving the optional empty.
* `opt_string.take().map(&std::string::size); //opt_string now empty;`
In addition to those member functions, optional references are also supported:
```
int i = 42;
tl::optional<int&> o = i;
*o == 42; //true
i = 12;
*o = 12; //true
&*o == &i; //true
```
Assignment has rebind semantics rather than assign-through semantics:
```
int j = 8;
o = j;
&*o == &j; //true
```
### Compiler support
Tested on:

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

3
standardese.config Normal file
View File

@ -0,0 +1,3 @@
[output]
format=commonmark
link_extension=html

View File

@ -1,7 +1,7 @@
#include "catch.hpp"
#include "optional.hpp"
TEST_CASE("Assignment", "[assignment]") {
TEST_CASE("Assignment value", "[assignment.value]") {
tl::optional<int> o1 = 42;
tl::optional<int> o2 = 12;
tl::optional<int> o3;
@ -32,3 +32,40 @@ TEST_CASE("Assignment", "[assignment]") {
o1 = std::move(o4);
REQUIRE(*o1 == 42);
}
TEST_CASE("Assignment reference", "[assignment.ref]") {
auto i = 42;
auto j = 12;
tl::optional<int&> o1 = i;
tl::optional<int&> o2 = j;
tl::optional<int&> o3;
o1 = o1;
REQUIRE(*o1 == 42);
REQUIRE(&*o1 == &i);
o1 = o2;
REQUIRE(*o1 == 12);
o1 = o3;
REQUIRE(!o1);
auto k = 42;
o1 = k;
REQUIRE(*o1 == 42);
REQUIRE(*o1 == i);
REQUIRE(*o1 == k);
REQUIRE(&*o1 != &i);
REQUIRE(&*o1 == &k);
k = 12;
REQUIRE(*o1 == 12);
o1 = tl::nullopt;
REQUIRE(!o1);
o1 = std::move(o2);
REQUIRE(*o1 == 12);
}

107
tests/bases.cpp Normal file
View File

@ -0,0 +1,107 @@
#include "catch.hpp"
#include "optional.hpp"
// Old versions of GCC don't have the correct trait names. Could fix them up if needs be.
#if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 && \
!defined(__clang__))
// nothing for now
#else
TEST_CASE("Triviality", "[bases.triviality]") {
REQUIRE(std::is_trivially_copy_constructible<tl::optional<int>>::value);
REQUIRE(std::is_trivially_copy_assignable<tl::optional<int>>::value);
REQUIRE(std::is_trivially_move_constructible<tl::optional<int>>::value);
REQUIRE(std::is_trivially_move_assignable<tl::optional<int>>::value);
REQUIRE(std::is_trivially_destructible<tl::optional<int>>::value);
{
struct T {
T(const T&) = default;
T(T&&) = default;
T& operator=(const T&) = default;
T& operator=(T&&) = default;
~T() = default;
};
REQUIRE(std::is_trivially_copy_constructible<tl::optional<T>>::value);
REQUIRE(std::is_trivially_copy_assignable<tl::optional<T>>::value);
REQUIRE(std::is_trivially_move_constructible<tl::optional<T>>::value);
REQUIRE(std::is_trivially_move_assignable<tl::optional<T>>::value);
REQUIRE(std::is_trivially_destructible<tl::optional<T>>::value);
}
{
struct T {
T(const T&){}
T(T&&) {};
T& operator=(const T&) {}
T& operator=(T&&) {};
~T(){}
};
REQUIRE(!std::is_trivially_copy_constructible<tl::optional<T>>::value);
REQUIRE(!std::is_trivially_copy_assignable<tl::optional<T>>::value);
REQUIRE(!std::is_trivially_move_constructible<tl::optional<T>>::value);
REQUIRE(!std::is_trivially_move_assignable<tl::optional<T>>::value);
REQUIRE(!std::is_trivially_destructible<tl::optional<T>>::value);
}
}
TEST_CASE("Deletion", "[bases.deletion]") {
REQUIRE(std::is_copy_constructible<tl::optional<int>>::value);
REQUIRE(std::is_copy_assignable<tl::optional<int>>::value);
REQUIRE(std::is_move_constructible<tl::optional<int>>::value);
REQUIRE(std::is_move_assignable<tl::optional<int>>::value);
REQUIRE(std::is_destructible<tl::optional<int>>::value);
{
struct T {
T(const T&) = default;
T(T&&) = default;
T& operator=(const T&) = default;
T& operator=(T&&) = default;
~T() = default;
};
REQUIRE(std::is_copy_constructible<tl::optional<T>>::value);
REQUIRE(std::is_copy_assignable<tl::optional<T>>::value);
REQUIRE(std::is_move_constructible<tl::optional<T>>::value);
REQUIRE(std::is_move_assignable<tl::optional<T>>::value);
REQUIRE(std::is_destructible<tl::optional<T>>::value);
}
{
struct T {
T(const T&)=delete;
T(T&&)=delete;
T& operator=(const T&)=delete;
T& operator=(T&&)=delete;
};
REQUIRE(!std::is_copy_constructible<tl::optional<T>>::value);
REQUIRE(!std::is_copy_assignable<tl::optional<T>>::value);
REQUIRE(!std::is_move_constructible<tl::optional<T>>::value);
REQUIRE(!std::is_move_assignable<tl::optional<T>>::value);
}
{
struct T {
T(const T&)=delete;
T(T&&)=default;
T& operator=(const T&)=delete;
T& operator=(T&&)=default;
};
REQUIRE(!std::is_copy_constructible<tl::optional<T>>::value);
REQUIRE(!std::is_copy_assignable<tl::optional<T>>::value);
REQUIRE(std::is_move_constructible<tl::optional<T>>::value);
REQUIRE(std::is_move_assignable<tl::optional<T>>::value);
}
{
struct T {
T(const T&)=default;
T(T&&)=delete;
T& operator=(const T&)=default;
T& operator=(T&&)=delete;
};
REQUIRE(std::is_copy_constructible<tl::optional<T>>::value);
REQUIRE(std::is_copy_assignable<tl::optional<T>>::value);
}
}
#endif

File diff suppressed because it is too large Load Diff

View File

@ -2,30 +2,49 @@
#include "optional.hpp"
TEST_CASE("Constructors", "[constructors]") {
tl::optional<int> o1;
REQUIRE(!o1);
tl::optional<int> o1;
REQUIRE(!o1);
tl::optional<int> o2 = tl::nullopt;
REQUIRE(!o2);
tl::optional<int> o2 = tl::nullopt;
REQUIRE(!o2);
tl::optional<int> o3 = 42;
REQUIRE(*o3 == 42);
tl::optional<int> o3 = 42;
REQUIRE(*o3 == 42);
tl::optional<int> o4 = o3;
REQUIRE(*o4 == 42);
tl::optional<int> o4 = o3;
REQUIRE(*o4 == 42);
tl::optional<int> o5 = o1;
REQUIRE(!o5);
tl::optional<int> o5 = o1;
REQUIRE(!o5);
tl::optional<int> o6 = std::move(o3);
REQUIRE(*o6 == 42);
tl::optional<int> o6 = std::move(o3);
REQUIRE(*o6 == 42);
tl::optional<short> o7 = 42;
REQUIRE(*o7 == 42);
tl::optional<short> o7 = 42;
REQUIRE(*o7 == 42);
tl::optional<int> o8 = o7;
REQUIRE(*o8 == 42);
tl::optional<int> o8 = o7;
REQUIRE(*o8 == 42);
tl::optional<int> o9 = std::move(o7);
REQUIRE(*o9 == 42);
tl::optional<int> o9 = std::move(o7);
REQUIRE(*o9 == 42);
{
tl::optional<int &> o;
REQUIRE(!o);
tl::optional<int &> oo = o;
REQUIRE(!oo);
}
{
auto i = 42;
tl::optional<int &> o = i;
REQUIRE(o);
REQUIRE(*o == 42);
tl::optional<int &> oo = o;
REQUIRE(oo);
REQUIRE(*oo == 42);
}
}

View File

@ -12,342 +12,261 @@ constexpr int get_int(int) { return 42; }
TL_OPTIONAL_11_CONSTEXPR tl::optional<int> get_opt_int(int) { return 42; }
// What is Clang Format up to?!
TEST_CASE("Monadic operations",
"[monadic]"){SECTION("map"){// lhs is empty
tl::optional<int> o1;
auto o1r = o1.map([](int i) { return i + 2; });
STATIC_REQUIRE((std::is_same<decltype(o1r), tl::optional<int>>::value));
REQUIRE(!o1r);
TEST_CASE("Monadic operations", "[monadic]") {
SECTION("map") { // lhs is empty
tl::optional<int> o1;
auto o1r = o1.map([](int i) { return i + 2; });
STATIC_REQUIRE((std::is_same<decltype(o1r), tl::optional<int>>::value));
REQUIRE(!o1r);
// lhs has value
tl::optional<int> o2 = 40;
auto o2r = o2.map([](int i) { return i + 2; });
STATIC_REQUIRE((std::is_same<decltype(o2r), tl::optional<int>>::value));
REQUIRE(o2r.value() == 42);
// lhs has value
tl::optional<int> o2 = 40;
auto o2r = o2.map([](int i) { return i + 2; });
STATIC_REQUIRE((std::is_same<decltype(o2r), tl::optional<int>>::value));
REQUIRE(o2r.value() == 42);
struct rval_call_map {
double operator()(int) && { return 42.0; };
};
// ensure that function object is forwarded
tl::optional<int> o3 = 42;
auto o3r = o3.map(rval_call_map{});
STATIC_REQUIRE((std::is_same<decltype(o3r), tl::optional<double>>::value));
REQUIRE(o3r.value() == 42);
// ensure that lhs is forwarded
tl::optional<int> o4 = 40;
auto o4r = std::move(o4).map([](int &&i) { return i + 2; });
STATIC_REQUIRE((std::is_same<decltype(o4r), tl::optional<int>>::value));
REQUIRE(o4r.value() == 42);
// ensure that lhs is const-propagated
const tl::optional<int> o5 = 40;
auto o5r = o5.map([](const int &i) { return i + 2; });
STATIC_REQUIRE((std::is_same<decltype(o5r), tl::optional<int>>::value));
REQUIRE(o5r.value() == 42);
// test applicative functor
tl::optional<int> o6 = 40;
auto f6 = tl::make_optional([](const int &i) { return i + 2; });
auto o6r = o6.map(f6);
STATIC_REQUIRE((std::is_same<decltype(o6r), tl::optional<int>>::value));
REQUIRE(o6r.value() == 42);
// test void return
tl::optional<int> o7 = 40;
auto f7 = tl::make_optional([](const int &i) { return; });
auto o7r = o7.map(f7);
STATIC_REQUIRE(
(std::is_same<decltype(o7r), tl::optional<tl::monostate>>::value));
REQUIRE(o6r.has_value());
// test each overload in turn
tl::optional<int> o8 = 42;
auto o8r = o8.map([](int) { return 42; });
REQUIRE(*o8r == 42);
tl::optional<int> o9 = 42;
auto o9r = o9.map([](int) { return; });
REQUIRE(o9r);
tl::optional<int> o10 = 42;
auto o10r = o10.map(tl::make_optional([](int) { return 42; }));
REQUIRE(*o10r == 42);
tl::optional<int> o11 = 42;
auto o11r = o11.map(tl::make_optional([](int) { return; }));
REQUIRE(o11r);
tl::optional<int> o12 = 42;
auto o12r = std::move(o12).map([](int) { return 42; });
REQUIRE(*o12r == 42);
tl::optional<int> o13 = 42;
auto o13r = std::move(o13).map([](int) { return; });
REQUIRE(o13r);
tl::optional<int> o14 = 42;
auto o14r = std::move(o14).map(tl::make_optional([](int) { return 42; }));
REQUIRE(*o14r == 42);
tl::optional<int> o15 = 42;
auto o15r = std::move(o15).map(tl::make_optional([](int) { return; }));
REQUIRE(o15r);
const tl::optional<int> o16 = 42;
auto o16r = o16.map([](int) { return 42; });
REQUIRE(*o16r == 42);
const tl::optional<int> o17 = 42;
auto o17r = o17.map([](int) { return; });
REQUIRE(o17r);
const tl::optional<int> o18 = 42;
auto o18r = o18.map(tl::make_optional([](int) { return 42; }));
REQUIRE(*o18r == 42);
const tl::optional<int> o19 = 42;
auto o19r = o19.map(tl::make_optional([](int) { return; }));
REQUIRE(o19r);
const tl::optional<int> o20 = 42;
auto o20r = std::move(o20).map([](int) { return 42; });
REQUIRE(*o20r == 42);
const tl::optional<int> o21 = 42;
auto o21r = std::move(o21).map([](int) { return; });
REQUIRE(o21r);
const tl::optional<int> o22 = 42;
auto o22r = std::move(o22).map(tl::make_optional([](int) { return 42; }));
REQUIRE(*o22r == 42);
const tl::optional<int> o23 = 42;
auto o23r = std::move(o23).map(tl::make_optional([](int) { return; }));
REQUIRE(o23r);
tl::optional<int> o24 = tl::nullopt;
auto o24r = o24.map([](int) { return 42; });
REQUIRE(!o24r);
tl::optional<int> o25 = tl::nullopt;
auto o25r = o25.map([](int) { return; });
REQUIRE(!o25r);
tl::optional<int> o26 = tl::nullopt;
auto o26r = o26.map(tl::make_optional([](int) { return 42; }));
REQUIRE(!o26r);
tl::optional<int> o27 = tl::nullopt;
auto o27r = o27.map(tl::make_optional([](int) { return; }));
REQUIRE(!o27r);
tl::optional<int> o28 = tl::nullopt;
auto o28r = std::move(o28).map([](int) { return 42; });
REQUIRE(!o28r);
tl::optional<int> o29 = tl::nullopt;
auto o29r = std::move(o29).map([](int) { return; });
REQUIRE(!o29r);
tl::optional<int> o30 = tl::nullopt;
auto o30r = std::move(o30).map(tl::make_optional([](int) { return 42; }));
REQUIRE(!o30r);
tl::optional<int> o31 = tl::nullopt;
auto o31r = std::move(o31).map(tl::make_optional([](int) { return; }));
REQUIRE(!o31r);
const tl::optional<int> o32 = tl::nullopt;
auto o32r = o32.map([](int) { return 42; });
REQUIRE(!o32r);
const tl::optional<int> o33 = tl::nullopt;
auto o33r = o33.map([](int) { return; });
REQUIRE(!o33r);
const tl::optional<int> o34 = tl::nullopt;
auto o34r = o34.map(tl::make_optional([](int) { return 42; }));
REQUIRE(!o34r);
const tl::optional<int> o35 = tl::nullopt;
auto o35r = o35.map(tl::make_optional([](int) { return; }));
REQUIRE(!o35r);
const tl::optional<int> o36 = tl::nullopt;
auto o36r = std::move(o36).map([](int) { return 42; });
REQUIRE(!o36r);
const tl::optional<int> o37 = tl::nullopt;
auto o37r = std::move(o37).map([](int) { return; });
REQUIRE(!o37r);
const tl::optional<int> o38 = tl::nullopt;
auto o38r = std::move(o38).map(tl::make_optional([](int) { return 42; }));
REQUIRE(!o38r);
const tl::optional<int> o39 = tl::nullopt;
auto o39r = std::move(o39).map(tl::make_optional([](int) { return; }));
REQUIRE(!o39r);
}
SECTION("map constexpr") {
#if !defined(_MSC_VER) && !defined(TL_OPTIONAL_GCC49)
// test each overload in turn
constexpr tl::optional<int> o16 = 42;
constexpr auto o16r = o16.map(get_int);
STATIC_REQUIRE(*o16r == 42);
constexpr tl::optional<int> o18 = 42;
constexpr auto opt_int = tl::make_optional(get_int);
constexpr auto o18r = o18.map(opt_int);
STATIC_REQUIRE(*o18r == 42);
constexpr tl::optional<int> o20 = 42;
constexpr auto o20r = std::move(o20).map(get_int);
STATIC_REQUIRE(*o20r == 42);
constexpr tl::optional<int> o22 = 42;
constexpr auto o22r = std::move(o22).map(opt_int);
STATIC_REQUIRE(*o22r == 42);
constexpr tl::optional<int> o32 = tl::nullopt;
constexpr auto o32r = o32.map(get_int);
STATIC_REQUIRE(!o32r);
constexpr tl::optional<int> o34 = tl::nullopt;
constexpr auto o34r = o34.map(opt_int);
STATIC_REQUIRE(!o34r);
constexpr tl::optional<int> o36 = tl::nullopt;
constexpr auto o36r = std::move(o36).map(get_int);
STATIC_REQUIRE(!o36r);
constexpr tl::optional<int> o38 = tl::nullopt;
constexpr auto o38r = std::move(o38).map(opt_int);
STATIC_REQUIRE(!o38r);
#endif
}
SECTION("and_then") {
// lhs is empty
tl::optional<int> o1;
auto o1r = o1.and_then([](int i) { return tl::optional<float>{42}; });
STATIC_REQUIRE((std::is_same<decltype(o1r), tl::optional<float>>::value));
REQUIRE(!o1r);
// lhs has value
tl::optional<int> o2 = 12;
auto o2r = o2.and_then([](int i) { return tl::optional<float>{42}; });
STATIC_REQUIRE((std::is_same<decltype(o2r), tl::optional<float>>::value));
REQUIRE(o2r.value() == 42.f);
// lhs is empty, rhs returns empty
tl::optional<int> o3;
auto o3r = o3.and_then([](int i) { return tl::optional<float>{}; });
STATIC_REQUIRE((std::is_same<decltype(o3r), tl::optional<float>>::value));
REQUIRE(!o3r);
// rhs returns empty
tl::optional<int> o4 = 12;
auto o4r = o4.and_then([](int i) { return tl::optional<float>{}; });
STATIC_REQUIRE((std::is_same<decltype(o4r), tl::optional<float>>::value));
REQUIRE(!o4r);
struct rval_call_and_then {
tl::optional<double> operator()(int) && {
return tl::optional<double>(42.0);
struct rval_call_map {
double operator()(int) && { return 42.0; };
};
};
// ensure that function object is forwarded
tl::optional<int> o5 = 42;
auto o5r = o5.and_then(rval_call_and_then{});
STATIC_REQUIRE((std::is_same<decltype(o5r), tl::optional<double>>::value));
REQUIRE(o5r.value() == 42);
// ensure that function object is forwarded
tl::optional<int> o3 = 42;
auto o3r = o3.map(rval_call_map{});
STATIC_REQUIRE((std::is_same<decltype(o3r), tl::optional<double>>::value));
REQUIRE(o3r.value() == 42);
// ensure that lhs is forwarded
tl::optional<int> o6 = 42;
auto o6r =
std::move(o6).and_then([](int &&i) { return tl::optional<double>(i); });
STATIC_REQUIRE((std::is_same<decltype(o6r), tl::optional<double>>::value));
REQUIRE(o6r.value() == 42);
// ensure that lhs is forwarded
tl::optional<int> o4 = 40;
auto o4r = std::move(o4).map([](int &&i) { return i + 2; });
STATIC_REQUIRE((std::is_same<decltype(o4r), tl::optional<int>>::value));
REQUIRE(o4r.value() == 42);
// ensure that function object is const-propagated
const tl::optional<int> o7 = 42;
auto o7r = o7.and_then([](const int &i) { return tl::optional<double>(i); });
STATIC_REQUIRE((std::is_same<decltype(o7r), tl::optional<double>>::value));
REQUIRE(o7r.value() == 42);
// ensure that lhs is const-propagated
const tl::optional<int> o5 = 40;
auto o5r = o5.map([](const int &i) { return i + 2; });
STATIC_REQUIRE((std::is_same<decltype(o5r), tl::optional<int>>::value));
REQUIRE(o5r.value() == 42);
// test each overload in turn
tl::optional<int> o8 = 42;
auto o8r = o8.and_then([](int i) { return tl::make_optional(42); });
REQUIRE(*o8r == 42);
// test void return
tl::optional<int> o7 = 40;
auto f7 = [](const int &i) { return; };
auto o7r = o7.map(f7);
STATIC_REQUIRE(
(std::is_same<decltype(o7r), tl::optional<tl::monostate>>::value));
REQUIRE(o7r.has_value());
tl::optional<int> o9 = 42;
auto o9r =
std::move(o9).and_then([](int i) { return tl::make_optional(42); });
REQUIRE(*o9r == 42);
// test each overload in turn
tl::optional<int> o8 = 42;
auto o8r = o8.map([](int) { return 42; });
REQUIRE(*o8r == 42);
const tl::optional<int> o10 = 42;
auto o10r = o10.and_then([](int i) { return tl::make_optional(42); });
REQUIRE(*o10r == 42);
tl::optional<int> o9 = 42;
auto o9r = o9.map([](int) { return; });
REQUIRE(o9r);
const tl::optional<int> o11 = 42;
auto o11r =
std::move(o11).and_then([](int i) { return tl::make_optional(42); });
REQUIRE(*o11r == 42);
tl::optional<int> o12 = 42;
auto o12r = std::move(o12).map([](int) { return 42; });
REQUIRE(*o12r == 42);
tl::optional<int> o16 = tl::nullopt;
auto o16r = o16.and_then([](int i) { return tl::make_optional(42); });
REQUIRE(!o16r);
tl::optional<int> o13 = 42;
auto o13r = std::move(o13).map([](int) { return; });
REQUIRE(o13r);
tl::optional<int> o17 = tl::nullopt;
auto o17r =
std::move(o17).and_then([](int i) { return tl::make_optional(42); });
REQUIRE(!o17r);
const tl::optional<int> o16 = 42;
auto o16r = o16.map([](int) { return 42; });
REQUIRE(*o16r == 42);
const tl::optional<int> o18 = tl::nullopt;
auto o18r = o18.and_then([](int i) { return tl::make_optional(42); });
REQUIRE(!o18r);
const tl::optional<int> o17 = 42;
auto o17r = o17.map([](int) { return; });
REQUIRE(o17r);
const tl::optional<int> o19 = tl::nullopt;
auto o19r =
std::move(o19).and_then([](int i) { return tl::make_optional(42); });
REQUIRE(!o19r);
}
const tl::optional<int> o20 = 42;
auto o20r = std::move(o20).map([](int) { return 42; });
REQUIRE(*o20r == 42);
SECTION("constexpr and_then") {
const tl::optional<int> o21 = 42;
auto o21r = std::move(o21).map([](int) { return; });
REQUIRE(o21r);
tl::optional<int> o24 = tl::nullopt;
auto o24r = o24.map([](int) { return 42; });
REQUIRE(!o24r);
tl::optional<int> o25 = tl::nullopt;
auto o25r = o25.map([](int) { return; });
REQUIRE(!o25r);
tl::optional<int> o28 = tl::nullopt;
auto o28r = std::move(o28).map([](int) { return 42; });
REQUIRE(!o28r);
tl::optional<int> o29 = tl::nullopt;
auto o29r = std::move(o29).map([](int) { return; });
REQUIRE(!o29r);
const tl::optional<int> o32 = tl::nullopt;
auto o32r = o32.map([](int) { return 42; });
REQUIRE(!o32r);
const tl::optional<int> o33 = tl::nullopt;
auto o33r = o33.map([](int) { return; });
REQUIRE(!o33r);
const tl::optional<int> o36 = tl::nullopt;
auto o36r = std::move(o36).map([](int) { return 42; });
REQUIRE(!o36r);
const tl::optional<int> o37 = tl::nullopt;
auto o37r = std::move(o37).map([](int) { return; });
REQUIRE(!o37r);
// callable which returns a reference
tl::optional<int> o38 = 42;
auto o38r = o38.map([](int &i) -> const int & { return i; });
REQUIRE(o38r);
REQUIRE(*o38r == 42);
}
SECTION("map constexpr") {
#if !defined(_MSC_VER) && !defined(TL_OPTIONAL_GCC49)
constexpr tl::optional<int> o10 = 42;
constexpr auto o10r = o10.and_then(get_opt_int);
REQUIRE(*o10r == 42);
// test each overload in turn
constexpr tl::optional<int> o16 = 42;
constexpr auto o16r = o16.map(get_int);
STATIC_REQUIRE(*o16r == 42);
constexpr tl::optional<int> o11 = 42;
constexpr auto o11r = std::move(o11).and_then(get_opt_int);
REQUIRE(*o11r == 42);
constexpr tl::optional<int> o20 = 42;
constexpr auto o20r = std::move(o20).map(get_int);
STATIC_REQUIRE(*o20r == 42);
constexpr tl::optional<int> o18 = tl::nullopt;
constexpr auto o18r = o18.and_then(get_opt_int);
REQUIRE(!o18r);
constexpr tl::optional<int> o19 = tl::nullopt;
constexpr auto o19r = std::move(o19).and_then(get_opt_int);
REQUIRE(!o19r);
constexpr tl::optional<int> o32 = tl::nullopt;
constexpr auto o32r = o32.map(get_int);
STATIC_REQUIRE(!o32r);
constexpr tl::optional<int> o36 = tl::nullopt;
constexpr auto o36r = std::move(o36).map(get_int);
STATIC_REQUIRE(!o36r);
#endif
}
}
SECTION("or else") {
SECTION("and_then") {
// lhs is empty
tl::optional<int> o1;
auto o1r = o1.and_then([](int i) { return tl::optional<float>{42}; });
STATIC_REQUIRE((std::is_same<decltype(o1r), tl::optional<float>>::value));
REQUIRE(!o1r);
// lhs has value
tl::optional<int> o2 = 12;
auto o2r = o2.and_then([](int i) { return tl::optional<float>{42}; });
STATIC_REQUIRE((std::is_same<decltype(o2r), tl::optional<float>>::value));
REQUIRE(o2r.value() == 42.f);
// lhs is empty, rhs returns empty
tl::optional<int> o3;
auto o3r = o3.and_then([](int i) { return tl::optional<float>{}; });
STATIC_REQUIRE((std::is_same<decltype(o3r), tl::optional<float>>::value));
REQUIRE(!o3r);
// rhs returns empty
tl::optional<int> o4 = 12;
auto o4r = o4.and_then([](int i) { return tl::optional<float>{}; });
STATIC_REQUIRE((std::is_same<decltype(o4r), tl::optional<float>>::value));
REQUIRE(!o4r);
struct rval_call_and_then {
tl::optional<double> operator()(int) && {
return tl::optional<double>(42.0);
};
};
// ensure that function object is forwarded
tl::optional<int> o5 = 42;
auto o5r = o5.and_then(rval_call_and_then{});
STATIC_REQUIRE((std::is_same<decltype(o5r), tl::optional<double>>::value));
REQUIRE(o5r.value() == 42);
// ensure that lhs is forwarded
tl::optional<int> o6 = 42;
auto o6r =
std::move(o6).and_then([](int &&i) { return tl::optional<double>(i); });
STATIC_REQUIRE((std::is_same<decltype(o6r), tl::optional<double>>::value));
REQUIRE(o6r.value() == 42);
// ensure that function object is const-propagated
const tl::optional<int> o7 = 42;
auto o7r =
o7.and_then([](const int &i) { return tl::optional<double>(i); });
STATIC_REQUIRE((std::is_same<decltype(o7r), tl::optional<double>>::value));
REQUIRE(o7r.value() == 42);
// test each overload in turn
tl::optional<int> o8 = 42;
auto o8r = o8.and_then([](int i) { return tl::make_optional(42); });
REQUIRE(*o8r == 42);
tl::optional<int> o9 = 42;
auto o9r =
std::move(o9).and_then([](int i) { return tl::make_optional(42); });
REQUIRE(*o9r == 42);
const tl::optional<int> o10 = 42;
auto o10r = o10.and_then([](int i) { return tl::make_optional(42); });
REQUIRE(*o10r == 42);
const tl::optional<int> o11 = 42;
auto o11r =
std::move(o11).and_then([](int i) { return tl::make_optional(42); });
REQUIRE(*o11r == 42);
tl::optional<int> o16 = tl::nullopt;
auto o16r = o16.and_then([](int i) { return tl::make_optional(42); });
REQUIRE(!o16r);
tl::optional<int> o17 = tl::nullopt;
auto o17r =
std::move(o17).and_then([](int i) { return tl::make_optional(42); });
REQUIRE(!o17r);
const tl::optional<int> o18 = tl::nullopt;
auto o18r = o18.and_then([](int i) { return tl::make_optional(42); });
REQUIRE(!o18r);
const tl::optional<int> o19 = tl::nullopt;
auto o19r =
std::move(o19).and_then([](int i) { return tl::make_optional(42); });
REQUIRE(!o19r);
}
SECTION("constexpr and_then") {
#if !defined(_MSC_VER) && !defined(TL_OPTIONAL_GCC49)
constexpr tl::optional<int> o10 = 42;
constexpr auto o10r = o10.and_then(get_opt_int);
REQUIRE(*o10r == 42);
constexpr tl::optional<int> o11 = 42;
constexpr auto o11r = std::move(o11).and_then(get_opt_int);
REQUIRE(*o11r == 42);
constexpr tl::optional<int> o18 = tl::nullopt;
constexpr auto o18r = o18.and_then(get_opt_int);
REQUIRE(!o18r);
constexpr tl::optional<int> o19 = tl::nullopt;
constexpr auto o19r = std::move(o19).and_then(get_opt_int);
REQUIRE(!o19r);
#endif
}
SECTION("or else") {
tl::optional<int> o1 = 42;
REQUIRE(*(o1.or_else([] { return tl::make_optional(13); })) == 42);
tl::optional<int> o2;
REQUIRE(*(o2.or_else([] { return tl::make_optional(13); })) == 13);
}
}
SECTION("disjunction") {
SECTION("disjunction") {
tl::optional<int> o1 = 42;
tl::optional<int> o2 = 12;
tl::optional<int> o3;
@ -358,37 +277,37 @@ SECTION("disjunction") {
REQUIRE(*o2.disjunction(o3) == 12);
REQUIRE(*o3.disjunction(o1) == 42);
REQUIRE(*o3.disjunction(o2) == 12);
}
}
SECTION("conjunction") {
SECTION("conjunction") {
tl::optional<int> o1 = 42;
REQUIRE(*o1.conjunction(42.0) == 42.0);
REQUIRE(*o1.conjunction(std::string{"hello"}) == std::string{"hello"});
REQUIRE(!o1.conjunction(tl::nullopt));
tl::optional<int> o2;
REQUIRE(!o2.conjunction(42.0));
REQUIRE(!o2.conjunction(std::string{"hello"}));
REQUIRE(!o2.conjunction(tl::nullopt));
}
}
SECTION("map_or") {
SECTION("map_or") {
tl::optional<int> o1 = 21;
REQUIRE((o1.map_or([](int x) { return x * 2; }, 13)) == 42);
tl::optional<int> o2;
REQUIRE((o2.map_or([](int x) { return x * 2; }, 13)) == 13);
}
}
SECTION("map_or_else") {
SECTION("map_or_else") {
tl::optional<int> o1 = 21;
REQUIRE((o1.map_or_else([](int x) { return x * 2; }, []{return 13;})) == 42);
REQUIRE((o1.map_or_else([](int x) { return x * 2; }, [] { return 13; })) ==
42);
tl::optional<int> o2;
REQUIRE((o2.map_or_else([](int x) { return x * 2; }, []{return 13;})) == 13);
}
REQUIRE((o2.map_or_else([](int x) { return x * 2; }, [] { return 13; })) ==
13);
}
SECTION("take") {
SECTION("take") {
tl::optional<int> o1 = 42;
REQUIRE(*o1.take() == 42);
REQUIRE(!o1);
@ -396,6 +315,28 @@ SECTION("take") {
tl::optional<int> o2;
REQUIRE(!o2.take());
REQUIRE(!o2);
}
}
;
}
struct foo {
void non_const() {}
};
#if defined(TL_OPTIONAL_CXX14) && !defined(TL_OPTIONAL_GCC49) && \
!defined(TL_OPTIONAL_GCC54)
SECTION("Issue #1") {
tl::optional<foo> f = foo{};
auto l = [](auto &&x) { x.non_const(); };
f.map(l);
}
#endif
struct overloaded {
tl::optional<int> operator()(foo &) { return 0; }
tl::optional<std::string> operator()(const foo &) { return ""; }
};
SECTION("Issue #2") {
tl::optional<foo> f = foo{};
auto x = f.and_then(overloaded{});
}
};

View File

@ -37,4 +37,10 @@ TEST_CASE("Make optional", "[make_optional]") {
REQUIRE(o5->v[1] == 1);
REQUIRE(std::get<0>(o5->t) == 2);
REQUIRE(std::get<1>(o5->t) == 3);
auto i = 42;
auto o6 = tl::make_optional<int&>(i);
REQUIRE((std::is_same<decltype(o6), tl::optional<int&>>::value));
REQUIRE(o6);
REQUIRE(*o6 == 42);
}

View File

@ -63,8 +63,8 @@ TEST_CASE("Noexcept", "[noexcept]") {
using nothrow_opt = tl::optional<nothrow_move>;
using throw_opt = tl::optional<throw_move>;
REQUIRE(noexcept(nothrow_opt{std::declval<nothrow_opt>()}));
REQUIRE(!noexcept(throw_opt{std::declval<throw_opt>()}));
REQUIRE(std::is_nothrow_move_constructible<nothrow_opt>::value);
REQUIRE(!std::is_nothrow_move_constructible<throw_opt>::value);
#endif
}

2308
tl/optional.hpp Normal file

File diff suppressed because it is too large Load Diff