Imported Upstream version 1.72.0
[platform/upstream/boost.git] / boost / outcome / detail / basic_result_storage.hpp
1 /* Storage for a very simple basic_result type
2 (C) 2017-2019 Niall Douglas <http://www.nedproductions.biz/> (6 commits)
3 File Created: Oct 2017
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_BASIC_RESULT_STORAGE_HPP
32 #define BOOST_OUTCOME_BASIC_RESULT_STORAGE_HPP
33
34 #include "../success_failure.hpp"
35 #include "../trait.hpp"
36 #include "value_storage.hpp"
37
38 BOOST_OUTCOME_V2_NAMESPACE_EXPORT_BEGIN
39
40 namespace detail
41 {
42   template <class State, class E> constexpr inline void _set_error_is_errno(State & /*unused*/, const E & /*unused*/) {}
43   template <class R, class S, class NoValuePolicy> class basic_result_final;
44 }  // namespace detail
45
46 namespace hooks
47 {
48   template <class R, class S, class NoValuePolicy> constexpr inline uint16_t spare_storage(const detail::basic_result_final<R, S, NoValuePolicy> *r) noexcept;
49   template <class R, class S, class NoValuePolicy> constexpr inline void set_spare_storage(detail::basic_result_final<R, S, NoValuePolicy> *r, uint16_t v) noexcept;
50 }  // namespace hooks
51
52 namespace policy
53 {
54   struct base;
55 }  // namespace policy
56
57 namespace detail
58 {
59   template <bool value_throws, bool error_throws> struct basic_result_storage_swap;
60   template <class R, class EC, class NoValuePolicy>                                                                                                                                    //
61   BOOST_OUTCOME_REQUIRES(trait::type_can_be_used_in_basic_result<R> &&trait::type_can_be_used_in_basic_result<EC> && (std::is_void<EC>::value || std::is_default_constructible<EC>::value))  //
62   class basic_result_storage;
63   template <class R, class EC, class NoValuePolicy>                                                                                                                                    //
64   BOOST_OUTCOME_REQUIRES(trait::type_can_be_used_in_basic_result<R> &&trait::type_can_be_used_in_basic_result<EC> && (std::is_void<EC>::value || std::is_default_constructible<EC>::value))  //
65   class basic_result_storage
66   {
67     static_assert(trait::type_can_be_used_in_basic_result<R>, "The type R cannot be used in a basic_result");
68     static_assert(trait::type_can_be_used_in_basic_result<EC>, "The type S cannot be used in a basic_result");
69     static_assert(std::is_void<EC>::value || std::is_default_constructible<EC>::value, "The type S must be void or default constructible");
70
71     friend struct policy::base;
72     template <class T, class U, class V>                                                                                                                                              //
73     BOOST_OUTCOME_REQUIRES(trait::type_can_be_used_in_basic_result<T> &&trait::type_can_be_used_in_basic_result<U> && (std::is_void<U>::value || std::is_default_constructible<U>::value))  //
74     friend class basic_result_storage;
75     template <class T, class U, class V> friend class basic_result_final;
76     template <class T, class U, class V> friend constexpr inline uint16_t hooks::spare_storage(const detail::basic_result_final<T, U, V> *r) noexcept;        // NOLINT
77     template <class T, class U, class V> friend constexpr inline void hooks::set_spare_storage(detail::basic_result_final<T, U, V> *r, uint16_t v) noexcept;  // NOLINT
78     template <bool value_throws, bool error_throws> struct basic_result_storage_swap;
79
80     struct disable_in_place_value_type
81     {
82     };
83     struct disable_in_place_error_type
84     {
85     };
86
87   protected:
88     using _value_type = std::conditional_t<std::is_same<R, EC>::value, disable_in_place_value_type, R>;
89     using _error_type = std::conditional_t<std::is_same<R, EC>::value, disable_in_place_error_type, EC>;
90
91 #ifdef BOOST_OUTCOME_STANDARDESE_IS_IN_THE_HOUSE
92     detail::value_storage_trivial<_value_type> _state;
93 #else
94     detail::value_storage_select_impl<_value_type> _state;
95 #endif
96     detail::devoid<_error_type> _error;
97
98   public:
99     // Used by iostream support to access state
100     detail::value_storage_select_impl<_value_type> &_iostreams_state() { return _state; }
101     const detail::value_storage_select_impl<_value_type> &_iostreams_state() const { return _state; }
102
103     // Hack to work around MSVC bug in /permissive-
104     detail::value_storage_select_impl<_value_type> &_msvc_nonpermissive_state() { return _state; }
105     detail::devoid<_error_type> &_msvc_nonpermissive_error() { return _error; }
106
107   protected:
108     basic_result_storage() = default;
109     basic_result_storage(const basic_result_storage &) = default;             // NOLINT
110     basic_result_storage(basic_result_storage &&) = default;                  // NOLINT
111     basic_result_storage &operator=(const basic_result_storage &) = default;  // NOLINT
112     basic_result_storage &operator=(basic_result_storage &&) = default;       // NOLINT
113     ~basic_result_storage() = default;
114
115     template <class... Args>
116     constexpr explicit basic_result_storage(in_place_type_t<_value_type> _, Args &&... args) noexcept(std::is_nothrow_constructible<_value_type, Args...>::value)
117         : _state{_, static_cast<Args &&>(args)...}
118         , _error()
119     {
120     }
121     template <class U, class... Args>
122     constexpr basic_result_storage(in_place_type_t<_value_type> _, std::initializer_list<U> il, Args &&... args) noexcept(std::is_nothrow_constructible<_value_type, std::initializer_list<U>, Args...>::value)
123         : _state{_, il, static_cast<Args &&>(args)...}
124         , _error()
125     {
126     }
127     template <class... Args>
128     constexpr explicit basic_result_storage(in_place_type_t<_error_type> /*unused*/, Args &&... args) noexcept(std::is_nothrow_constructible<_error_type, Args...>::value)
129         : _state{detail::status_have_error}
130         , _error(static_cast<Args &&>(args)...)
131     {
132       _set_error_is_errno(_state, _error);
133     }
134     template <class U, class... Args>
135     constexpr basic_result_storage(in_place_type_t<_error_type> /*unused*/, std::initializer_list<U> il, Args &&... args) noexcept(std::is_nothrow_constructible<_error_type, std::initializer_list<U>, Args...>::value)
136         : _state{detail::status_have_error}
137         , _error{il, static_cast<Args &&>(args)...}
138     {
139       _set_error_is_errno(_state, _error);
140     }
141     struct compatible_conversion_tag
142     {
143     };
144     template <class T, class U, class V>
145     constexpr basic_result_storage(compatible_conversion_tag /*unused*/, const basic_result_storage<T, U, V> &o) noexcept(std::is_nothrow_constructible<_value_type, T>::value &&std::is_nothrow_constructible<_error_type, U>::value)
146         : _state(o._state)
147         , _error(o._error)
148     {
149     }
150     template <class T, class V>
151     constexpr basic_result_storage(compatible_conversion_tag /*unused*/, const basic_result_storage<T, void, V> &o) noexcept(std::is_nothrow_constructible<_value_type, T>::value)
152         : _state(o._state)
153         , _error(_error_type{})
154     {
155     }
156     template <class T, class U, class V>
157     constexpr basic_result_storage(compatible_conversion_tag /*unused*/, basic_result_storage<T, U, V> &&o) noexcept(std::is_nothrow_constructible<_value_type, T>::value &&std::is_nothrow_constructible<_error_type, U>::value)
158         : _state(static_cast<decltype(o._state) &&>(o._state))
159         , _error(static_cast<U &&>(o._error))
160     {
161     }
162     template <class T, class V>
163     constexpr basic_result_storage(compatible_conversion_tag /*unused*/, basic_result_storage<T, void, V> &&o) noexcept(std::is_nothrow_constructible<_value_type, T>::value)
164         : _state(static_cast<decltype(o._state) &&>(o._state))
165         , _error(_error_type{})
166     {
167     }
168
169     struct make_error_code_compatible_conversion_tag
170     {
171     };
172     template <class T, class U, class V>
173     constexpr basic_result_storage(make_error_code_compatible_conversion_tag /*unused*/, const basic_result_storage<T, U, V> &o) noexcept(std::is_nothrow_constructible<_value_type, T>::value &&noexcept(make_error_code(std::declval<U>())))
174         : _state(o._state)
175         , _error(make_error_code(o._error))
176     {
177     }
178     template <class T, class U, class V>
179     constexpr basic_result_storage(make_error_code_compatible_conversion_tag /*unused*/, basic_result_storage<T, U, V> &&o) noexcept(std::is_nothrow_constructible<_value_type, T>::value &&noexcept(make_error_code(std::declval<U>())))
180         : _state(static_cast<decltype(o._state) &&>(o._state))
181         , _error(make_error_code(static_cast<U &&>(o._error)))
182     {
183     }
184
185     struct make_exception_ptr_compatible_conversion_tag
186     {
187     };
188     template <class T, class U, class V>
189     constexpr basic_result_storage(make_exception_ptr_compatible_conversion_tag /*unused*/, const basic_result_storage<T, U, V> &o) noexcept(std::is_nothrow_constructible<_value_type, T>::value &&noexcept(make_exception_ptr(std::declval<U>())))
190         : _state(o._state)
191         , _error(make_exception_ptr(o._error))
192     {
193     }
194     template <class T, class U, class V>
195     constexpr basic_result_storage(make_exception_ptr_compatible_conversion_tag /*unused*/, basic_result_storage<T, U, V> &&o) noexcept(std::is_nothrow_constructible<_value_type, T>::value &&noexcept(make_exception_ptr(std::declval<U>())))
196         : _state(static_cast<decltype(o._state) &&>(o._state))
197         , _error(make_exception_ptr(static_cast<U &&>(o._error)))
198     {
199     }
200   };
201
202 // Neither value nor error type can throw during swap
203 #ifndef BOOST_NO_EXCEPTIONS
204   template <> struct basic_result_storage_swap<false, false>
205 #else
206   template <bool value_throws, bool error_throws> struct basic_result_storage_swap
207 #endif
208   {
209     template <class R, class EC, class NoValuePolicy> constexpr basic_result_storage_swap(basic_result_storage<R, EC, NoValuePolicy> &a, basic_result_storage<R, EC, NoValuePolicy> &b)
210     {
211       using std::swap;
212       a._msvc_nonpermissive_state().swap(b._msvc_nonpermissive_state());
213       swap(a._msvc_nonpermissive_error(), b._msvc_nonpermissive_error());
214     }
215   };
216 #ifndef BOOST_NO_EXCEPTIONS
217   // Swap potentially throwing value first
218   template <> struct basic_result_storage_swap<true, false>
219   {
220     template <class R, class EC, class NoValuePolicy> constexpr basic_result_storage_swap(basic_result_storage<R, EC, NoValuePolicy> &a, basic_result_storage<R, EC, NoValuePolicy> &b)
221     {
222       using std::swap;
223       a._msvc_nonpermissive_state().swap(b._msvc_nonpermissive_state());
224       swap(a._msvc_nonpermissive_error(), b._msvc_nonpermissive_error());
225     }
226   };
227   // Swap potentially throwing error first
228   template <> struct basic_result_storage_swap<false, true>
229   {
230     template <class R, class EC, class NoValuePolicy> constexpr basic_result_storage_swap(basic_result_storage<R, EC, NoValuePolicy> &a, basic_result_storage<R, EC, NoValuePolicy> &b)
231     {
232       struct _
233       {
234         unsigned &a, &b;
235         bool all_good{false};
236         ~_()
237         {
238           if(!all_good)
239           {
240             // We lost one of the values
241             a |= status_lost_consistency;
242             b |= status_lost_consistency;
243           }
244         }
245       } _{a._msvc_nonpermissive_state()._status, b._msvc_nonpermissive_state()._status};
246       strong_swap(_.all_good, a._msvc_nonpermissive_error(), b._msvc_nonpermissive_error());
247       a._msvc_nonpermissive_state().swap(b._msvc_nonpermissive_state());
248     }
249   };
250   // Both could throw
251   template <> struct basic_result_storage_swap<true, true>
252   {
253     template <class R, class EC, class NoValuePolicy> basic_result_storage_swap(basic_result_storage<R, EC, NoValuePolicy> &a, basic_result_storage<R, EC, NoValuePolicy> &b)
254     {
255       using std::swap;
256       // Swap value and status first, if it throws, status will remain unchanged
257       a._msvc_nonpermissive_state().swap(b._msvc_nonpermissive_state());
258       bool all_good = false;
259       try
260       {
261         strong_swap(all_good, a._msvc_nonpermissive_error(), b._msvc_nonpermissive_error());
262       }
263       catch(...)
264       {
265         if(!all_good)
266         {
267           a._msvc_nonpermissive_state()._status |= detail::status_lost_consistency;
268           b._msvc_nonpermissive_state()._status |= detail::status_lost_consistency;
269         }
270         else
271         {
272           // We may still be able to rescue tis
273           // First try to put the value and status back
274           try
275           {
276             a._msvc_nonpermissive_state().swap(b._msvc_nonpermissive_state());
277             // If that succeeded, continue by rethrowing the exception
278           }
279           catch(...)
280           {
281             all_good = false;
282           }
283         }
284         if(!all_good)
285         {
286           // We are now trapped. The value swapped, the error did not,
287           // trying to restore the value failed. We now have
288           // inconsistent result objects. Best we can do is fix up the
289           // status bits to prevent has_value() == has_error().
290           auto check = [](basic_result_storage<R, EC, NoValuePolicy> &x) {
291             bool has_value = (x._state._status & detail::status_have_value) != 0;
292             bool has_error = (x._state._status & detail::status_have_error) != 0;
293             bool has_exception = (x._state._status & detail::status_have_exception) != 0;
294             x._state._status |= detail::status_lost_consistency;
295             if(has_value == (has_error || has_exception))
296             {
297               if(has_value)
298               {
299                 // We know the value swapped and is now set, so clear error and exception
300                 x._state._status &= ~(detail::status_have_error | detail::status_have_exception);
301               }
302               else
303               {
304                 // We know the value swapped and is now unset, so set error
305                 x._state._status |= detail::status_have_error;
306                 // TODO: Should I default construct reset _error? It's guaranteed default constructible.
307               }
308             }
309           };
310           check(a);
311           check(b);
312         }
313         throw;
314       }
315     }
316   };
317 #endif
318
319 }  // namespace detail
320 BOOST_OUTCOME_V2_NAMESPACE_END
321
322 #endif