1 #ifndef BOOST_LEAF_RESULT_HPP_INCLUDED
2 #define BOOST_LEAF_RESULT_HPP_INCLUDED
3 
4 // Copyright (c) 2018-2020 Emil Dotchevski and Reverge Studios, Inc.
5 
6 // Distributed under the Boost Software License, Version 1.0. (See accompanying
7 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
8 
9 #ifndef BOOST_LEAF_ENABLE_WARNINGS
10 #   if defined(__clang__)
11 #       pragma clang system_header
12 #   elif (__GNUC__*100+__GNUC_MINOR__>301)
13 #       pragma GCC system_header
14 #   elif defined(_MSC_VER)
15 #       pragma warning(push,1)
16 #   endif
17 #endif
18 
19 #include <boost/leaf/error.hpp>
20 #include <climits>
21 
22 namespace boost { namespace leaf {
23 
24     class bad_result:
25         public std::exception,
26         public error_id
27     {
what() const28         char const * what() const noexcept final override
29         {
30             return "boost::leaf::bad_result";
31         }
32 
33     public:
34 
bad_result(error_id id)35         explicit bad_result( error_id id ) noexcept:
36             error_id(id)
37         {
38             BOOST_LEAF_ASSERT(value());
39         }
40     };
41 
42     ////////////////////////////////////////
43 
44     namespace leaf_detail
45     {
46         template <class T>
47         struct stored
48         {
49             using type = T;
50             using value_type = T;
51             using value_type_const = T const;
52             using value_cref = T const &;
53             using value_ref = T &;
54             using value_rv_cref = T const &&;
55             using value_rv_ref = T &&;
56         };
57 
58         template <class T>
59         struct stored<T &>
60         {
61             using type = std::reference_wrapper<T>;
62             using value_type_const = T;
63             using value_type = T;
64             using value_ref = T &;
65             using value_cref = T &;
66             using value_rv_ref = T &;
67             using value_rv_cref = T &;
68         };
69 
70         class result_discriminant
71         {
72             unsigned state_;
73 
74         public:
75 
76             enum kind_t
77             {
78                 no_error = 0,
79                 err_id = 1,
80                 ctx_ptr = 2,
81                 val = 3
82             };
83 
result_discriminant(error_id id)84             explicit result_discriminant( error_id id ) noexcept:
85                 state_(id.value())
86             {
87                 BOOST_LEAF_ASSERT(state_==0 || (state_&3)==1);
88             }
89 
90             struct kind_val { };
result_discriminant(kind_val)91             explicit result_discriminant( kind_val ) noexcept:
92                 state_(val)
93             {
94             }
95 
96             struct kind_ctx_ptr { };
result_discriminant(kind_ctx_ptr)97             explicit result_discriminant( kind_ctx_ptr ) noexcept:
98                 state_(ctx_ptr)
99             {
100             }
101 
kind() const102             kind_t kind() const noexcept
103             {
104                 return kind_t(state_&3);
105             }
106 
get_error_id() const107             error_id get_error_id() const noexcept
108             {
109                 BOOST_LEAF_ASSERT(kind()==no_error || kind()==err_id);
110                 return make_error_id(state_);
111             }
112         };
113     }
114 
115     ////////////////////////////////////////
116 
117     template <class T>
118     class result
119     {
120         template <class U>
121         friend class result;
122 
123         using result_discriminant = leaf_detail::result_discriminant;
124 
125         struct error_result
126         {
127             error_result( error_result && ) = default;
128             error_result( error_result const & ) = delete;
129             error_result & operator=( error_result const & ) = delete;
130 
131             result & r_;
132 
error_resultboost::leaf::result::error_result133             error_result( result & r ) noexcept:
134                 r_(r)
135             {
136             }
137 
138             template <class U>
operator result<U>boost::leaf::result::error_result139             operator result<U>() noexcept
140             {
141                 switch(r_.what_.kind())
142                 {
143                 case result_discriminant::val:
144                     return result<U>(error_id());
145                 case result_discriminant::ctx_ptr:
146                     return result<U>(std::move(r_.ctx_));
147                 default:
148                     return result<U>(std::move(r_.what_));
149                 }
150             }
151 
operator error_idboost::leaf::result::error_result152             operator error_id() noexcept
153             {
154                 switch(r_.what_.kind())
155                 {
156                 case result_discriminant::val:
157                     return error_id();
158                 case result_discriminant::ctx_ptr:
159                 {
160                     error_id captured_id = r_.ctx_->propagate_captured_errors();
161                     leaf_detail::id_factory<>::current_id = captured_id.value();
162                     return captured_id;
163                 }
164                 default:
165                     return r_.what_.get_error_id();
166                 }
167             }
168         };
169 
170         using stored_type = typename leaf_detail::stored<T>::type;
171         using value_type = typename leaf_detail::stored<T>::value_type;
172         using value_type_const = typename leaf_detail::stored<T>::value_type_const;
173         using value_ref = typename leaf_detail::stored<T>::value_ref;
174         using value_cref = typename leaf_detail::stored<T>::value_cref;
175         using value_rv_ref = typename leaf_detail::stored<T>::value_rv_ref;
176         using value_rv_cref = typename leaf_detail::stored<T>::value_rv_cref;
177 
178         union
179         {
180             stored_type stored_;
181             context_ptr ctx_;
182         };
183 
184         result_discriminant what_;
185 
destroy() const186         void destroy() const noexcept
187         {
188             switch(this->what_.kind())
189             {
190             case result_discriminant::val:
191                 stored_.~stored_type();
192                 break;
193             case result_discriminant::ctx_ptr:
194                 BOOST_LEAF_ASSERT(!ctx_ || ctx_->captured_id_);
195                 ctx_.~context_ptr();
196             default:
197                 break;
198             }
199         }
200 
201         template <class U>
move_from(result<U> && x)202         result_discriminant move_from( result<U> && x ) noexcept
203         {
204             auto x_what = x.what_;
205             switch(x_what.kind())
206             {
207             case result_discriminant::val:
208                 (void) new(&stored_) stored_type(std::move(x.stored_));
209                 break;
210             case result_discriminant::ctx_ptr:
211                 BOOST_LEAF_ASSERT(!x.ctx_ || x.ctx_->captured_id_);
212                 (void) new(&ctx_) context_ptr(std::move(x.ctx_));
213             default:
214                 break;
215             }
216             return x_what;
217         }
218 
result(result_discriminant && what)219         result( result_discriminant && what ) noexcept:
220             what_(std::move(what))
221         {
222             BOOST_LEAF_ASSERT(what_.kind()==result_discriminant::err_id || what_.kind()==result_discriminant::no_error);
223         }
224 
get_error_id() const225         error_id get_error_id() const noexcept
226         {
227             BOOST_LEAF_ASSERT(what_.kind()!=result_discriminant::val);
228             return what_.kind()==result_discriminant::ctx_ptr ? ctx_->captured_id_ : what_.get_error_id();
229         }
230 
231         static int init_T_with_U( T && );
232 
233     protected:
234 
enforce_value_state() const235         void enforce_value_state() const
236         {
237             if( what_.kind() != result_discriminant::val )
238                 ::boost::leaf::throw_exception(bad_result(get_error_id()));
239         }
240 
241     public:
242 
result(result && x)243         result( result && x ) noexcept:
244             what_(move_from(std::move(x)))
245         {
246         }
247 
248         template <class U>
result(result<U> && x)249         result( result<U> && x ) noexcept:
250             what_(move_from(std::move(x)))
251 
252         {
253         }
254 
result()255         result():
256             stored_(stored_type()),
257             what_(result_discriminant::kind_val{})
258         {
259         }
260 
result(value_type && v)261         result( value_type && v ) noexcept:
262             stored_(std::forward<value_type>(v)),
263             what_(result_discriminant::kind_val{})
264         {
265         }
266 
result(value_type const & v)267         result( value_type const & v ):
268             stored_(v),
269             what_(result_discriminant::kind_val{})
270         {
271         }
272 
result(error_id err)273         result( error_id err ) noexcept:
274             what_(err)
275         {
276         }
277 
278         // SFINAE: T can be initialized with a U, e.g. result<std::string>("literal").
279         // Not using is_constructible on purpose, bug with COMPILER=/usr/bin/clang++ CXXSTD=11 clang 3.3.
280         template <class U>
result(U && u,decltype(init_T_with_U (std::forward<U> (u)))* =0)281         result( U && u, decltype(init_T_with_U(std::forward<U>(u))) * = 0 ):
282             stored_(std::forward<U>(u)),
283             what_(result_discriminant::kind_val{})
284         {
285         }
286 
result(std::error_code const & ec)287         result( std::error_code const & ec ) noexcept:
288             what_(error_id(ec))
289         {
290         }
291 
292         template <class Enum>
result(Enum e,typename std::enable_if<std::is_error_code_enum<Enum>::value,int>::type * =0)293         result( Enum e, typename std::enable_if<std::is_error_code_enum<Enum>::value, int>::type * = 0 ) noexcept:
294             what_(error_id(e))
295         {
296         }
297 
result(context_ptr && ctx)298         result( context_ptr && ctx ) noexcept:
299             ctx_(std::move(ctx)),
300             what_(result_discriminant::kind_ctx_ptr{})
301         {
302         }
303 
~result()304         ~result() noexcept
305         {
306             destroy();
307         }
308 
operator =(result && x)309         result & operator=( result && x ) noexcept
310         {
311             destroy();
312             what_ = move_from(std::move(x));
313             return *this;
314         }
315 
316         template <class U>
operator =(result<U> && x)317         result & operator=( result<U> && x ) noexcept
318         {
319             destroy();
320             what_ = move_from(std::move(x));
321             return *this;
322         }
323 
operator bool() const324         explicit operator bool() const noexcept
325         {
326             return what_.kind() == result_discriminant::val;
327         }
328 
value() const329         value_cref value() const &
330         {
331             enforce_value_state();
332             return stored_;
333         }
334 
value()335         value_ref value() &
336         {
337             enforce_value_state();
338             return stored_;
339         }
340 
value() const341         value_rv_cref value() const &&
342         {
343             enforce_value_state();
344             return std::move(stored_);
345         }
346 
value()347         value_rv_ref value() &&
348         {
349             enforce_value_state();
350             return std::move(stored_);
351         }
352 
operator *() const353         value_cref operator*() const &
354         {
355             return value();
356         }
357 
operator *()358         value_ref operator*() &
359         {
360             return value();
361         }
362 
operator *() const363         value_rv_cref operator*() const &&
364         {
365             return value();
366         }
367 
operator *()368         value_rv_ref operator*() &&
369         {
370             return value();
371         }
372 
operator ->() const373         value_type_const * operator->() const
374         {
375             return &value();
376         }
377 
operator ->()378         value_type * operator->()
379         {
380             return &value();
381         }
382 
error()383         error_result error() noexcept
384         {
385             return error_result{*this};
386         }
387 
388         template <class... Item>
load(Item &&...item)389         error_id load( Item && ... item ) noexcept
390         {
391             return error_id(error()).load(std::forward<Item>(item)...);
392         }
393     };
394 
395     ////////////////////////////////////////
396 
397     namespace leaf_detail
398     {
399         struct void_ { };
400     }
401 
402     template <>
403     class result<void>:
404         result<leaf_detail::void_>
405     {
406         using result_discriminant = leaf_detail::result_discriminant;
407         using void_ = leaf_detail::void_;
408         using base = result<void_>;
409 
410         template <class U>
411         friend class result;
412 
result(result_discriminant && what)413         result( result_discriminant && what ) noexcept:
414             base(std::move(what))
415         {
416         }
417 
418     public:
419 
420         using value_type = void;
421 
result(result && x)422         result( result && x ) noexcept:
423             base(std::move(x))
424         {
425         }
426 
result()427         result() noexcept
428         {
429         }
430 
result(error_id err)431         result( error_id err ) noexcept:
432             base(err)
433         {
434         }
435 
result(std::error_code const & ec)436         result( std::error_code const & ec ) noexcept:
437             base(ec)
438         {
439         }
440 
441         template <class Enum>
result(Enum e,typename std::enable_if<std::is_error_code_enum<Enum>::value,Enum>::type * =0)442         result( Enum e, typename std::enable_if<std::is_error_code_enum<Enum>::value, Enum>::type * = 0 ) noexcept:
443             base(e)
444         {
445         }
446 
result(context_ptr && ctx)447         result( context_ptr && ctx ) noexcept:
448             base(std::move(ctx))
449         {
450         }
451 
~result()452         ~result() noexcept
453         {
454         }
455 
value() const456         void value() const
457         {
458             base::enforce_value_state();
459         }
460 
461         using base::operator=;
462         using base::operator bool;
463         using base::get_error_id;
464         using base::error;
465         using base::load;
466     };
467 
468     ////////////////////////////////////////
469 
470     template <class R>
471     struct is_result_type;
472 
473     template <class T>
474     struct is_result_type<result<T>>: std::true_type
475     {
476     };
477 
478 } }
479 
480 #endif
481