1 /* A partial result based on std::variant and proposed std::error
2 (C) 2020 Niall Douglas <http://www.nedproductions.biz/> (11 commits)
3 File Created: Jan 2020
4
5
6 Boost Software License - Version 1.0 - August 17th, 2003
7
8 Permission is hereby granted, free of charge, to any person or organization
9 obtaining a copy of the software and accompanying documentation covered by
10 this license (the "Software") to use, reproduce, display, distribute,
11 execute, and transmit the Software, and to prepare derivative works of the
12 Software, and to permit third-parties to whom the Software is furnished to
13 do so, all subject to the following:
14
15 The copyright notices in the Software and this entire statement, including
16 the above license grant, this restriction and the following disclaimer,
17 must be included in all copies of the Software, in whole or in part, and
18 all derivative works of the Software, unless such copies or derivative
19 works are solely in the form of machine-executable object code generated by
20 a source language processor.
21
22 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
25 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
26 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
27 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
28 DEALINGS IN THE SOFTWARE.
29 */
30
31 #ifndef BOOST_OUTCOME_SYSTEM_ERROR2_RESULT_HPP
32 #define BOOST_OUTCOME_SYSTEM_ERROR2_RESULT_HPP
33
34 #include "error.hpp"
35
36 #if __cplusplus >= 201703L || _HAS_CXX17
37 #if __has_include(<variant>)
38
39 #include <exception>
40 #include <variant>
41
42 BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_BEGIN
43
44 template <class T> inline constexpr std::in_place_type_t<T> in_place_type{};
45
46 template <class T> class result;
47
48 //! \brief A trait for detecting result types
49 template <class T> struct is_result : public std::false_type
50 {
51 };
52 template <class T> struct is_result<result<T>> : public std::true_type
53 {
54 };
55
56 /*! \brief Exception type representing the failure to retrieve an error.
57 */
58 class bad_result_access : public std::exception
59 {
60 public:
61 bad_result_access() = default;
62 //! Return an explanatory string
what() const63 virtual const char *what() const noexcept override { return "bad result access"; } // NOLINT
64 };
65
66 namespace detail
67 {
68 struct void_
69 {
70 };
71 template <class T> using devoid = std::conditional_t<std::is_void_v<T>, void_, T>;
72 } // namespace detail
73
74 /*! \class result
75 \brief A imperfect `result<T>` type with its error type hardcoded to `error`, only available on C++ 17 or later.
76
77 Note that the proper `result<T>` type does not have the possibility of
78 valueless by exception state. This implementation is therefore imperfect.
79 */
80 template <class T> class result : protected std::variant<BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE::error, detail::devoid<T>>
81 {
82 using _base = std::variant<BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE::error, detail::devoid<T>>;
83 static_assert(!std::is_reference_v<T>, "Type cannot be a reference");
84 static_assert(!std::is_array_v<T>, "Type cannot be an array");
85 static_assert(!std::is_same_v<T, BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE::error>, "Type cannot be a std::error");
86 // not success nor failure types
87
88 struct _implicit_converting_constructor_tag
89 {
90 };
91 struct _explicit_converting_constructor_tag
92 {
93 };
94 struct _implicit_constructor_tag
95 {
96 };
97 struct _implicit_in_place_value_constructor_tag
98 {
99 };
100 struct _implicit_in_place_error_constructor_tag
101 {
102 };
103
104 public:
105 //! The value type
106 using value_type = T;
107 //! The error type
108 using error_type = BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE::error;
109 //! The value type, if it is available, else a usefully named unusable internal type
110 using value_type_if_enabled = detail::devoid<T>;
111 //! Used to rebind result types
112 template <class U> using rebind = result<U>;
113
114 protected:
_check() const115 constexpr void _check() const
116 {
117 if(_base::index() == 0)
118 {
119 std::get_if<0>(this)->throw_exception();
120 }
121 }
122 constexpr
123 #ifdef _MSC_VER
124 __declspec(noreturn)
125 #elif defined(__GNUC__) || defined(__clang__)
126 __attribute__((noreturn))
127 #endif
_ub()128 void _ub()
129 {
130 assert(false); // NOLINT
131 #if defined(__GNUC__) || defined(__clang__)
132 __builtin_unreachable();
133 #elif defined(_MSC_VER)
134 __assume(0);
135 #endif
136 }
137
138 public:
_internal()139 constexpr _base &_internal() noexcept { return *this; }
_internal() const140 constexpr const _base &_internal() const noexcept { return *this; }
141
142 //! Default constructor is disabled
143 result() = delete;
144 //! Copy constructor
145 result(const result &) = delete;
146 //! Move constructor
147 result(result &&) = default;
148 //! Copy assignment
149 result &operator=(const result &) = delete;
150 //! Move assignment
151 result &operator=(result &&) = default;
152 //! Destructor
153 ~result() = default;
154
155 //! Implicit result converting move constructor
156 template <class U, std::enable_if_t<std::is_convertible_v<U, T>, bool> = true>
result(result<U> && o,_implicit_converting_constructor_tag={})157 constexpr result(result<U> &&o, _implicit_converting_constructor_tag = {}) noexcept(std::is_nothrow_constructible_v<T, U>)
158 : _base(std::move(o))
159 {
160 }
161 //! Implicit result converting copy constructor
162 template <class U, std::enable_if_t<std::is_convertible_v<U, T>, bool> = true>
result(const result<U> & o,_implicit_converting_constructor_tag={})163 constexpr result(const result<U> &o, _implicit_converting_constructor_tag = {}) noexcept(std::is_nothrow_constructible_v<T, U>)
164 : _base(o)
165 {
166 }
167 //! Explicit result converting move constructor
168 template <class U, std::enable_if_t<std::is_constructible_v<T, U>, bool> = true>
result(result<U> && o,_explicit_converting_constructor_tag={})169 constexpr explicit result(result<U> &&o, _explicit_converting_constructor_tag = {}) noexcept(std::is_nothrow_constructible_v<T, U>)
170 : _base(std::move(o))
171 {
172 }
173 //! Explicit result converting copy constructor
174 template <class U, std::enable_if_t<std::is_constructible_v<T, U>, bool> = true>
result(const result<U> & o,_explicit_converting_constructor_tag={})175 constexpr explicit result(const result<U> &o, _explicit_converting_constructor_tag = {}) noexcept(std::is_nothrow_constructible_v<T, U>)
176 : _base(o)
177 {
178 }
179
180 //! Anything which `std::variant<error, T>` will construct from, we shall implicitly construct from
181 using _base::_base;
182
183 //! Special case `in_place_type_t<void>`
result(std::in_place_type_t<void>)184 constexpr explicit result(std::in_place_type_t<void> /*unused*/) noexcept
185 : _base(in_place_type<detail::void_>)
186 {
187 }
188
189 //! Implicit in-place converting error constructor
190 template <class Arg1, class Arg2, class... Args, //
191 std::enable_if_t<!(std::is_constructible_v<value_type, Arg1, Arg2, Args...> && std::is_constructible_v<error_type, Arg1, Arg2, Args...>) //
192 &&std::is_constructible_v<error_type, Arg1, Arg2, Args...>,
193 bool> = true,
194 long = 5>
result(Arg1 && arg1,Arg2 && arg2,Args &&...args)195 constexpr result(Arg1 &&arg1, Arg2 &&arg2, Args &&... args) noexcept(std::is_nothrow_constructible_v<error_type, Arg1, Arg2, Args...>)
196 : _base(std::in_place_index<0>, std::forward<Arg1>(arg1), std::forward<Arg2>(arg2), std::forward<Args>(args)...)
197 {
198 }
199
200 //! Implicit in-place converting value constructor
201 template <class Arg1, class Arg2, class... Args, //
202 std::enable_if_t<!(std::is_constructible_v<value_type, Arg1, Arg2, Args...> && std::is_constructible_v<error_type, Arg1, Arg2, Args...>) //
203 &&std::is_constructible_v<value_type, Arg1, Arg2, Args...>,
204 bool> = true,
205 int = 5>
result(Arg1 && arg1,Arg2 && arg2,Args &&...args)206 constexpr result(Arg1 &&arg1, Arg2 &&arg2, Args &&... args) noexcept(std::is_nothrow_constructible_v<value_type, Arg1, Arg2, Args...>)
207 : _base(std::in_place_index<1>, std::forward<Arg1>(arg1), std::forward<Arg2>(arg2), std::forward<Args>(args)...)
208 {
209 }
210
211 //! Implicit construction from any type where an ADL discovered `make_status_code(T, Args ...)` returns a `status_code`.
212 template <class U, class... Args, //
213 class MakeStatusCodeResult = typename detail::safe_get_make_status_code_result<U, Args...>::type, // Safe ADL lookup of make_status_code(), returns void if not found
214 typename std::enable_if<!std::is_same<typename std::decay<U>::type, result>::value // not copy/move of self
215 && !std::is_same<typename std::decay<U>::type, value_type>::value // not copy/move of value type
216 && is_status_code<MakeStatusCodeResult>::value // ADL makes a status code
217 && std::is_constructible<error_type, MakeStatusCodeResult>::value, // ADLed status code is compatible
218 bool>::type = true>
result(U && v,Args &&...args)219 constexpr result(U &&v, Args &&... args) noexcept(noexcept(make_status_code(std::declval<U>(), std::declval<Args>()...))) // NOLINT
220 : _base(std::in_place_index<0>, make_status_code(static_cast<U &&>(v), static_cast<Args &&>(args)...))
221 {
222 }
223
224 //! Swap with another result
swap(result & o)225 constexpr void swap(result &o) noexcept(std::is_nothrow_swappable_v<_base>) { _base::swap(o); }
226
227 //! Clone the result
clone() const228 constexpr result clone() const { return has_value() ? result(value()) : result(error().clone()); }
229
230 //! True if result has a value
has_value() const231 constexpr bool has_value() const noexcept { return _base::index() == 1; }
232 //! True if result has a value
operator bool() const233 explicit operator bool() const noexcept { return has_value(); }
234 //! True if result has an error
has_error() const235 constexpr bool has_error() const noexcept { return _base::index() == 0; }
236
237 //! Accesses the value if one exists, else calls `.error().throw_exception()`.
value()238 constexpr value_type_if_enabled &value() &
239 {
240 _check();
241 return std::get<1>(*this);
242 }
243 //! Accesses the value if one exists, else calls `.error().throw_exception()`.
value() const244 constexpr const value_type_if_enabled &value() const &
245 {
246 _check();
247 return std::get<1>(*this);
248 }
249 //! Accesses the value if one exists, else calls `.error().throw_exception()`.
value()250 constexpr value_type_if_enabled &&value() &&
251 {
252 _check();
253 return std::get<1>(std::move(*this));
254 }
255 //! Accesses the value if one exists, else calls `.error().throw_exception()`.
value() const256 constexpr const value_type_if_enabled &&value() const &&
257 {
258 _check();
259 return std::get<1>(std::move(*this));
260 }
261
262 //! Accesses the error if one exists, else throws `bad_result_access`.
error()263 constexpr error_type &error() &
264 {
265 if(!has_error())
266 {
267 #ifndef BOOST_NO_EXCEPTIONS
268 throw bad_result_access();
269 #else
270 abort();
271 #endif
272 }
273 return *std::get_if<0>(this);
274 }
275 //! Accesses the error if one exists, else throws `bad_result_access`.
error() const276 constexpr const error_type &error() const &
277 {
278 if(!has_error())
279 {
280 #ifndef BOOST_NO_EXCEPTIONS
281 throw bad_result_access();
282 #else
283 abort();
284 #endif
285 }
286 return *std::get_if<0>(this);
287 }
288 //! Accesses the error if one exists, else throws `bad_result_access`.
error()289 constexpr error_type &&error() &&
290 {
291 if(!has_error())
292 {
293 #ifndef BOOST_NO_EXCEPTIONS
294 throw bad_result_access();
295 #else
296 abort();
297 #endif
298 }
299 return std::move(*std::get_if<0>(this));
300 }
301 //! Accesses the error if one exists, else throws `bad_result_access`.
error() const302 constexpr const error_type &&error() const &&
303 {
304 if(!has_error())
305 {
306 #ifndef BOOST_NO_EXCEPTIONS
307 throw bad_result_access();
308 #else
309 abort();
310 #endif
311 }
312 return std::move(*std::get_if<0>(this));
313 }
314
315 //! Accesses the value, being UB if none exists
assume_value()316 constexpr value_type_if_enabled &assume_value() & noexcept
317 {
318 if(!has_value())
319 {
320 _ub();
321 }
322 return *std::get_if<1>(this);
323 }
324 //! Accesses the error, being UB if none exists
assume_value() const325 constexpr const value_type_if_enabled &assume_value() const &noexcept
326 {
327 if(!has_value())
328 {
329 _ub();
330 }
331 return *std::get_if<1>(this);
332 }
333 //! Accesses the error, being UB if none exists
assume_value()334 constexpr value_type_if_enabled &&assume_value() && noexcept
335 {
336 if(!has_value())
337 {
338 _ub();
339 }
340 return std::move(*std::get_if<1>(this));
341 }
342 //! Accesses the error, being UB if none exists
assume_value() const343 constexpr const value_type_if_enabled &&assume_value() const &&noexcept
344 {
345 if(!has_value())
346 {
347 _ub();
348 }
349 return std::move(*std::get_if<1>(this));
350 }
351
352 //! Accesses the error, being UB if none exists
assume_error()353 constexpr error_type &assume_error() & noexcept
354 {
355 if(!has_error())
356 {
357 _ub();
358 }
359 return *std::get_if<0>(this);
360 }
361 //! Accesses the error, being UB if none exists
assume_error() const362 constexpr const error_type &assume_error() const &noexcept
363 {
364 if(!has_error())
365 {
366 _ub();
367 }
368 return *std::get_if<0>(this);
369 }
370 //! Accesses the error, being UB if none exists
assume_error()371 constexpr error_type &&assume_error() && noexcept
372 {
373 if(!has_error())
374 {
375 _ub();
376 }
377 return std::move(*std::get_if<0>(this));
378 }
379 //! Accesses the error, being UB if none exists
assume_error() const380 constexpr const error_type &&assume_error() const &&noexcept
381 {
382 if(!has_error())
383 {
384 _ub();
385 }
386 return std::move(*std::get_if<0>(this));
387 }
388 };
389
390 //! True if the two results compare equal.
operator ==(const result<T> & a,const result<U> & b)391 template <class T, class U, typename = decltype(std::declval<T>() == std::declval<U>())> constexpr inline bool operator==(const result<T> &a, const result<U> &b) noexcept
392 {
393 const auto &x = a._internal();
394 return x == b;
395 }
396 //! True if the two results compare unequal.
operator !=(const result<T> & a,const result<U> & b)397 template <class T, class U, typename = decltype(std::declval<T>() != std::declval<U>())> constexpr inline bool operator!=(const result<T> &a, const result<U> &b) noexcept
398 {
399 const auto &x = a._internal();
400 return x != b;
401 }
402
403 BOOST_OUTCOME_SYSTEM_ERROR2_NAMESPACE_END
404
405 #endif
406 #endif
407 #endif
408