Skip to content

Commit 0cfe644

Browse files
committed
feat(error): add capture function to convert exception to std::expected
1 parent e29f63b commit 0cfe644

3 files changed

Lines changed: 107 additions & 8 deletions

File tree

include/zero/error.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include <array>
55
#include <utility>
66
#include <expected>
7+
#include <exception>
78
#include <system_error>
89

910
#if __has_include(<stacktrace>)
@@ -687,6 +688,25 @@ namespace zero::error {
687688
else
688689
return *std::move(expected);
689690
}
691+
692+
template<typename F>
693+
std::expected<std::invoke_result_t<F>, std::exception_ptr>
694+
capture(F &&f) {
695+
using T = std::invoke_result_t<F>;
696+
697+
try {
698+
if constexpr (std::is_void_v<T>) {
699+
f();
700+
return {};
701+
}
702+
else {
703+
return f();
704+
}
705+
}
706+
catch (const std::exception &) {
707+
return std::unexpected{std::current_exception()};
708+
}
709+
}
690710
}
691711

692712
#endif //ZERO_ERROR_H

test/error.cpp

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#include "catch_extensions.h"
22
#include <zero/error.h>
3+
#include <catch2/matchers/catch_matchers_all.hpp>
34

45
namespace {
56
std::string stringify(const int value) {
@@ -447,3 +448,83 @@ TEST_CASE("custom extended error condition defined in class", "[error]") {
447448
REQUIRE(condition == static_cast<ErrorTransformerExWrapper::ErrorTransformerEx>(ETIMEDOUT));
448449
}
449450
}
451+
452+
TEST_CASE("guard", "[error]") {
453+
SECTION("void") {
454+
SECTION("success") {
455+
REQUIRE_NOTHROW(zero::error::guard(std::expected<void, std::error_code>{}));
456+
}
457+
458+
SECTION("failure") {
459+
REQUIRE_THROWS_MATCHES(
460+
zero::error::guard(
461+
std::expected<void, std::error_code>{std::unexpect, make_error_code(std::errc::invalid_argument)}
462+
),
463+
std::system_error,
464+
Catch::Matchers::Predicate<std::system_error>([](const auto &error) {
465+
return error.code() == std::errc::invalid_argument;
466+
})
467+
);
468+
}
469+
}
470+
471+
SECTION("not void") {
472+
SECTION("success") {
473+
REQUIRE(zero::error::guard(std::expected<int, std::error_code>{0}) == 0);
474+
}
475+
476+
SECTION("failure") {
477+
REQUIRE_THROWS_MATCHES(
478+
zero::error::guard(
479+
std::expected<int, std::error_code>{std::unexpect, make_error_code(std::errc::invalid_argument)}
480+
),
481+
std::system_error,
482+
Catch::Matchers::Predicate<std::system_error>([](const auto &error) {
483+
return error.code() == std::errc::invalid_argument;
484+
})
485+
);
486+
}
487+
}
488+
}
489+
490+
TEST_CASE("capture", "[error]") {
491+
SECTION("void") {
492+
SECTION("success") {
493+
REQUIRE(zero::error::capture([] {}));
494+
}
495+
496+
SECTION("failure") {
497+
const auto result = zero::error::capture([] {
498+
throw std::system_error{make_error_code(std::errc::invalid_argument)};
499+
});
500+
REQUIRE_FALSE(result);
501+
REQUIRE_THROWS_MATCHES(
502+
std::rethrow_exception(result.error()),
503+
std::system_error,
504+
Catch::Matchers::Predicate<std::system_error>([](const auto &error) {
505+
return error.code() == std::errc::invalid_argument;
506+
})
507+
);
508+
}
509+
}
510+
511+
SECTION("not void") {
512+
SECTION("success") {
513+
REQUIRE(zero::error::capture([] { return 0; }) == 0);
514+
}
515+
516+
SECTION("failure") {
517+
const auto result = zero::error::capture([]() -> int {
518+
throw std::system_error{make_error_code(std::errc::invalid_argument)};
519+
});
520+
REQUIRE_FALSE(result);
521+
REQUIRE_THROWS_MATCHES(
522+
std::rethrow_exception(result.error()),
523+
std::system_error,
524+
Catch::Matchers::Predicate<std::system_error>([](const auto &error) {
525+
return error.code() == std::errc::invalid_argument;
526+
})
527+
);
528+
}
529+
}
530+
}

test/utility.cpp

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ TEST_CASE("flatten std::expected", "[utility]") {
1919
}
2020

2121
SECTION("has error") {
22-
REQUIRE_ERROR(zero::flatten(std::expected<std::expected<void, short>, int>{std::unexpected{-1}}), -1);
22+
REQUIRE_ERROR(zero::flatten(std::expected<std::expected<void, short>, int>{std::unexpect, -1}), -1);
2323
}
2424
}
2525

@@ -29,7 +29,7 @@ TEST_CASE("flatten std::expected", "[utility]") {
2929
}
3030

3131
SECTION("has error") {
32-
REQUIRE_ERROR(zero::flatten(std::expected<std::expected<int, short>, int>{std::unexpected{-1}}), -1);
32+
REQUIRE_ERROR(zero::flatten(std::expected<std::expected<int, short>, int>{std::unexpect, -1}), -1);
3333
}
3434
}
3535
}
@@ -42,7 +42,7 @@ TEST_CASE("flatten std::expected with error type", "[utility]") {
4242

4343
SECTION("has error") {
4444
REQUIRE_ERROR(
45-
zero::flattenWith<long>(std::expected<std::expected<void, short>, int>{std::unexpected{-1}}),
45+
zero::flattenWith<long>(std::expected<std::expected<void, short>, int>{std::unexpect, -1}),
4646
-1
4747
);
4848
}
@@ -55,9 +55,7 @@ TEST_CASE("flatten std::expected with error type", "[utility]") {
5555

5656
SECTION("has error") {
5757
REQUIRE_ERROR(
58-
zero::flattenWith<long>(std::expected<std::expected<int, short>, int>{
59-
std::unexpected{-1}
60-
}),
58+
zero::flattenWith<long>(std::expected<std::expected<int, short>, int>{std::unexpect, -1}),
6159
-1
6260
);
6361
}
@@ -70,7 +68,7 @@ TEST_CASE("extract std::expected", "[utility]") {
7068
}
7169

7270
SECTION("has error") {
73-
REQUIRE_FALSE(zero::extract(std::expected<int, int>{std::unexpected{-1}}));
71+
REQUIRE_FALSE(zero::extract(std::expected<int, int>{std::unexpect, -1}));
7472
}
7573
}
7674

@@ -86,7 +84,7 @@ TEST_CASE("transpose std::expected", "[utility]") {
8684
}
8785

8886
SECTION("has error") {
89-
REQUIRE(zero::transpose(std::expected<std::optional<int>, int>{std::unexpected{-1}}) == std::unexpected{-1});
87+
REQUIRE(zero::transpose(std::expected<std::optional<int>, int>{std::unexpect, -1}) == std::unexpected{-1} );
9088
}
9189
}
9290

0 commit comments

Comments
 (0)