The main posixpp library and associated tests.
Revision | fb5ee525b881762d6d71a476e6a7b144c5d50bdf (tree) |
---|---|
Time | 2021-04-25 07:45:43 |
Author | Eric Hopper <hopper@omni...> |
Commiter | Eric Hopper |
Remove dependency on variant. It uses virtual tables.
@@ -3,7 +3,6 @@ | ||
3 | 3 | #include <system_error> |
4 | 4 | #include <utility> |
5 | 5 | #include <stdexcept> |
6 | -#include <variant> | |
7 | 6 | #include <concepts> |
8 | 7 | |
9 | 8 | namespace posixpp { |
@@ -12,16 +11,37 @@ | ||
12 | 11 | class expected_base { |
13 | 12 | public: |
14 | 13 | struct err_tag {}; // Just a type to serve as a tag to indicate error value. |
15 | - | |
14 | +/* | |
16 | 15 | struct err_t { |
17 | 16 | int errval; |
18 | 17 | |
19 | 18 | err_t(int e) : errval(e) {} |
20 | 19 | operator int() const { return errval; } |
21 | 20 | }; // Just a type to serve as a tag to indicate error value. |
21 | +*/ | |
22 | 22 | }; |
23 | + | |
23 | 24 | } |
24 | 25 | |
26 | + | |
27 | +class no_error_here : public ::std::exception | |
28 | +{ | |
29 | + public: | |
30 | + no_error_here() noexcept { } | |
31 | + | |
32 | + char const *what() const noexcept override | |
33 | + { | |
34 | + return reason_; | |
35 | + } | |
36 | + | |
37 | + private: | |
38 | + no_error_here(char const *reason) noexcept : reason_(reason) { } | |
39 | + | |
40 | + // Must point to a string with static storage duration: | |
41 | + char const *reason_ = "no error in expected when error requested"; | |
42 | +}; | |
43 | + | |
44 | + | |
25 | 45 | //! A value that may be an error, throws if accessed and is an error. |
26 | 46 | template <typename T> |
27 | 47 | class expected : private priv_::expected_base { |
@@ -30,47 +50,57 @@ | ||
30 | 50 | using priv_::expected_base::err_tag; |
31 | 51 | using result_t = T; |
32 | 52 | |
33 | - explicit expected(T const &val) requires ::std::copyable<T> : val_{val} | |
53 | + explicit constexpr expected(T const &val) noexcept requires ::std::copyable<T> | |
54 | + : val_{val}, has_error_{false} | |
34 | 55 | {} |
35 | - explicit expected(T &&val) requires ::std::movable<T> | |
36 | - : val_{::std::move(val)} | |
56 | + explicit constexpr expected(T &&val) noexcept requires ::std::movable<T> | |
57 | + : val_{::std::move(val)}, has_error_{false} | |
37 | 58 | {} |
38 | - explicit expected(err_tag const &, int ec) | |
59 | + explicit constexpr expected(err_tag const &, int ec) noexcept | |
39 | 60 | requires ::std::movable<T> || ::std::copyable<T> |
40 | - : val_{err_t{ec}} | |
61 | + : val_{.errcode_ = ec}, has_error_{true} | |
41 | 62 | {} |
42 | - | |
43 | - [[nodiscard]] T &result() requires ::std::movable<T> { | |
44 | - if (auto result = ::std::get_if<T>(&val_)) { | |
45 | - return *result; | |
46 | - } else { | |
47 | - auto const &cat = ::std::system_category(); | |
48 | - throw ::std::system_error(::std::get<err_t>(val_), cat); | |
63 | + constexpr ~expected() noexcept { | |
64 | + if (!has_error_) { | |
65 | + val_.value_.~T(); | |
49 | 66 | } |
50 | 67 | } |
51 | 68 | |
52 | - [[nodiscard]] T const &result() const requires ::std::copyable<T> { | |
53 | - if (auto result = ::std::get_if<T>(&val_)) { | |
54 | - return *result; | |
69 | + [[nodiscard]] constexpr T &&result() requires ::std::movable<T> { | |
70 | + if (!has_error_) { | |
71 | + return ::std::move(val_.value_); | |
55 | 72 | } else { |
56 | 73 | auto const &cat = ::std::system_category(); |
57 | - throw ::std::system_error(::std::get<err_t>(val_), cat); | |
74 | + throw ::std::system_error(val_.errcode_, cat); | |
75 | + } | |
76 | + } | |
77 | + | |
78 | + [[nodiscard]] constexpr T const &result() const requires ::std::copyable<T> { | |
79 | + if (!has_error_) { | |
80 | + return val_.value_; | |
81 | + } else { | |
82 | + auto const &cat = ::std::system_category(); | |
83 | + throw ::std::system_error(val_.value_, cat); | |
58 | 84 | } |
59 | 85 | } |
60 | 86 | |
61 | 87 | void throw_if_error() const { |
62 | - if (auto error = ::std::get_if<err_t>(&val_)) { | |
88 | + if (has_error_) { | |
63 | 89 | auto const &cat = ::std::system_category(); |
64 | - throw ::std::system_error(*error, cat); | |
90 | + throw ::std::system_error(val_.errcode_, cat); | |
65 | 91 | } |
66 | 92 | } |
67 | 93 | |
68 | - [[nodiscard]] bool has_error() const noexcept { | |
69 | - return ::std::holds_alternative<err_t>(val_); | |
94 | + [[nodiscard]] constexpr bool has_error() const noexcept { | |
95 | + return has_error_; | |
70 | 96 | } |
71 | 97 | |
72 | - [[nodiscard]] int error() const { | |
73 | - return ::std::get<err_t>(val_); | |
98 | + [[nodiscard]] constexpr int error() const { | |
99 | + if (has_error_) { | |
100 | + return val_.errcode_; | |
101 | + } else { | |
102 | + throw no_error_here{}; | |
103 | + } | |
74 | 104 | } |
75 | 105 | |
76 | 106 | [[nodiscard]] ::std::error_condition error_condition() const { |
@@ -78,7 +108,13 @@ | ||
78 | 108 | } |
79 | 109 | |
80 | 110 | private: |
81 | - ::std::variant<T, err_t> val_; | |
111 | + union anonymous { | |
112 | + T value_; | |
113 | + int errcode_; | |
114 | + | |
115 | + ~anonymous() {} // Destruction handled by expected<T> | |
116 | + } val_; | |
117 | + bool has_error_; | |
82 | 118 | }; |
83 | 119 | |
84 | 120 | //! A value that may be an error, throws if accessed and is an error. |
@@ -88,28 +124,32 @@ | ||
88 | 124 | using priv_::expected_base::err_tag; |
89 | 125 | using result_t = void; |
90 | 126 | |
91 | - expected() : err_(err_t{0}) {} | |
92 | - explicit expected(err_tag const &, int ec) noexcept | |
93 | - : err_{err_t{ec}} | |
127 | + expected() : errcode_{0} {} | |
128 | + constexpr explicit expected(int ec) noexcept | |
129 | + : errcode_{ec} | |
94 | 130 | {} |
95 | 131 | |
96 | 132 | void result() const { |
97 | 133 | throw_if_error(); |
98 | 134 | } |
99 | 135 | |
100 | - void throw_if_error() const { | |
101 | - if (err_ != 0) { | |
136 | + constexpr void throw_if_error() const { | |
137 | + if (errcode_ != 0) { | |
102 | 138 | auto const &cat = ::std::system_category(); |
103 | - throw ::std::system_error(err_, cat); | |
139 | + throw ::std::system_error(errcode_, cat); | |
104 | 140 | } |
105 | 141 | } |
106 | 142 | |
107 | - [[nodiscard]] bool has_error() const noexcept { | |
108 | - return err_ != 0; | |
143 | + [[nodiscard]] constexpr bool has_error() const noexcept { | |
144 | + return errcode_ != 0; | |
109 | 145 | } |
110 | 146 | |
111 | - [[nodiscard]] int error() const noexcept { | |
112 | - return err_; | |
147 | + [[nodiscard]] constexpr int error() const { | |
148 | + if (errcode_ == 0) { | |
149 | + return errcode_; | |
150 | + } else { | |
151 | + throw no_error_here{}; | |
152 | + } | |
113 | 153 | } |
114 | 154 | |
115 | 155 | [[nodiscard]] ::std::error_condition error_condition() const noexcept { |
@@ -117,7 +157,7 @@ | ||
117 | 157 | } |
118 | 158 | |
119 | 159 | private: |
120 | - err_t err_; | |
160 | + int errcode_; | |
121 | 161 | }; |
122 | 162 | |
123 | 163 | //! Call converter with result, or cascade error upward. |
@@ -158,7 +198,7 @@ | ||
158 | 198 | using outresult_t = expected<void>; |
159 | 199 | using errtag = typename outresult_t::err_tag; |
160 | 200 | if (result.has_error()) { |
161 | - return outresult_t{errtag{}, result.error()}; | |
201 | + return outresult_t{result.error()}; | |
162 | 202 | } else { |
163 | 203 | return outresult_t{}; |
164 | 204 | } |
@@ -11,8 +11,8 @@ | ||
11 | 11 | CHECK( result.has_error() == false ); |
12 | 12 | } THEN(" result.throw_if_error() does nothing ") { |
13 | 13 | CHECK_NOTHROW(result.throw_if_error()); |
14 | - } THEN(" result.error() throws bad_variant_access ") { | |
15 | - CHECK_THROWS_AS(result.error(), ::std::bad_variant_access); | |
14 | + } THEN(" result.error() throws no_error_here ") { | |
15 | + CHECK_THROWS_AS(result.error(), ::posixpp::no_error_here); | |
16 | 16 | } |
17 | 17 | } |
18 | 18 | GIVEN("An expectedd<int> result initialized as an error with ENOENT") { |