• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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