diff --git a/CMakeLists.txt b/CMakeLists.txt index e2ab259..452671f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,8 +4,6 @@ project(stl-concepts VERSION 0.0.0 LANGUAGES CXX) enable_testing() -set(CMAKE_CXX_STANDARD 20) - set(TARGETS_EXPORT_NAME ${CMAKE_PROJECT_NAME}Targets) add_subdirectory(extern) diff --git a/etc/clang-17-toolchain.cmake b/etc/clang-17-toolchain.cmake index 40807e4..71046ff 100755 --- a/etc/clang-17-toolchain.cmake +++ b/etc/clang-17-toolchain.cmake @@ -3,9 +3,10 @@ include_guard(GLOBAL) set(CMAKE_C_COMPILER clang-17) set(CMAKE_CXX_COMPILER clang++-17) +set(CMAKE_CXX_STANDARD 20) + set(CMAKE_CXX_FLAGS - "-std=gnu++23 \ - -Wall -Wextra \ + "-Wall -Wextra \ -stdlib=libstdc++ " CACHE STRING "CXX_FLAGS" FORCE) @@ -13,4 +14,4 @@ set(CMAKE_CXX_FLAGS_DEBUG "-O0 -fno-inline -g3" CACHE STRING "C++ DEBUG Flags" F set(CMAKE_CXX_FLAGS_RELEASE "-Ofast -g0 -DNDEBUG" CACHE STRING "C++ Release Flags" FORCE) set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O3 -g -DNDEBUG" CACHE STRING "C++ RelWithDebInfo Flags" FORCE) set(CMAKE_CXX_FLAGS_TSAN "-O3 -g -DNDEBUG -fsanitize=thread" CACHE STRING "C++ TSAN Flags" FORCE) -set(CMAKE_CXX_FLAGS_ASAN "-O3 -g -DNDEBUG -fsanitize=address -fsanitize=undefined -fsanitize=leak" CACHE STRING "C++ ASAN Flags" FORCE) +set(CMAKE_CXX_FLAGS_ASAN "-O3 -g -DNDEBUG -fsanitize=address,undefined,leak" CACHE STRING "C++ ASAN Flags" FORCE) diff --git a/etc/gcc-14-toolchain.cmake b/etc/gcc-14-toolchain.cmake new file mode 100755 index 0000000..27e2ab1 --- /dev/null +++ b/etc/gcc-14-toolchain.cmake @@ -0,0 +1,16 @@ +include_guard(GLOBAL) + +set(CMAKE_C_COMPILER gcc-14) +set(CMAKE_CXX_COMPILER g++-14) + +set(CMAKE_CXX_STANDARD 23) + +set(CMAKE_CXX_FLAGS + "-Wall -Wextra " +CACHE STRING "CXX_FLAGS" FORCE) + +set(CMAKE_CXX_FLAGS_DEBUG "-O0 -fno-inline -g3" CACHE STRING "C++ DEBUG Flags" FORCE) +set(CMAKE_CXX_FLAGS_RELEASE "-Ofast -g0 -DNDEBUG" CACHE STRING "C++ Release Flags" FORCE) +set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O3 -g -DNDEBUG" CACHE STRING "C++ RelWithDebInfo Flags" FORCE) +set(CMAKE_CXX_FLAGS_TSAN "-O3 -g -DNDEBUG -fsanitize=thread" CACHE STRING "C++ TSAN Flags" FORCE) +set(CMAKE_CXX_FLAGS_ASAN "-O3 -g -DNDEBUG -fsanitize=undefined" CACHE STRING "C++ ASAN Flags" FORCE) diff --git a/etc/gcc-toolchain.cmake b/etc/gcc-toolchain.cmake index 64de0cb..ab7ec45 100755 --- a/etc/gcc-toolchain.cmake +++ b/etc/gcc-toolchain.cmake @@ -12,4 +12,4 @@ set(CMAKE_CXX_FLAGS_DEBUG "-O0 -fno-inline -g3" CACHE STRING "C++ DEBUG Flags" F set(CMAKE_CXX_FLAGS_RELEASE "-Ofast -g0 -DNDEBUG" CACHE STRING "C++ Release Flags" FORCE) set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O3 -g -DNDEBUG" CACHE STRING "C++ RelWithDebInfo Flags" FORCE) set(CMAKE_CXX_FLAGS_TSAN "-O3 -g -DNDEBUG -fsanitize=thread" CACHE STRING "C++ TSAN Flags" FORCE) -set(CMAKE_CXX_FLAGS_ASAN "-O3 -g -DNDEBUG -fsanitize=address -fsanitize=undefined -fsanitize=leak" CACHE STRING "C++ ASAN Flags" FORCE) +set(CMAKE_CXX_FLAGS_ASAN "-O3 -g -DNDEBUG -fsanitize=address,undefined,leak" CACHE STRING "C++ ASAN Flags" FORCE) diff --git a/src/smd/optional/CMakeLists.txt b/src/smd/optional/CMakeLists.txt index 7a5c8e4..70649f0 100644 --- a/src/smd/optional/CMakeLists.txt +++ b/src/smd/optional/CMakeLists.txt @@ -37,6 +37,8 @@ target_sources( PRIVATE optional.t.cpp optional_ref.t.cpp + optional_monadic.t.cpp + optional_ref_monadic.t.cpp ) target_link_libraries(optional_test optional) diff --git a/src/smd/optional/optional.h b/src/smd/optional/optional.h index b735c0d..44fe5b8 100644 --- a/src/smd/optional/optional.h +++ b/src/smd/optional/optional.h @@ -472,75 +472,89 @@ class optional { } template - constexpr auto and_then(F&& f) & - requires detail::is_optional>>::value - { - using result = std::invoke_result_t; - return has_value() ? std::invoke(std::forward(f), value()) : result(nullopt); + constexpr auto and_then(F&& f) & { + using U = std::invoke_result_t; + static_assert(detail::is_optional>::value); + if (has_value()) { + return std::invoke(std::forward(f), value_); + } else { + return std::remove_cvref_t(); + } } template - constexpr auto and_then(F&& f) && - requires detail::is_optional>>::value - { - using result = std::invoke_result_t; - return has_value() ? std::invoke(std::forward(f), value()) : result(nullopt); + constexpr auto and_then(F&& f) && { + using U = std::invoke_result_t; + static_assert(detail::is_optional>::value); + if (has_value()) { + return std::invoke(std::forward(f), std::move(value_)); + } else { + return std::remove_cvref_t(); + } } template - constexpr auto and_then(F&& f) const& - requires detail::is_optional>>::value - { - using result = std::invoke_result_t; - return has_value() ? std::invoke(std::forward(f), value()) : result(nullopt); + constexpr auto and_then(F&& f) const& { + using U = std::invoke_result_t; + static_assert(detail::is_optional>::value); + if (has_value()) { + return std::invoke(std::forward(f), value_); + } else { + return std::remove_cvref_t(); + } } template - constexpr auto and_then(F&& f) const&& - requires detail::is_optional>>::value - { - using result = std::invoke_result_t; - return has_value() ? std::invoke(std::forward(f), value()) : result(nullopt); + constexpr auto and_then(F&& f) const&& { + using U = std::invoke_result_t; + static_assert(detail::is_optional>::value); + if (has_value()) { + return std::invoke(std::forward(f), std::move(value_)); + } else { + return std::remove_cvref_t(); + } } /// Carries out some operation on the stored object if there is one. template constexpr auto transform(F&& f) & { - return optional_map_impl(*this, std::forward(f)); + using U = std::invoke_result_t; + return (has_value()) ? optional{std::invoke(std::forward(f), value_)} : optional{}; } template constexpr auto transform(F&& f) && { - return optional_map_impl(std::move(*this), std::forward(f)); + using U = std::invoke_result_t; + return (has_value()) ? optional{std::invoke(std::forward(f), std::move(value_))} : optional{}; } template constexpr auto transform(F&& f) const& { - return optional_map_impl(*this, std::forward(f)); + using U = std::invoke_result_t; + return (has_value()) ? optional{std::invoke(std::forward(f), value_)} : optional{}; } template constexpr auto transform(F&& f) const&& { - return optional_map_impl(std::move(*this), std::forward(f)); + using U = std::invoke_result_t; + return (has_value()) ? optional{std::invoke(std::forward(f), value_)} : optional{}; } /// Calls `f` if the optional is empty template constexpr optional or_else(F&& f) & { if (has_value()) - return *this; + return value_; - std::forward(f)(); - return nullopt; + return std::forward(f)(); } template optional or_else(F&& f) && { if (has_value()) - return std::move(*this); + return std::move(value_); - std::forward(f)(); - return nullopt; + return std::forward(f)(); } /// Assigns the stored value from `u`, destroying the old value if there @@ -959,7 +973,7 @@ class optional { T* value_; // exposition only public: - // \rSec3[optional.ctor]{Constructors} + // \rSec3[optional.ctor]{Constructors} constexpr optional() noexcept : value_(nullptr) {} @@ -978,11 +992,11 @@ class optional { template constexpr explicit optional(const optional& rhs) noexcept : optional(*rhs) {} - // \rSec3[optional.dtor]{Destructor} + // \rSec3[optional.dtor]{Destructor} ~optional() = default; - // \rSec3[optional.assign]{Assignment} + // \rSec3[optional.assign]{Assignment} optional& operator=(nullopt_t) noexcept { value_ = nullptr; @@ -990,7 +1004,7 @@ class optional { } optional& operator=(const optional& rhs) noexcept = default; - optional& operator=(optional&& rhs) noexcept = default; + optional& operator=(optional&& rhs) noexcept = default; template requires(!detail::is_optional>::value) @@ -1021,31 +1035,12 @@ class optional { // \rSec3[optional.observe]{Observers} constexpr T* operator->() const noexcept { return value_; } - constexpr T& operator*() const& noexcept { return *value_; } - constexpr T&& operator*() const&& noexcept { return *value_; } + constexpr T& operator*() const noexcept { return *value_; } constexpr explicit operator bool() const noexcept { return value_ != nullptr; } constexpr bool has_value() const noexcept { return value_ != nullptr; } - constexpr T& value() const& { - if (has_value()) - return *value_; - throw bad_optional_access(); - } - - constexpr T& value() & { - if (has_value()) - return *value_; - throw bad_optional_access(); - } - - constexpr T&& value() && { - if (has_value()) - return *value_; - throw bad_optional_access(); - } - - constexpr const T&& value() const&& { + constexpr T& value() const { if (has_value()) return *value_; throw bad_optional_access(); @@ -1055,86 +1050,36 @@ class optional { constexpr T value_or(U&& u) const& { static_assert(std::is_copy_constructible_v && std::is_convertible_v, "T must be copy constructible and convertible from U"); - return has_value() ? value() : static_cast(std::forward(u)); + return has_value() ? *value_ : static_cast(std::forward(u)); } template constexpr T value_or(U&& u) && { static_assert(std::is_move_constructible_v && std::is_convertible_v, "T must be move constructible and convertible from U"); - return has_value() ? value() : static_cast(std::forward(u)); + return has_value() ? *value_ : static_cast(std::forward(u)); } // \rSec3[optional.monadic]{Monadic operations} template - constexpr auto and_then(F&& f) & { - using result = std::invoke_result_t; - static_assert(detail::is_optional::value, "F must return an optional"); - - return has_value() ? std::invoke(std::forward(f), value()) : result(nullopt); - } - - template - constexpr auto and_then(F&& f) && { - using result = std::invoke_result_t; - static_assert(detail::is_optional::value, "F must return an optional"); - - return has_value() ? std::invoke(std::forward(f), value()) : result(nullopt); - } - - template - constexpr auto and_then(F&& f) const& { - using result = std::invoke_result_t; - static_assert(detail::is_optional::value, "F must return an optional"); - - return has_value() ? std::invoke(std::forward(f), value()) : result(nullopt); - } - - template - constexpr auto and_then(F&& f) const&& { - using result = std::invoke_result_t; - static_assert(detail::is_optional::value, "F must return an optional"); - - return has_value() ? std::invoke(std::forward(f), value()) : result(nullopt); + constexpr auto and_then(F&& f) const { + using U = std::invoke_result_t; + static_assert(detail::is_optional::value, "F must return an optional"); + return (has_value()) ? std::invoke(std::forward(f), *value_) : std::remove_cvref_t(); } template - constexpr auto transform(F&& f) & { - return detail::optional_map_impl(*this, std::forward(f)); + constexpr auto transform(F&& f) const -> optional> { + using U = std::invoke_result_t; + return (has_value()) ? optional{std::invoke(std::forward(f), *value_)} : optional{}; } template - constexpr auto transform(F&& f) && { - return detail::optional_map_impl(std::move(*this), std::forward(f)); - } - - template - constexpr auto transform(F&& f) const& { - return detail::optional_map_impl(*this, std::forward(f)); - } - - template - constexpr auto transform(F&& f) const&& { - return detail::optional_map_impl(std::move(*this), std::forward(f)); - } - - template - constexpr optional or_else(F&& f) && { - if (*this) { - return std::move(*this); - } else { - return std::forward(f)(); - } - } - - template - constexpr optional or_else(F&& f) const& { - if (*this) { - return *this; - } else { - return std::forward(f)(); - } + constexpr optional or_else(F&& f) const { + using U = std::invoke_result_t; + static_assert(std::is_same_v, optional>); + return has_value() ? *value_ : std::forward(f)(); } void reset() noexcept { value_ = nullptr; } diff --git a/src/smd/optional/optional_monadic.t.cpp b/src/smd/optional/optional_monadic.t.cpp new file mode 100644 index 0000000..939b548 --- /dev/null +++ b/src/smd/optional/optional_monadic.t.cpp @@ -0,0 +1,315 @@ +#include + +#include + +#include + +constexpr int get_int(int) { return 42; } +constexpr smd::optional::optional get_opt_int(int) { return 42; } + + +TEST(OptionalMonadicTest, Transform) { + // lhs is empty + smd::optional::optional o1; + auto o1r = o1.transform([](int i) { return i + 2; }); + static_assert((std::is_same>::value)); + EXPECT_TRUE(!o1r); + + // lhs has value + smd::optional::optional o2 = 40; + auto o2r = o2.transform([](int i) { return i + 2; }); + static_assert((std::is_same>::value)); + EXPECT_TRUE(o2r.value() == 42); + + struct rval_call_transform { + double operator()(int) && { return 42.0; }; + }; + + // ensure that function object is forwarded + smd::optional::optional o3 = 42; + auto o3r = o3.transform(rval_call_transform{}); + static_assert((std::is_same>::value)); + EXPECT_TRUE(o3r.value() == 42); + + // ensure that lhs is forwarded + smd::optional::optional o4 = 40; + auto o4r = std::move(o4).transform([](int &&i) { return i + 2; }); + static_assert((std::is_same>::value)); + EXPECT_TRUE(o4r.value() == 42); + + // ensure that lhs is const-propagated + const smd::optional::optional o5 = 40; + auto o5r = o5.transform([](const int &i) { return i + 2; }); + static_assert((std::is_same>::value)); + EXPECT_TRUE(o5r.value() == 42); + + + // test each overload in turn + smd::optional::optional o8 = 42; + auto o8r = o8.transform([](int) { return 42; }); + EXPECT_TRUE(*o8r == 42); + + smd::optional::optional o12 = 42; + auto o12r = std::move(o12).transform([](int) { return 42; }); + EXPECT_TRUE(*o12r == 42); + + const smd::optional::optional o16 = 42; + auto o16r = o16.transform([](int) { return 42; }); + EXPECT_TRUE(*o16r == 42); + + const smd::optional::optional o20 = 42; + auto o20r = std::move(o20).transform([](int) { return 42; }); + EXPECT_TRUE(*o20r == 42); + + smd::optional::optional o24 = smd::optional::nullopt; + auto o24r = o24.transform([](int) { return 42; }); + EXPECT_TRUE(!o24r); + + smd::optional::optional o28 = smd::optional::nullopt; + auto o28r = std::move(o28).transform([](int) { return 42; }); + EXPECT_TRUE(!o28r); + + const smd::optional::optional o32 = smd::optional::nullopt; + auto o32r = o32.transform([](int) { return 42; }); + EXPECT_TRUE(!o32r); + + const smd::optional::optional o36 = smd::optional::nullopt; + auto o36r = std::move(o36).transform([](int) { return 42; }); + EXPECT_TRUE(!o36r); + + // callable which returns a reference + smd::optional::optional o38 = 42; + auto o38r = o38.transform([](int &i) -> const int & { return i; }); + EXPECT_TRUE(o38r); + EXPECT_TRUE(*o38r == 42); + + } + +TEST(OptionalMonadicTest, TransformConstexpr) { + + // test each overload in turn + constexpr smd::optional::optional o16 = 42; + constexpr auto o16r = o16.transform(get_int); + static_assert(*o16r == 42); + + constexpr smd::optional::optional o20 = 42; + constexpr auto o20r = std::move(o20).transform(get_int); + static_assert(*o20r == 42); + + constexpr smd::optional::optional o32 = smd::optional::nullopt; + constexpr auto o32r = o32.transform(get_int); + static_assert(!o32r); + constexpr smd::optional::optional o36 = smd::optional::nullopt; + constexpr auto o36r = std::move(o36).transform(get_int); + static_assert(!o36r); + } + +TEST(OptionalMonadicTest, Transform2) { + // lhs is empty + smd::optional::optional o1; + auto o1r = o1.transform([](int i) { return i + 2; }); + static_assert((std::is_same>::value)); + EXPECT_TRUE(!o1r); + + // lhs has value + smd::optional::optional o2 = 40; + auto o2r = o2.transform([](int i) { return i + 2; }); + static_assert((std::is_same>::value)); + EXPECT_TRUE(o2r.value() == 42); + + struct rval_call_transform { + double operator()(int) && { return 42.0; }; + }; + + // ensure that function object is forwarded + smd::optional::optional o3 = 42; + auto o3r = o3.transform(rval_call_transform{}); + static_assert((std::is_same>::value)); + EXPECT_TRUE(o3r.value() == 42); + + // ensure that lhs is forwarded + smd::optional::optional o4 = 40; + auto o4r = std::move(o4).transform([](int&& i) { return i + 2; }); + static_assert((std::is_same>::value)); + EXPECT_TRUE(o4r.value() == 42); + + // ensure that lhs is const-propagated + const smd::optional::optional o5 = 40; + auto o5r = o5.transform([](const int& i) { return i + 2; }); + static_assert((std::is_same>::value)); + EXPECT_TRUE(o5r.value() == 42); + + // test each overload in turn + smd::optional::optional o8 = 42; + auto o8r = o8.transform([](int) { return 42; }); + EXPECT_TRUE(*o8r == 42); + + smd::optional::optional o12 = 42; + auto o12r = std::move(o12).transform([](int) { return 42; }); + EXPECT_TRUE(*o12r == 42); + + const smd::optional::optional o16 = 42; + auto o16r = o16.transform([](int) { return 42; }); + EXPECT_TRUE(*o16r == 42); + + const smd::optional::optional o20 = 42; + auto o20r = std::move(o20).transform([](int) { return 42; }); + EXPECT_TRUE(*o20r == 42); + + smd::optional::optional o24 = smd::optional::nullopt; + auto o24r = o24.transform([](int) { return 42; }); + EXPECT_TRUE(!o24r); + + smd::optional::optional o28 = smd::optional::nullopt; + auto o28r = std::move(o28).transform([](int) { return 42; }); + EXPECT_TRUE(!o28r); + + const smd::optional::optional o32 = smd::optional::nullopt; + auto o32r = o32.transform([](int) { return 42; }); + EXPECT_TRUE(!o32r); + + const smd::optional::optional o36 = smd::optional::nullopt; + auto o36r = std::move(o36).transform([](int) { return 42; }); + EXPECT_TRUE(!o36r); + } + +TEST(OptionalMonadicTest, TransformConstxpr) + { + // test each overload in turn + constexpr smd::optional::optional o16 = 42; + constexpr auto o16r = o16.transform(get_int); + static_assert(*o16r == 42); + + constexpr smd::optional::optional o20 = 42; + constexpr auto o20r = std::move(o20).transform(get_int); + static_assert(*o20r == 42); + + constexpr smd::optional::optional o32 = smd::optional::nullopt; + constexpr auto o32r = o32.transform(get_int); + static_assert(!o32r); + constexpr smd::optional::optional o36 = smd::optional::nullopt; + constexpr auto o36r = std::move(o36).transform(get_int); + static_assert(!o36r); + } + +TEST(OptionalMonadicTest, and_then) + { + // lhs is empty + smd::optional::optional o1; + auto o1r = o1.and_then([](int) { return smd::optional::optional{42}; }); + static_assert((std::is_same>::value)); + EXPECT_TRUE(!o1r); + + // lhs has value + smd::optional::optional o2 = 12; + auto o2r = o2.and_then([](int) { return smd::optional::optional{42}; }); + static_assert((std::is_same>::value)); + EXPECT_TRUE(o2r.value() == 42.f); + + // lhs is empty, rhs returns empty + smd::optional::optional o3; + auto o3r = o3.and_then([](int) { return smd::optional::optional{}; }); + static_assert((std::is_same>::value)); + EXPECT_TRUE(!o3r); + + // rhs returns empty + smd::optional::optional o4 = 12; + auto o4r = o4.and_then([](int) { return smd::optional::optional{}; }); + static_assert((std::is_same>::value)); + EXPECT_TRUE(!o4r); + + struct rval_call_and_then { + smd::optional::optional operator()(int) && { + return smd::optional::optional(42.0); + }; + }; + + // ensure that function object is forwarded + smd::optional::optional o5 = 42; + auto o5r = o5.and_then(rval_call_and_then{}); + static_assert((std::is_same>::value)); + EXPECT_TRUE(o5r.value() == 42); + + // ensure that lhs is forwarded + smd::optional::optional o6 = 42; + auto o6r = + std::move(o6).and_then([](int &&i) { return smd::optional::optional(i); }); + static_assert((std::is_same>::value)); + EXPECT_TRUE(o6r.value() == 42); + + // ensure that function object is const-propagated + const smd::optional::optional o7 = 42; + auto o7r = + o7.and_then([](const int &i) { return smd::optional::optional(i); }); + static_assert((std::is_same>::value)); + EXPECT_TRUE(o7r.value() == 42); + + // test each overload in turn + smd::optional::optional o8 = 42; + auto o8r = o8.and_then([](int) { return smd::optional::make_optional(42); }); + EXPECT_TRUE(*o8r == 42); + + smd::optional::optional o9 = 42; + auto o9r = + std::move(o9).and_then([](int) { return smd::optional::make_optional(42); }); + EXPECT_TRUE(*o9r == 42); + + const smd::optional::optional o10 = 42; + auto o10r = o10.and_then([](int) { return smd::optional::make_optional(42); }); + EXPECT_TRUE(*o10r == 42); + + const smd::optional::optional o11 = 42; + auto o11r = + std::move(o11).and_then([](int) { return smd::optional::make_optional(42); }); + EXPECT_TRUE(*o11r == 42); + + smd::optional::optional o16 = smd::optional::nullopt; + auto o16r = o16.and_then([](int) { return smd::optional::make_optional(42); }); + EXPECT_TRUE(!o16r); + + smd::optional::optional o17 = smd::optional::nullopt; + auto o17r = + std::move(o17).and_then([](int) { return smd::optional::make_optional(42); }); + EXPECT_TRUE(!o17r); + + const smd::optional::optional o18 = smd::optional::nullopt; + auto o18r = o18.and_then([](int) { return smd::optional::make_optional(42); }); + EXPECT_TRUE(!o18r); + + const smd::optional::optional o19 = smd::optional::nullopt; + auto o19r = std::move(o19).and_then([](int) { return smd::optional::make_optional(42); }); + EXPECT_TRUE(!o19r); + + int i = 3; + smd::optional::optional o20{i}; + std::move(o20).and_then([](int& r){return smd::optional::optional{++r};}); + EXPECT_TRUE(o20); + EXPECT_TRUE(i == 4); + } + +TEST(OptionalMonadicTest, Constexpr_and_then) +{ + constexpr smd::optional::optional o10 = 42; + constexpr auto o10r = o10.and_then(get_opt_int); + EXPECT_TRUE(*o10r == 42); + + constexpr smd::optional::optional o11 = 42; + constexpr auto o11r = std::move(o11).and_then(get_opt_int); + EXPECT_TRUE(*o11r == 42); + + constexpr smd::optional::optional o18 = smd::optional::nullopt; + constexpr auto o18r = o18.and_then(get_opt_int); + EXPECT_TRUE(!o18r); + + constexpr smd::optional::optional o19 = smd::optional::nullopt; + constexpr auto o19r = std::move(o19).and_then(get_opt_int); + EXPECT_TRUE(!o19r); + } + +TEST(OptionalMonadicTest, or_else){ + smd::optional::optional o1 = 42; + EXPECT_TRUE(*(o1.or_else([] { return smd::optional::make_optional(13); })) == 42); + + smd::optional::optional o2; + EXPECT_EQ(*(o2.or_else([] { return smd::optional::make_optional(13); })), 13); + } diff --git a/src/smd/optional/optional_ref.t.cpp b/src/smd/optional/optional_ref.t.cpp index f1087de..6ef214d 100644 --- a/src/smd/optional/optional_ref.t.cpp +++ b/src/smd/optional/optional_ref.t.cpp @@ -436,12 +436,70 @@ TEST(OptionalRefTest, Observers) { auto success = std::is_same::value; static_assert(std::is_same::value); EXPECT_TRUE(success); + success = std::is_same::value; + static_assert(std::is_same::value); + EXPECT_TRUE(success); success = std::is_same::value; static_assert(std::is_same::value); EXPECT_TRUE(success); - success = std::is_same::value; - static_assert(std::is_same::value); + success = std::is_same::value; + static_assert(std::is_same::value); + EXPECT_TRUE(success); + + success = std::is_same::value; + static_assert(std::is_same::value); + EXPECT_TRUE(success); + success = std::is_same::value; + static_assert(std::is_same::value); + EXPECT_TRUE(success); + success = std::is_same::value; + static_assert(std::is_same::value); + EXPECT_TRUE(success); + success = std::is_same::value; + static_assert(std::is_same::value); + EXPECT_TRUE(success); + + success = std::is_same()), int*>::value; + static_assert(std::is_same()), int*>::value); + EXPECT_TRUE(success); + success = std::is_same()), int*>::value; + static_assert(std::is_same()), int*>::value); + EXPECT_TRUE(success); + success = std::is_same()), int*>::value; + static_assert(std::is_same()), int*>::value); + EXPECT_TRUE(success); + success = std::is_same()), int*>::value; + static_assert(std::is_same()), int*>::value); EXPECT_TRUE(success); + + struct int_box {int i_;}; + int_box i1{3}; + smd::optional::optional ob1 = i1; + smd::optional::optional ob2; + const smd::optional::optional ob3 = i1; + success = std::is_samei_), int>::value; + static_assert(std::is_samei_), int>::value); + EXPECT_TRUE(success); + success = std::is_samei_), int>::value; + static_assert(std::is_samei_), int>::value); + EXPECT_TRUE(success); + success = std::is_samei_), int>::value; + static_assert(std::is_samei_), int>::value); + EXPECT_TRUE(success); + success = std::is_samei_), int>::value; + static_assert(std::is_samei_), int>::value); + EXPECT_TRUE(success); + +} + +TEST(OptionalRefTest, MoveCheck) { + int x = 0; + int& y = std::move(smd::optional::optional(x)).value(); + EXPECT_EQ(&y, &x); + + int& z = *std::move(smd::optional::optional(x)); + EXPECT_EQ(&z, &x); + } TEST(OptionalRefTest, SwapValue) { diff --git a/src/smd/optional/optional_ref_monadic.t.cpp b/src/smd/optional/optional_ref_monadic.t.cpp new file mode 100644 index 0000000..68acba4 --- /dev/null +++ b/src/smd/optional/optional_ref_monadic.t.cpp @@ -0,0 +1,320 @@ +#include + +#include + +#include + +namespace { +inline constexpr int constexpr_fortytwo = 42; +constexpr int get_int(int) { return constexpr_fortytwo; } +constexpr smd::optional::optional get_opt_int(int) { return constexpr_fortytwo; } +} // namespace + +TEST(OptionalRefMonadicTest, Transform) { + // lhs is empty + smd::optional::optional o1; + auto o1r = o1.transform([](int i) { return i + 2; }); + static_assert((std::is_same>::value)); + EXPECT_TRUE(!o1r); + + // lhs has value + int forty = 40; + smd::optional::optional o2 = forty; + auto o2r = o2.transform([](int i) { return i + 2; }); + static_assert((std::is_same>::value)); + EXPECT_TRUE(o2r.value() == 42); + + struct rval_call_transform { + double operator()(int) && { return 42.0; }; + }; + + // ensure that function object is forwarded + int fortytwo = 42; + smd::optional::optional o3 = fortytwo; + auto o3r = o3.transform(rval_call_transform{}); + static_assert((std::is_same>::value)); + EXPECT_TRUE(o3r.value() == 42); + + // // ensure that lhs is forwarded + // forty = 40; + // smd::optional::optional o4 = forty; + // auto o4r = std::move(o4).transform([](int &&i) { return i + 2; }); + // static_assert((std::is_same>::value)); + // EXPECT_TRUE(o4r.value() == 42); + + // ensure that lhs is const-propagated + forty = 40; + const smd::optional::optional o5 = forty; + auto o5r = o5.transform([](const int& i) { return i + 2; }); + static_assert((std::is_same>::value)); + EXPECT_TRUE(o5r.value() == 42); + + // test each overload in turn + fortytwo = 42; + smd::optional::optional o8 = fortytwo; + auto o8r = o8.transform([](int) { return 42; }); + EXPECT_TRUE(*o8r == 42); + + smd::optional::optional o12 = fortytwo; + auto o12r = std::move(o12).transform([](int) { return 42; }); + EXPECT_TRUE(*o12r == 42); + + const smd::optional::optional o16 = fortytwo; + auto o16r = o16.transform([](int) { return 42; }); + EXPECT_TRUE(*o16r == 42); + + const smd::optional::optional o20 = fortytwo; + auto o20r = std::move(o20).transform([](int) { return 42; }); + EXPECT_TRUE(*o20r == 42); + + smd::optional::optional o24 = smd::optional::nullopt; + auto o24r = o24.transform([](int) { return 42; }); + EXPECT_TRUE(!o24r); + + smd::optional::optional o28 = smd::optional::nullopt; + auto o28r = std::move(o28).transform([](int) { return 42; }); + EXPECT_TRUE(!o28r); + + const smd::optional::optional o32 = smd::optional::nullopt; + auto o32r = o32.transform([](int) { return 42; }); + EXPECT_TRUE(!o32r); + + const smd::optional::optional o36 = smd::optional::nullopt; + auto o36r = std::move(o36).transform([](int) { return 42; }); + EXPECT_TRUE(!o36r); + + // callable which returns a reference + smd::optional::optional o38 = fortytwo; + auto o38r = o38.transform([](int& i) -> const int& { return i; }); + EXPECT_TRUE(o38r); + EXPECT_TRUE(*o38r == 42); +} + +TEST(OptionalRefMonadicTest, TransformConstexpr) { + + // test each overload in turn + constexpr smd::optional::optional o16 = constexpr_fortytwo; + constexpr auto o16r = o16.transform(get_int); + static_assert(*o16r == 42); + + constexpr smd::optional::optional o20 = constexpr_fortytwo; + constexpr auto o20r = std::move(o20).transform(get_int); + static_assert(*o20r == 42); + + constexpr smd::optional::optional o32 = smd::optional::nullopt; + constexpr auto o32r = o32.transform(get_int); + static_assert(!o32r); + constexpr smd::optional::optional o36 = smd::optional::nullopt; + constexpr auto o36r = std::move(o36).transform(get_int); + static_assert(!o36r); +} + +TEST(OptionalRefMonadicTest, Transform2) { + // lhs is empty + smd::optional::optional o1; + auto o1r = o1.transform([](int i) { return i + 2; }); + static_assert((std::is_same>::value)); + EXPECT_TRUE(!o1r); + + // lhs has value + int forty = 40; + smd::optional::optional o2 = forty; + auto o2r = o2.transform([](int i) { return i + 2; }); + static_assert((std::is_same>::value)); + EXPECT_TRUE(o2r.value() == 42); + + struct rval_call_transform { + double operator()(int) && { return 42.0; }; + }; + + // ensure that function object is forwarded + int fortytwo = 42; + smd::optional::optional o3 = fortytwo; + auto o3r = o3.transform(rval_call_transform{}); + static_assert((std::is_same>::value)); + EXPECT_TRUE(o3r.value() == 42); + + // // ensure that lhs is forwarded + // int forty = 40; + // smd::optional::optional o4 = forty; + // auto o4r = std::move(o4).transform([](int&& i) { return i + 2; }); + // static_assert((std::is_same>::value)); + // EXPECT_TRUE(o4r.value() == 42); + + // ensure that lhs is const-propagated + forty = 40; + const smd::optional::optional o5 = forty; + auto o5r = o5.transform([](const int& i) { return i + 2; }); + static_assert((std::is_same>::value)); + EXPECT_TRUE(o5r.value() == 42); + + // test each overload in turn + fortytwo = 42; + smd::optional::optional o8 = fortytwo; + auto o8r = o8.transform([](int) { return 42; }); + EXPECT_TRUE(*o8r == 42); + + smd::optional::optional o12 = fortytwo; + auto o12r = std::move(o12).transform([](int) { return 42; }); + EXPECT_TRUE(*o12r == 42); + + const smd::optional::optional o16 = fortytwo; + auto o16r = o16.transform([](int) { return 42; }); + EXPECT_TRUE(*o16r == 42); + + const smd::optional::optional o20 = fortytwo; + auto o20r = std::move(o20).transform([](int) { return 42; }); + EXPECT_TRUE(*o20r == 42); + + smd::optional::optional o24 = smd::optional::nullopt; + auto o24r = o24.transform([](int) { return 42; }); + EXPECT_TRUE(!o24r); + + smd::optional::optional o28 = smd::optional::nullopt; + auto o28r = std::move(o28).transform([](int) { return 42; }); + EXPECT_TRUE(!o28r); + + const smd::optional::optional o32 = smd::optional::nullopt; + auto o32r = o32.transform([](int) { return 42; }); + EXPECT_TRUE(!o32r); + + const smd::optional::optional o36 = smd::optional::nullopt; + auto o36r = std::move(o36).transform([](int) { return 42; }); + EXPECT_TRUE(!o36r); +} + +TEST(OptionalRefMonadicTest, TransformConstxpr) { + // test each overload in turn + constexpr smd::optional::optional o16 = constexpr_fortytwo; + constexpr auto o16r = o16.transform(get_int); + static_assert(*o16r == 42); + + constexpr smd::optional::optional o20 = constexpr_fortytwo; + constexpr auto o20r = std::move(o20).transform(get_int); + static_assert(*o20r == 42); + + constexpr smd::optional::optional o32 = smd::optional::nullopt; + constexpr auto o32r = o32.transform(get_int); + static_assert(!o32r); + constexpr smd::optional::optional o36 = smd::optional::nullopt; + constexpr auto o36r = std::move(o36).transform(get_int); + static_assert(!o36r); +} + +TEST(OptionalRefMonadicTest, and_then) { + // lhs is empty + smd::optional::optional o1; + auto o1r = o1.and_then([](int) { return smd::optional::optional{42}; }); + static_assert((std::is_same>::value)); + EXPECT_TRUE(!o1r); + + // lhs has value + int twelve = 12; + smd::optional::optional o2 = twelve; + auto o2r = o2.and_then([](int) { return smd::optional::optional{42}; }); + static_assert((std::is_same>::value)); + EXPECT_TRUE(o2r.value() == 42.f); + + // lhs is empty, rhs returns empty + smd::optional::optional o3; + auto o3r = o3.and_then([](int) { return smd::optional::optional{}; }); + static_assert((std::is_same>::value)); + EXPECT_TRUE(!o3r); + + // rhs returns empty + smd::optional::optional o4 = twelve; + auto o4r = o4.and_then([](int) { return smd::optional::optional{}; }); + static_assert((std::is_same>::value)); + EXPECT_TRUE(!o4r); + + struct rval_call_and_then { + smd::optional::optional operator()(int) && { return smd::optional::optional(42.0); }; + }; + + // ensure that function object is forwarded + int fortytwo = 42; + smd::optional::optional o5 = fortytwo; + auto o5r = o5.and_then(rval_call_and_then{}); + static_assert((std::is_same>::value)); + EXPECT_TRUE(o5r.value() == 42); + + // // ensure that lhs is forwarded + // smd::optional::optional o6 = fortytwo; + // auto o6r = + // std::move(o6).and_then([](int &&i) { return smd::optional::optional(i); }); + // static_assert((std::is_same>::value)); + // EXPECT_TRUE(o6r.value() == 42); + + // ensure that function object is const-propagated + const smd::optional::optional o7 = fortytwo; + auto o7r = o7.and_then([](const int& i) { return smd::optional::optional(i); }); + static_assert((std::is_same>::value)); + EXPECT_TRUE(o7r.value() == 42); + + // test each overload in turn + smd::optional::optional o8 = fortytwo; + auto o8r = o8.and_then([](int) { return smd::optional::make_optional(42); }); + EXPECT_TRUE(*o8r == 42); + + smd::optional::optional o9 = fortytwo; + auto o9r = std::move(o9).and_then([](int) { return smd::optional::make_optional(42); }); + EXPECT_TRUE(*o9r == 42); + + const smd::optional::optional o10 = fortytwo; + auto o10r = o10.and_then([](int) { return smd::optional::make_optional(42); }); + EXPECT_TRUE(*o10r == 42); + + const smd::optional::optional o11 = fortytwo; + auto o11r = std::move(o11).and_then([](int) { return smd::optional::make_optional(42); }); + EXPECT_TRUE(*o11r == 42); + + smd::optional::optional o16 = smd::optional::nullopt; + auto o16r = o16.and_then([](int) { return smd::optional::make_optional(42); }); + EXPECT_TRUE(!o16r); + + smd::optional::optional o17 = smd::optional::nullopt; + auto o17r = std::move(o17).and_then([](int) { return smd::optional::make_optional(42); }); + EXPECT_TRUE(!o17r); + + const smd::optional::optional o18 = smd::optional::nullopt; + auto o18r = o18.and_then([](int) { return smd::optional::make_optional(42); }); + EXPECT_TRUE(!o18r); + + const smd::optional::optional o19 = smd::optional::nullopt; + auto o19r = std::move(o19).and_then([](int) { return smd::optional::make_optional(42); }); + EXPECT_TRUE(!o19r); + + int i = 3; + smd::optional::optional o20{i}; + std::move(o20).and_then([](int& r) { return smd::optional::optional{++r}; }); + EXPECT_TRUE(o20); + EXPECT_TRUE(i == 4); +} + +TEST(OptionalRefMonadicTest, Constexpr_and_then) { + constexpr smd::optional::optional o10 = constexpr_fortytwo; + constexpr auto o10r = o10.and_then(get_opt_int); + EXPECT_TRUE(*o10r == 42); + + constexpr smd::optional::optional o11 = constexpr_fortytwo; + constexpr auto o11r = std::move(o11).and_then(get_opt_int); + EXPECT_TRUE(*o11r == 42); + + constexpr smd::optional::optional o18 = smd::optional::nullopt; + constexpr auto o18r = o18.and_then(get_opt_int); + EXPECT_TRUE(!o18r); + + constexpr smd::optional::optional o19 = smd::optional::nullopt; + constexpr auto o19r = std::move(o19).and_then(get_opt_int); + EXPECT_TRUE(!o19r); +} + +TEST(OptionalRefMonadicTest, or_else) { + int fortytwo = 42; + int thirteen = 13; + smd::optional::optional o1 = fortytwo; + EXPECT_TRUE(*(o1.or_else([&] { return smd::optional::optional(thirteen); })) == 42); + + smd::optional::optional o2; + EXPECT_EQ(*(o2.or_else([&] { return smd::optional::optional(thirteen); })), 13); +}