diff --git a/include/zeus/expected.hpp b/include/zeus/expected.hpp index a374863..8a62249 100644 --- a/include/zeus/expected.hpp +++ b/include/zeus/expected.hpp @@ -26,6 +26,13 @@ static_assert(false, "This expected variant requires C++17"); #define ZEUS_EXPECTED_CONSTEXPR_DTOR #endif +// Detect exception support +#if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND) + #define ZEUS_EXPECTED_THROW(e) (throw (e)) +#else + #define ZEUS_EXPECTED_THROW(e) ((void)(e), std::terminate()) +#endif + #define ZEUS_EXPECTED_ABI_TAG expected_abi #define ZEUS_EXPECTED_NS_VERSION_CONCAT_EX(major, minor, patch) _v##major##_##minor##_##patch @@ -1657,14 +1664,14 @@ class expected { static_assert(std::is_copy_constructible_v, "E must be copy constructible, by LWG-3843"); if (!has_value()) - throw bad_expected_access(error()); + ZEUS_EXPECTED_THROW(bad_expected_access(error())); return val(); } constexpr T &value() & { static_assert(std::is_copy_constructible_v, "E must be copy constructible, by LWG-3843"); if (!has_value()) - throw bad_expected_access(std::as_const(error())); + ZEUS_EXPECTED_THROW(bad_expected_access(std::as_const(error()))); return val(); } constexpr const T &&value() const && @@ -1672,7 +1679,7 @@ class expected static_assert(std::is_copy_constructible_v, "E must be copy constructible, by LWG-3843"); static_assert(std::is_constructible_v, "E must be constructible from const E&&, by LWG-3843"); if (!has_value()) - throw bad_expected_access(std::move(error())); + ZEUS_EXPECTED_THROW(bad_expected_access(std::move(error()))); return std::move(val()); } constexpr T &&value() && @@ -1680,7 +1687,7 @@ class expected static_assert(std::is_copy_constructible_v, "E must be copy constructible, by LWG-3843"); static_assert(std::is_constructible_v, "E must be constructible from E&&, by LWG-3843"); if (!has_value()) - throw bad_expected_access(std::move(error())); + ZEUS_EXPECTED_THROW(bad_expected_access(std::move(error()))); return std::move(val()); } @@ -2428,7 +2435,7 @@ class expected { static_assert(std::is_copy_constructible_v, "E must be copy constructible, by LWG-3940"); if (!has_value()) - throw bad_expected_access(err()); + ZEUS_EXPECTED_THROW(bad_expected_access(err())); } constexpr void value() && @@ -2436,7 +2443,7 @@ class expected static_assert(std::is_copy_constructible_v, "E must be copy constructible, by LWG-3940"); static_assert(std::is_move_constructible_v, "E must be move constructible, by LWG-3940"); if (!has_value()) - throw bad_expected_access(std::move(err())); + ZEUS_EXPECTED_THROW(bad_expected_access(std::move(err()))); } constexpr const E &error() const & noexcept // diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index da1472e..898c030 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,2 +1,3 @@ add_subdirectory(test_expected) +add_subdirectory(test_no_exceptions) add_subdirectory(third_party) diff --git a/tests/test_no_exceptions/CMakeLists.txt b/tests/test_no_exceptions/CMakeLists.txt new file mode 100644 index 0000000..dfe0003 --- /dev/null +++ b/tests/test_no_exceptions/CMakeLists.txt @@ -0,0 +1,13 @@ +project(test_no_exceptions) + +add_executable(${PROJECT_NAME} test_no_exceptions.cpp) +target_link_libraries(${PROJECT_NAME} zeus::expected) + +if (MSVC) + target_compile_options(${PROJECT_NAME} PRIVATE /EHs-c-) + target_compile_definitions(${PROJECT_NAME} PRIVATE _HAS_EXCEPTIONS=0) +else () + target_compile_options(${PROJECT_NAME} PRIVATE -fno-exceptions) +endif () + +add_test(NAME ${PROJECT_NAME} COMMAND ${PROJECT_NAME}) diff --git a/tests/test_no_exceptions/test_no_exceptions.cpp b/tests/test_no_exceptions/test_no_exceptions.cpp new file mode 100644 index 0000000..d7dd593 --- /dev/null +++ b/tests/test_no_exceptions/test_no_exceptions.cpp @@ -0,0 +1,50 @@ +#include + +#if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND) +static_assert(false, "This test must be compiled with exceptions disabled"); +#endif + +zeus::expected try_parse(bool succeed) +{ + if (succeed) + return 42; + return zeus::unexpected(1); +} + +int main() +{ + auto e = try_parse(true); + if (!e.has_value()) + return 1; + if (*e != 42) + return 2; + + auto e2 = try_parse(false); + if (e2.has_value()) + return 3; + if (e2.error() != 1) + return 4; + + if (e2.value_or(99) != 99) + return 5; + + auto e3 = e.and_then([](int v) -> zeus::expected { return v + 1; }); + if (*e3 != 43) + return 6; + + auto e4 = e2.or_else([](int) -> zeus::expected { return 0; }); + if (*e4 != 0) + return 7; + + zeus::expected ev; + if (!ev.has_value()) + return 8; + + zeus::expected ev2{zeus::unexpect, 10}; + if (ev2.has_value()) + return 9; + if (ev2.error() != 10) + return 10; + + return 0; +}