summaryrefslogtreecommitdiff
path: root/boost/outcome/detail/value_storage.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'boost/outcome/detail/value_storage.hpp')
-rw-r--r--boost/outcome/detail/value_storage.hpp364
1 files changed, 364 insertions, 0 deletions
diff --git a/boost/outcome/detail/value_storage.hpp b/boost/outcome/detail/value_storage.hpp
new file mode 100644
index 0000000000..5f148f3eb5
--- /dev/null
+++ b/boost/outcome/detail/value_storage.hpp
@@ -0,0 +1,364 @@
+/* Essentially an internal optional implementation :)
+(C) 2017-2019 Niall Douglas <http://www.nedproductions.biz/> (59 commits)
+File Created: June 2017
+
+
+Boost Software License - Version 1.0 - August 17th, 2003
+
+Permission is hereby granted, free of charge, to any person or organization
+obtaining a copy of the software and accompanying documentation covered by
+this license (the "Software") to use, reproduce, display, distribute,
+execute, and transmit the Software, and to prepare derivative works of the
+Software, and to permit third-parties to whom the Software is furnished to
+do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including
+the above license grant, this restriction and the following disclaimer,
+must be included in all copies of the Software, in whole or in part, and
+all derivative works of the Software, unless such copies or derivative
+works are solely in the form of machine-executable object code generated by
+a source language processor.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef BOOST_OUTCOME_VALUE_STORAGE_HPP
+#define BOOST_OUTCOME_VALUE_STORAGE_HPP
+
+#include "../config.hpp"
+
+BOOST_OUTCOME_V2_NAMESPACE_BEGIN
+
+namespace detail
+{
+ using status_bitfield_type = uint32_t;
+
+ // WARNING: These bits are not tracked by abi-dumper, but changing them will break ABI!
+ static constexpr status_bitfield_type status_have_value = (1U << 0U);
+ static constexpr status_bitfield_type status_have_error = (1U << 1U);
+ static constexpr status_bitfield_type status_have_exception = (1U << 2U);
+ static constexpr status_bitfield_type status_error_is_errno = (1U << 4U); // can errno be set from this error?
+ // bit 7 unused
+ // bits 8-15 unused
+ // bits 16-31 used for user supplied 16 bit value
+ static constexpr status_bitfield_type status_2byte_shift = 16;
+ static constexpr status_bitfield_type status_2byte_mask = (0xffffU << status_2byte_shift);
+
+ // Used if T is trivial
+ template <class T> struct value_storage_trivial
+ {
+ using value_type = T;
+ union {
+ empty_type _empty;
+ devoid<T> _value;
+ };
+ status_bitfield_type _status{0};
+ constexpr value_storage_trivial() noexcept : _empty{} {}
+ // Special from-void catchall constructor, always constructs default T irrespective of whether void is valued or not (can do no better if T cannot be copied)
+ struct disable_void_catchall
+ {
+ };
+ using void_value_storage_trivial = std::conditional_t<std::is_void<T>::value, disable_void_catchall, value_storage_trivial<void>>;
+ explicit constexpr value_storage_trivial(const void_value_storage_trivial &o) noexcept(std::is_nothrow_default_constructible<value_type>::value)
+ : _value()
+ , _status(o._status)
+ {
+ }
+ value_storage_trivial(const value_storage_trivial &) = default; // NOLINT
+ value_storage_trivial(value_storage_trivial &&) = default; // NOLINT
+ value_storage_trivial &operator=(const value_storage_trivial &) = default; // NOLINT
+ value_storage_trivial &operator=(value_storage_trivial &&) = default; // NOLINT
+ ~value_storage_trivial() = default;
+ constexpr explicit value_storage_trivial(status_bitfield_type status)
+ : _empty()
+ , _status(status)
+ {
+ }
+ template <class... Args>
+ constexpr explicit value_storage_trivial(in_place_type_t<value_type> /*unused*/, Args &&... args) noexcept(std::is_nothrow_constructible<value_type, Args...>::value)
+ : _value(static_cast<Args &&>(args)...)
+ , _status(status_have_value)
+ {
+ }
+ template <class U, class... Args>
+ constexpr value_storage_trivial(in_place_type_t<value_type> /*unused*/, std::initializer_list<U> il, Args &&... args) noexcept(std::is_nothrow_constructible<value_type, std::initializer_list<U>, Args...>::value)
+ : _value(il, static_cast<Args &&>(args)...)
+ , _status(status_have_value)
+ {
+ }
+ template <class U> static constexpr bool enable_converting_constructor = !std::is_same<std::decay_t<U>, value_type>::value && std::is_constructible<value_type, U>::value;
+ BOOST_OUTCOME_TEMPLATE(class U)
+ BOOST_OUTCOME_TREQUIRES(BOOST_OUTCOME_TPRED(enable_converting_constructor<U>))
+ constexpr explicit value_storage_trivial(const value_storage_trivial<U> &o) noexcept(std::is_nothrow_constructible<value_type, U>::value)
+ : value_storage_trivial(((o._status & status_have_value) != 0) ? value_storage_trivial(in_place_type<value_type>, o._value) : value_storage_trivial()) // NOLINT
+ {
+ _status = o._status;
+ }
+ BOOST_OUTCOME_TEMPLATE(class U)
+ BOOST_OUTCOME_TREQUIRES(BOOST_OUTCOME_TPRED(enable_converting_constructor<U>))
+ constexpr explicit value_storage_trivial(value_storage_trivial<U> &&o) noexcept(std::is_nothrow_constructible<value_type, U>::value)
+ : value_storage_trivial(((o._status & status_have_value) != 0) ? value_storage_trivial(in_place_type<value_type>, static_cast<U &&>(o._value)) : value_storage_trivial()) // NOLINT
+ {
+ _status = o._status;
+ }
+ constexpr void swap(value_storage_trivial &o) noexcept
+ {
+ // storage is trivial, so just use assignment
+ auto temp = static_cast<value_storage_trivial &&>(*this);
+ *this = static_cast<value_storage_trivial &&>(o);
+ o = static_cast<value_storage_trivial &&>(temp);
+ }
+ };
+ // Used if T is non-trivial
+ template <class T> struct value_storage_nontrivial
+ {
+ using value_type = T;
+ union {
+ empty_type _empty;
+ value_type _value;
+ };
+ status_bitfield_type _status{0};
+ value_storage_nontrivial() noexcept : _empty{} {}
+ value_storage_nontrivial &operator=(const value_storage_nontrivial &) = default; // if reaches here, copy assignment is trivial
+ value_storage_nontrivial &operator=(value_storage_nontrivial &&) = default; // NOLINT if reaches here, move assignment is trivial
+ value_storage_nontrivial(value_storage_nontrivial &&o) noexcept(std::is_nothrow_move_constructible<value_type>::value) // NOLINT
+ : _status(o._status)
+ {
+ if(this->_status & status_have_value)
+ {
+ this->_status &= ~status_have_value;
+ new(&_value) value_type(static_cast<value_type &&>(o._value)); // NOLINT
+ _status = o._status;
+ }
+ }
+ value_storage_nontrivial(const value_storage_nontrivial &o) noexcept(std::is_nothrow_copy_constructible<value_type>::value)
+ : _status(o._status)
+ {
+ if(this->_status & status_have_value)
+ {
+ this->_status &= ~status_have_value;
+ new(&_value) value_type(o._value); // NOLINT
+ _status = o._status;
+ }
+ }
+ // Special from-void constructor, constructs default T if void valued
+ explicit value_storage_nontrivial(const value_storage_trivial<void> &o) noexcept(std::is_nothrow_default_constructible<value_type>::value)
+ : _status(o._status)
+ {
+ if(this->_status & status_have_value)
+ {
+ this->_status &= ~status_have_value;
+ new(&_value) value_type; // NOLINT
+ _status = o._status;
+ }
+ }
+ explicit value_storage_nontrivial(status_bitfield_type status)
+ : _empty()
+ , _status(status)
+ {
+ }
+ template <class... Args>
+ explicit value_storage_nontrivial(in_place_type_t<value_type> /*unused*/, Args &&... args) noexcept(std::is_nothrow_constructible<value_type, Args...>::value)
+ : _value(static_cast<Args &&>(args)...) // NOLINT
+ , _status(status_have_value)
+ {
+ }
+ template <class U, class... Args>
+ value_storage_nontrivial(in_place_type_t<value_type> /*unused*/, std::initializer_list<U> il, Args &&... args) noexcept(std::is_nothrow_constructible<value_type, std::initializer_list<U>, Args...>::value)
+ : _value(il, static_cast<Args &&>(args)...)
+ , _status(status_have_value)
+ {
+ }
+ template <class U> static constexpr bool enable_converting_constructor = !std::is_same<std::decay_t<U>, value_type>::value && std::is_constructible<value_type, U>::value;
+ BOOST_OUTCOME_TEMPLATE(class U)
+ BOOST_OUTCOME_TREQUIRES(BOOST_OUTCOME_TPRED(enable_converting_constructor<U>))
+ constexpr explicit value_storage_nontrivial(const value_storage_nontrivial<U> &o) noexcept(std::is_nothrow_constructible<value_type, U>::value)
+ : value_storage_nontrivial((o._status & status_have_value) != 0 ? value_storage_nontrivial(in_place_type<value_type>, o._value) : value_storage_nontrivial())
+ {
+ _status = o._status;
+ }
+ BOOST_OUTCOME_TEMPLATE(class U)
+ BOOST_OUTCOME_TREQUIRES(BOOST_OUTCOME_TPRED(enable_converting_constructor<U>))
+ constexpr explicit value_storage_nontrivial(const value_storage_trivial<U> &o) noexcept(std::is_nothrow_constructible<value_type, U>::value)
+ : value_storage_nontrivial((o._status & status_have_value) != 0 ? value_storage_nontrivial(in_place_type<value_type>, o._value) : value_storage_nontrivial())
+ {
+ _status = o._status;
+ }
+ BOOST_OUTCOME_TEMPLATE(class U)
+ BOOST_OUTCOME_TREQUIRES(BOOST_OUTCOME_TPRED(enable_converting_constructor<U>))
+ constexpr explicit value_storage_nontrivial(value_storage_nontrivial<U> &&o) noexcept(std::is_nothrow_constructible<value_type, U>::value)
+ : value_storage_nontrivial((o._status & status_have_value) != 0 ? value_storage_nontrivial(in_place_type<value_type>, static_cast<U &&>(o._value)) : value_storage_nontrivial())
+ {
+ _status = o._status;
+ }
+ BOOST_OUTCOME_TEMPLATE(class U)
+ BOOST_OUTCOME_TREQUIRES(BOOST_OUTCOME_TPRED(enable_converting_constructor<U>))
+ constexpr explicit value_storage_nontrivial(value_storage_trivial<U> &&o) noexcept(std::is_nothrow_constructible<value_type, U>::value)
+ : value_storage_nontrivial((o._status & status_have_value) != 0 ? value_storage_nontrivial(in_place_type<value_type>, static_cast<U &&>(o._value)) : value_storage_nontrivial())
+ {
+ _status = o._status;
+ }
+ ~value_storage_nontrivial() noexcept(std::is_nothrow_destructible<T>::value)
+ {
+ if(this->_status & status_have_value)
+ {
+ this->_value.~value_type(); // NOLINT
+ this->_status &= ~status_have_value;
+ }
+ }
+ constexpr void swap(value_storage_nontrivial &o) noexcept(detail::is_nothrow_swappable<value_type>::value &&std::is_nothrow_move_constructible<value_type>::value)
+ {
+ using std::swap;
+ if((_status & status_have_value) == 0 && (o._status & status_have_value) == 0)
+ {
+ swap(_status, o._status);
+ return;
+ }
+ if((_status & status_have_value) != 0 && (o._status & status_have_value) != 0)
+ {
+ swap(_value, o._value); // NOLINT
+ swap(_status, o._status);
+ return;
+ }
+ // One must be empty and the other non-empty, so use move construction
+ if((_status & status_have_value) != 0)
+ {
+ // Move construct me into other
+ new(&o._value) value_type(static_cast<value_type &&>(_value)); // NOLINT
+ this->_value.~value_type(); // NOLINT
+ swap(_status, o._status);
+ }
+ else
+ {
+ // Move construct other into me
+ new(&_value) value_type(static_cast<value_type &&>(o._value)); // NOLINT
+ o._value.~value_type(); // NOLINT
+ swap(_status, o._status);
+ }
+ }
+ };
+ template <class Base> struct value_storage_delete_copy_constructor : Base // NOLINT
+ {
+ using Base::Base;
+ using value_type = typename Base::value_type;
+ value_storage_delete_copy_constructor() = default;
+ value_storage_delete_copy_constructor(const value_storage_delete_copy_constructor &) = delete;
+ value_storage_delete_copy_constructor(value_storage_delete_copy_constructor &&) = default; // NOLINT
+ };
+ template <class Base> struct value_storage_delete_copy_assignment : Base // NOLINT
+ {
+ using Base::Base;
+ using value_type = typename Base::value_type;
+ value_storage_delete_copy_assignment() = default;
+ value_storage_delete_copy_assignment(const value_storage_delete_copy_assignment &) = default;
+ value_storage_delete_copy_assignment(value_storage_delete_copy_assignment &&) = default; // NOLINT
+ value_storage_delete_copy_assignment &operator=(const value_storage_delete_copy_assignment &o) = delete;
+ value_storage_delete_copy_assignment &operator=(value_storage_delete_copy_assignment &&o) = default; // NOLINT
+ };
+ template <class Base> struct value_storage_delete_move_assignment : Base // NOLINT
+ {
+ using Base::Base;
+ using value_type = typename Base::value_type;
+ value_storage_delete_move_assignment() = default;
+ value_storage_delete_move_assignment(const value_storage_delete_move_assignment &) = default;
+ value_storage_delete_move_assignment(value_storage_delete_move_assignment &&) = default; // NOLINT
+ value_storage_delete_move_assignment &operator=(const value_storage_delete_move_assignment &o) = default;
+ value_storage_delete_move_assignment &operator=(value_storage_delete_move_assignment &&o) = delete;
+ };
+ template <class Base> struct value_storage_delete_move_constructor : Base // NOLINT
+ {
+ using Base::Base;
+ using value_type = typename Base::value_type;
+ value_storage_delete_move_constructor() = default;
+ value_storage_delete_move_constructor(const value_storage_delete_move_constructor &) = default;
+ value_storage_delete_move_constructor(value_storage_delete_move_constructor &&) = delete;
+ };
+ template <class Base> struct value_storage_nontrivial_move_assignment : Base // NOLINT
+ {
+ using Base::Base;
+ using value_type = typename Base::value_type;
+ value_storage_nontrivial_move_assignment() = default;
+ value_storage_nontrivial_move_assignment(const value_storage_nontrivial_move_assignment &) = default;
+ value_storage_nontrivial_move_assignment(value_storage_nontrivial_move_assignment &&) = default; // NOLINT
+ value_storage_nontrivial_move_assignment &operator=(const value_storage_nontrivial_move_assignment &o) = default;
+ value_storage_nontrivial_move_assignment &operator=(value_storage_nontrivial_move_assignment &&o) noexcept(std::is_nothrow_move_assignable<value_type>::value) // NOLINT
+ {
+ if((this->_status & status_have_value) != 0 && (o._status & status_have_value) != 0)
+ {
+ this->_value = static_cast<value_type &&>(o._value); // NOLINT
+ }
+ else if((this->_status & status_have_value) != 0 && (o._status & status_have_value) == 0)
+ {
+ this->_value.~value_type(); // NOLINT
+ }
+ else if((this->_status & status_have_value) == 0 && (o._status & status_have_value) != 0)
+ {
+ new(&this->_value) value_type(static_cast<value_type &&>(o._value)); // NOLINT
+ }
+ this->_status = o._status;
+ return *this;
+ }
+ };
+ template <class Base> struct value_storage_nontrivial_copy_assignment : Base // NOLINT
+ {
+ using Base::Base;
+ using value_type = typename Base::value_type;
+ value_storage_nontrivial_copy_assignment() = default;
+ value_storage_nontrivial_copy_assignment(const value_storage_nontrivial_copy_assignment &) = default;
+ value_storage_nontrivial_copy_assignment(value_storage_nontrivial_copy_assignment &&) = default; // NOLINT
+ value_storage_nontrivial_copy_assignment &operator=(value_storage_nontrivial_copy_assignment &&o) = default; // NOLINT
+ value_storage_nontrivial_copy_assignment &operator=(const value_storage_nontrivial_copy_assignment &o) noexcept(std::is_nothrow_copy_assignable<value_type>::value)
+ {
+ if((this->_status & status_have_value) != 0 && (o._status & status_have_value) != 0)
+ {
+ this->_value = o._value; // NOLINT
+ }
+ else if((this->_status & status_have_value) != 0 && (o._status & status_have_value) == 0)
+ {
+ this->_value.~value_type(); // NOLINT
+ }
+ else if((this->_status & status_have_value) == 0 && (o._status & status_have_value) != 0)
+ {
+ new(&this->_value) value_type(o._value); // NOLINT
+ }
+ this->_status = o._status;
+ return *this;
+ }
+ };
+
+ // We don't actually need all of std::is_trivial<>, std::is_trivially_copyable<> is sufficient
+ template <class T> using value_storage_select_trivality = std::conditional_t<std::is_trivially_copyable<devoid<T>>::value, value_storage_trivial<T>, value_storage_nontrivial<T>>;
+ template <class T> using value_storage_select_move_constructor = std::conditional_t<std::is_move_constructible<devoid<T>>::value, value_storage_select_trivality<T>, value_storage_delete_move_constructor<value_storage_select_trivality<T>>>;
+ template <class T> using value_storage_select_copy_constructor = std::conditional_t<std::is_copy_constructible<devoid<T>>::value, value_storage_select_move_constructor<T>, value_storage_delete_copy_constructor<value_storage_select_move_constructor<T>>>;
+ template <class T>
+ using value_storage_select_move_assignment = std::conditional_t<std::is_trivially_move_assignable<devoid<T>>::value, value_storage_select_copy_constructor<T>,
+ std::conditional_t<std::is_move_assignable<devoid<T>>::value, value_storage_nontrivial_move_assignment<value_storage_select_copy_constructor<T>>, value_storage_delete_copy_assignment<value_storage_select_copy_constructor<T>>>>;
+ template <class T>
+ using value_storage_select_copy_assignment = std::conditional_t<std::is_trivially_copy_assignable<devoid<T>>::value, value_storage_select_move_assignment<T>,
+ std::conditional_t<std::is_copy_assignable<devoid<T>>::value, value_storage_nontrivial_copy_assignment<value_storage_select_move_assignment<T>>, value_storage_delete_copy_assignment<value_storage_select_move_assignment<T>>>>;
+ template <class T> using value_storage_select_impl = value_storage_select_copy_assignment<T>;
+#ifndef NDEBUG
+ // Check is trivial in all ways except default constructibility
+ // static_assert(std::is_trivial<value_storage_select_impl<int>>::value, "value_storage_select_impl<int> is not trivial!");
+ // static_assert(std::is_trivially_default_constructible<value_storage_select_impl<int>>::value, "value_storage_select_impl<int> is not trivially default constructible!");
+ static_assert(std::is_trivially_copyable<value_storage_select_impl<int>>::value, "value_storage_select_impl<int> is not trivially copyable!");
+ static_assert(std::is_trivially_assignable<value_storage_select_impl<int>, value_storage_select_impl<int>>::value, "value_storage_select_impl<int> is not trivially assignable!");
+ static_assert(std::is_trivially_destructible<value_storage_select_impl<int>>::value, "value_storage_select_impl<int> is not trivially destructible!");
+ static_assert(std::is_trivially_copy_constructible<value_storage_select_impl<int>>::value, "value_storage_select_impl<int> is not trivially copy constructible!");
+ static_assert(std::is_trivially_move_constructible<value_storage_select_impl<int>>::value, "value_storage_select_impl<int> is not trivially move constructible!");
+ static_assert(std::is_trivially_copy_assignable<value_storage_select_impl<int>>::value, "value_storage_select_impl<int> is not trivially copy assignable!");
+ static_assert(std::is_trivially_move_assignable<value_storage_select_impl<int>>::value, "value_storage_select_impl<int> is not trivially move assignable!");
+ // Also check is standard layout
+ static_assert(std::is_standard_layout<value_storage_select_impl<int>>::value, "value_storage_select_impl<int> is not a standard layout type!");
+#endif
+} // namespace detail
+
+BOOST_OUTCOME_V2_NAMESPACE_END
+
+#endif