diff options
| author | Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> | 2020-11-09 09:20:54 +0200 |
|---|---|---|
| committer | Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> | 2020-11-15 17:20:52 +0200 |
| commit | 702a1d84529c2fcede6b79d70b0a76a71d76cbbd (patch) | |
| tree | ee8a21117fbed26fe2a1f0e3617c006114c66b47 /lib/utils | |
| parent | 13118ae173b5adeff9bc812d16e52163235468a6 (diff) | |
utils: add Result / Option types
Add some Rust-style Result/Option types, based on TartanLlama's
expected, optional classes.
There's std::optional of course, but we can't depend on C++17 yet.
Diffstat (limited to 'lib/utils')
| -rw-r--r-- | lib/utils/Makefile.am | 14 | ||||
| -rw-r--r-- | lib/utils/expected.hpp | 2326 | ||||
| -rw-r--r-- | lib/utils/mu-error.hh | 4 | ||||
| -rw-r--r-- | lib/utils/mu-option.hh | 26 | ||||
| -rw-r--r-- | lib/utils/mu-result.hh | 39 | ||||
| -rw-r--r-- | lib/utils/optional.hpp | 2062 |
6 files changed, 4465 insertions, 6 deletions
diff --git a/lib/utils/Makefile.am b/lib/utils/Makefile.am index 7bfb699..d8cc0d5 100644 --- a/lib/utils/Makefile.am +++ b/lib/utils/Makefile.am @@ -45,17 +45,24 @@ AM_LDFLAGS= \ noinst_LTLIBRARIES= \ libmu-utils.la + +third_party= \ + optional.hpp \ + expected.hpp + libmu_utils_la_SOURCES= \ mu-async-queue.hh \ + mu-command-parser.cc \ + mu-command-parser.hh \ mu-date.c \ mu-date.h \ mu-error.hh \ mu-logger.cc \ mu-logger.hh \ - mu-command-parser.cc \ - mu-command-parser.hh \ + mu-option.hh \ mu-readline.cc \ mu-readline.hh \ + mu-result.hh \ mu-sexp.cc \ mu-sexp.hh \ mu-str.c \ @@ -63,7 +70,8 @@ libmu_utils_la_SOURCES= \ mu-util.c \ mu-util.h \ mu-utils.cc \ - mu-utils.hh + mu-utils.hh \ + ${third_party} libmu_utils_la_LIBADD= \ $(GLIB_LIBS) \ diff --git a/lib/utils/expected.hpp b/lib/utils/expected.hpp new file mode 100644 index 0000000..31a5193 --- /dev/null +++ b/lib/utils/expected.hpp @@ -0,0 +1,2326 @@ +/// +// expected - An implementation of std::expected with extensions +// Written in 2017 by Simon Brand (simonrbrand@gmail.com, @TartanLlama) +// +// Documentation available at http://tl.tartanllama.xyz/ +// +// To the extent possible under law, the author(s) have dedicated all +// copyright and related and neighboring rights to this software to the +// public domain worldwide. This software is distributed without any warranty. +// +// You should have received a copy of the CC0 Public Domain Dedication +// along with this software. If not, see +// <http://creativecommons.org/publicdomain/zero/1.0/>. +/// + +#ifndef TL_EXPECTED_HPP +#define TL_EXPECTED_HPP + +#define TL_EXPECTED_VERSION_MAJOR 1 +#define TL_EXPECTED_VERSION_MINOR 0 +#define TL_EXPECTED_VERSION_PATCH 1 + +#include <exception> +#include <functional> +#include <type_traits> +#include <utility> + +#if defined(__EXCEPTIONS) || defined(_CPPUNWIND) +#define TL_EXPECTED_EXCEPTIONS_ENABLED +#endif + +#if (defined(_MSC_VER) && _MSC_VER == 1900) +#define TL_EXPECTED_MSVC2015 +#define TL_EXPECTED_MSVC2015_CONSTEXPR +#else +#define TL_EXPECTED_MSVC2015_CONSTEXPR constexpr +#endif + +#if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 && \ + !defined(__clang__)) +#define TL_EXPECTED_GCC49 +#endif + +#if (defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 4 && \ + !defined(__clang__)) +#define TL_EXPECTED_GCC54 +#endif + +#if (defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 5 && \ + !defined(__clang__)) +#define TL_EXPECTED_GCC55 +#endif + +#if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 && \ + !defined(__clang__)) +// GCC < 5 doesn't support overloading on const&& for member functions + +#define TL_EXPECTED_NO_CONSTRR +// GCC < 5 doesn't support some standard C++11 type traits +#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \ + std::has_trivial_copy_constructor<T> +#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \ + std::has_trivial_copy_assign<T> + +// This one will be different for GCC 5.7 if it's ever supported +#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \ + std::is_trivially_destructible<T> + +// GCC 5 < v < 8 has a bug in is_trivially_copy_constructible which breaks std::vector +// for non-copyable types +#elif (defined(__GNUC__) && __GNUC__ < 8 && \ + !defined(__clang__)) +#ifndef TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX +#define TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX +namespace tl { + namespace detail { + template<class T> + struct is_trivially_copy_constructible : std::is_trivially_copy_constructible<T>{}; +#ifdef _GLIBCXX_VECTOR + template<class T, class A> + struct is_trivially_copy_constructible<std::vector<T,A>> + : std::false_type{}; +#endif + } +} +#endif + +#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \ + tl::detail::is_trivially_copy_constructible<T> +#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \ + std::is_trivially_copy_assignable<T> +#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) std::is_trivially_destructible<T> +#else +#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \ + std::is_trivially_copy_constructible<T> +#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \ + std::is_trivially_copy_assignable<T> +#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \ + std::is_trivially_destructible<T> +#endif + +#if __cplusplus > 201103L +#define TL_EXPECTED_CXX14 +#endif + +#ifdef TL_EXPECTED_GCC49 +#define TL_EXPECTED_GCC49_CONSTEXPR +#else +#define TL_EXPECTED_GCC49_CONSTEXPR constexpr +#endif + +#if (__cplusplus == 201103L || defined(TL_EXPECTED_MSVC2015) || \ + defined(TL_EXPECTED_GCC49)) +#define TL_EXPECTED_11_CONSTEXPR +#else +#define TL_EXPECTED_11_CONSTEXPR constexpr +#endif + +namespace tl { +template <class T, class E> class expected; + +#ifndef TL_MONOSTATE_INPLACE_MUTEX +#define TL_MONOSTATE_INPLACE_MUTEX +class monostate {}; + +struct in_place_t { + explicit in_place_t() = default; +}; +static constexpr in_place_t in_place{}; +#endif + +template <class E> class unexpected { +public: + static_assert(!std::is_same<E, void>::value, "E must not be void"); + + unexpected() = delete; + constexpr explicit unexpected(const E &e) : m_val(e) {} + + constexpr explicit unexpected(E &&e) : m_val(std::move(e)) {} + + constexpr const E &value() const & { return m_val; } + TL_EXPECTED_11_CONSTEXPR E &value() & { return m_val; } + TL_EXPECTED_11_CONSTEXPR E &&value() && { return std::move(m_val); } + constexpr const E &&value() const && { return std::move(m_val); } + +private: + E m_val; +}; + +template <class E> +constexpr bool operator==(const unexpected<E> &lhs, const unexpected<E> &rhs) { + return lhs.value() == rhs.value(); +} +template <class E> +constexpr bool operator!=(const unexpected<E> &lhs, const unexpected<E> &rhs) { + return lhs.value() != rhs.value(); +} +template <class E> +constexpr bool operator<(const unexpected<E> &lhs, const unexpected<E> &rhs) { + return lhs.value() < rhs.value(); +} +template <class E> +constexpr bool operator<=(const unexpected<E> &lhs, const unexpected<E> &rhs) { + return lhs.value() <= rhs.value(); +} +template <class E> +constexpr bool operator>(const unexpected<E> &lhs, const unexpected<E> &rhs) { + return lhs.value() > rhs.value(); +} +template <class E> +constexpr bool operator>=(const unexpected<E> &lhs, const unexpected<E> &rhs) { + return lhs.value() >= rhs.value(); +} + +template <class E> +unexpected<typename std::decay<E>::type> make_unexpected(E &&e) { + return unexpected<typename std::decay<E>::type>(std::forward<E>(e)); +} + +struct unexpect_t { + unexpect_t() = default; +}; +static constexpr unexpect_t unexpect{}; + +namespace detail { +template<typename E> +[[noreturn]] TL_EXPECTED_11_CONSTEXPR void throw_exception(E &&e) { +#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED + throw std::forward<E>(e); +#else + #ifdef _MSC_VER + __assume(0); + #else + __builtin_unreachable(); + #endif +#endif +} + +#ifndef TL_TRAITS_MUTEX +#define TL_TRAITS_MUTEX +// C++14-style aliases for brevity +template <class T> using remove_const_t = typename std::remove_const<T>::type; +template <class T> +using remove_reference_t = typename std::remove_reference<T>::type; +template <class T> using decay_t = typename std::decay<T>::type; +template <bool E, class T = void> +using enable_if_t = typename std::enable_if<E, T>::type; +template <bool B, class T, class F> +using conditional_t = typename std::conditional<B, T, F>::type; + +// std::conjunction from C++17 +template <class...> struct conjunction : std::true_type {}; +template <class B> struct conjunction<B> : B {}; +template <class B, class... Bs> +struct conjunction<B, Bs...> + : std::conditional<bool(B::value), conjunction<Bs...>, B>::type {}; + +#if defined(_LIBCPP_VERSION) && __cplusplus == 201103L +#define TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND +#endif + +// In C++11 mode, there's an issue in libc++'s std::mem_fn +// which results in a hard-error when using it in a noexcept expression +// in some cases. This is a check to workaround the common failing case. +#ifdef TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND +template <class T> struct is_pointer_to_non_const_member_func : std::false_type {}; +template <class T, class Ret, class... Args> +struct is_pointer_to_non_const_member_func<Ret(T::*) (Args...)> : std::true_type {}; +template <class T, class Ret, class... Args> +struct is_pointer_to_non_const_member_func<Ret(T::*) (Args...)&> : std::true_type {}; +template <class T, class Ret, class... Args> +struct is_pointer_to_non_const_member_func<Ret(T::*) (Args...) &&> : std::true_type {}; +template <class T, class Ret, class... Args> +struct is_pointer_to_non_const_member_func<Ret(T::*) (Args...) volatile> : std::true_type {}; +template <class T, class Ret, class... Args> +struct is_pointer_to_non_const_member_func<Ret(T::*) (Args...) volatile &> : std::true_type {}; +template <class T, class Ret, class... Args> +struct is_pointer_to_non_const_member_func<Ret(T::*) (Args...) volatile &&> : std::true_type {}; + +template <class T> struct is_const_or_const_ref : std::false_type {}; +template <class T> struct is_const_or_const_ref<T const&> : std::true_type {}; +template <class T> struct is_const_or_const_ref<T const> : std::true_type {}; +#endif + +// std::invoke from C++17 +// https://stackoverflow.com/questions/38288042/c11-14-invoke-workaround +template <typename Fn, typename... Args, +#ifdef TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND + typename = enable_if_t<!(is_pointer_to_non_const_member_func<Fn>::value + && is_const_or_const_ref<Args...>::value)>, +#endif + typename = enable_if_t<std::is_member_pointer<decay_t<Fn>>::value>, + int = 0> + constexpr auto invoke(Fn && f, Args && ... args) noexcept( + noexcept(std::mem_fn(f)(std::forward<Args>(args)...))) + -> decltype(std::mem_fn(f)(std::forward<Args>(args)...)) { + return std::mem_fn(f)(std::forward<Args>(args)...); +} + +template <typename Fn, typename... Args, + typename = enable_if_t<!std::is_member_pointer<decay_t<Fn>>::value>> + constexpr auto invoke(Fn && f, Args && ... args) noexcept( + noexcept(std::forward<Fn>(f)(std::forward<Args>(args)...))) + -> decltype(std::forward<Fn>(f)(std::forward<Args>(args)...)) { + return std::forward<Fn>(f)(std::forward<Args>(args)...); +} + +// std::invoke_result from C++17 +template <class F, class, class... Us> struct invoke_result_impl; + +template <class F, class... Us> +struct invoke_result_impl< + F, decltype(detail::invoke(std::declval<F>(), std::declval<Us>()...), void()), + Us...> { + using type = decltype(detail::invoke(std::declval<F>(), std::declval<Us>()...)); +}; + +template <class F, class... Us> +using invoke_result = invoke_result_impl<F, void, Us...>; + +template <class F, class... Us> +using invoke_result_t = typename invoke_result<F, Us...>::type; + +#if defined(_MSC_VER) && _MSC_VER <= 1900 +// TODO make a version which works with MSVC 2015 +template <class T, class U = T> struct is_swappable : std::true_type {}; + +template <class T, class U = T> struct is_nothrow_swappable : std::true_type {}; +#else +// https://stackoverflow.com/questions/26744589/what-is-a-proper-way-to-implement-is-swappable-to-test-for-the-swappable-concept +namespace swap_adl_tests { + // if swap ADL finds this then it would call std::swap otherwise (same + // signature) + struct tag {}; + + template <class T> tag swap(T&, T&); + template <class T, std::size_t N> tag swap(T(&a)[N], T(&b)[N]); + + // helper functions to test if an unqualified swap is possible, and if it + // becomes std::swap + template <class, class> std::false_type can_swap(...) noexcept(false); + template <class T, class U, + class = decltype(swap(std::declval<T&>(), std::declval<U&>()))> + std::true_type can_swap(int) noexcept(noexcept(swap(std::declval<T&>(), + std::declval<U&>()))); + + template <class, class> std::false_type uses_std(...); + template <class T, class U> + std::is_same<decltype(swap(std::declval<T&>(), std::declval<U&>())), tag> + uses_std(int); + + template <class T> + struct is_std_swap_noexcept + : std::integral_constant<bool, + std::is_nothrow_move_constructible<T>::value&& + std::is_nothrow_move_assignable<T>::value> {}; + + template <class T, std::size_t N> + struct is_std_swap_noexcept<T[N]> : is_std_swap_noexcept<T> {}; + + template <class T, class U> + struct is_adl_swap_noexcept + : std::integral_constant<bool, noexcept(can_swap<T, U>(0))> {}; +} // namespace swap_adl_tests + +template <class T, class U = T> +struct is_swappable + : std::integral_constant< + bool, + decltype(detail::swap_adl_tests::can_swap<T, U>(0))::value && + (!decltype(detail::swap_adl_tests::uses_std<T, U>(0))::value || + (std::is_move_assignable<T>::value && + std::is_move_constructible<T>::value))> {}; + +template <class T, std::size_t N> +struct is_swappable<T[N], T[N]> + : std::integral_constant< + bool, + decltype(detail::swap_adl_tests::can_swap<T[N], T[N]>(0))::value && + (!decltype( + detail::swap_adl_tests::uses_std<T[N], T[N]>(0))::value || + is_swappable<T, T>::value)> {}; + +template <class T, class U = T> +struct is_nothrow_swappable + : std::integral_constant< + bool, + is_swappable<T, U>::value && + ((decltype(detail::swap_adl_tests::uses_std<T, U>(0))::value + && detail::swap_adl_tests::is_std_swap_noexcept<T>::value) || + (!decltype(detail::swap_adl_tests::uses_std<T, U>(0))::value && + detail::swap_adl_tests::is_adl_swap_noexcept<T, + U>::value))> { +}; +#endif +#endif + +// Trait for checking if a type is a tl::expected +template <class T> struct is_expected_impl : std::false_type {}; +template <class T, class E> +struct is_expected_impl<expected<T, E>> : std::true_type {}; +template <class T> using is_expected = is_expected_impl<decay_t<T>>; + +template <class T, class E, class U> +using expected_enable_forward_value = detail::enable_if_t< + std::is_constructible<T, U &&>::value && + !std::is_same<detail::decay_t<U>, in_place_t>::value && + !std::is_same<expected<T, E>, detail::decay_t<U>>::value && + !std::is_same<unexpected<E>, detail::decay_t<U>>::value>; + +template <class T, class E, class U, class G, class UR, class GR> +using expected_enable_from_other = detail::enable_if_t< + std::is_constructible<T, UR>::value && + std::is_constructible<E, GR>::value && + !std::is_constructible<T, expected<U, G> &>::value && + !std::is_constructible<T, expected<U, G> &&>::value && + !std::is_constructible<T, const expected<U, G> &>::value && + !std::is_constructible<T, const expected<U, G> &&>::value && + !std::is_convertible<expected<U, G> &, T>::value && + !std::is_convertible<expected<U, G> &&, T>::value && + !std::is_convertible<const expected<U, G> &, T>::value && + !std::is_convertible<const expected<U, G> &&, T>::value>; + +template <class T, class U> +using is_void_or = conditional_t<std::is_void<T>::value, std::true_type, U>; + +template <class T> +using is_copy_constructible_or_void = + is_void_or<T, std::is_copy_constructible<T>>; + +template <class T> +using is_move_constructible_or_void = + is_void_or<T, std::is_move_constructible<T>>; + +template <class T> +using is_copy_assignable_or_void = + is_void_or<T, std::is_copy_assignable<T>>; + + +template <class T> +using is_move_assignable_or_void = + is_void_or<T, std::is_move_assignable<T>>; + + +} // namespace detail + +namespace detail { +struct no_init_t {}; +static constexpr no_init_t no_init{}; + +// Implements the storage of the values, and ensures that the destructor is +// trivial if it can be. +// +// This specialization is for where neither `T` or `E` is trivially +// destructible, so the destructors must be called on destruction of the +// `expected` +template <class T, class E, bool = std::is_trivially_destructible<T>::value, + bool = std::is_trivially_destructible<E>::value> +struct expected_storage_base { + constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {} + constexpr expected_storage_base(no_init_t) : m_no_init(), m_has_val(false) {} + + template <class... Args, + detail::enable_if_t<std::is_constructible<T, Args &&...>::value> * = + nullptr> + constexpr expected_storage_base(in_place_t, Args &&... args) + : m_val(std::forward<Args>(args)...), m_has_val(true) {} + + template <class U, class... Args, + detail::enable_if_t<std::is_constructible< + T, std::initializer_list<U> &, Args &&...>::value> * = nullptr> + constexpr expected_storage_base(in_place_t, std::initializer_list<U> il, + Args &&... args) + : m_val(il, std::forward<Args>(args)...), m_has_val(true) {} + template <class... Args, + detail::enable_if_t<std::is_constructible<E, Args &&...>::value> * = + nullptr> + constexpr explicit expected_storage_base(unexpect_t, Args &&... args) + : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {} + + template <class U, class... Args, + detail::enable_if_t<std::is_constructible< + E, std::initializer_list<U> &, Args &&...>::value> * = nullptr> + constexpr explicit expected_storage_base(unexpect_t, + std::initializer_list<U> il, + Args &&... args) + : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {} + + ~expected_storage_base() { + if (m_has_val) { + m_val.~T(); + } else { + m_unexpect.~unexpected<E>(); + } + } + union { + T m_val; + unexpected<E> m_unexpect; + char m_no_init; + }; + bool m_has_val; +}; + +// This specialization is for when both `T` and `E` are trivially-destructible, +// so the destructor of the `expected` can be trivial. +template <class T, class E> struct expected_storage_base<T, E, true, true> { + constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {} + constexpr expected_storage_base(no_init_t) : m_no_init(), m_has_val(false) {} + + template <class... Args, + detail::enable_if_t<std::is_constructible<T, Args &&...>::value> * = + nullptr> + constexpr expected_storage_base(in_place_t, Args &&... args) + : m_val(std::forward<Args>(args)...), m_has_val(true) {} + + template <class U, class... Args, + detail::enable_if_t<std::is_constructible< + T, std::initializer_list<U> &, Args &&...>::value> * = nullptr> + constexpr expected_storage_base(in_place_t, std::initializer_list<U> il, + Args &&... args) + : m_val(il, std::forward<Args>(args)...), m_has_val(true) {} + template <class... Args, + detail::enable_if_t<std::is_constructible<E, Args &&...>::value> * = + nullptr> + constexpr explicit expected_storage_base(unexpect_t, Args &&... args) + : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {} + + template <class U, class... Args, + detail::enable_if_t<std::is_constructible< + E, std::initializer_list<U> &, Args &&...>::value> * = nullptr> + constexpr explicit expected_storage_base(unexpect_t, + std::initializer_list<U> il, + Args &&... args) + : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {} + + ~expected_storage_base() = default; + union { + T m_val; + unexpected<E> m_unexpect; + char m_no_init; + }; + bool m_has_val; +}; + +// T is trivial, E is not. +template <class T, class E> struct expected_storage_base<T, E, true, false> { + constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {} + TL_EXPECTED_MSVC2015_CONSTEXPR expected_storage_base(no_init_t) + : m_no_init(), m_has_val(false) {} + + template <class... Args, + detail::enable_if_t<std::is_constructible<T, Args &&...>::value> * = + nullptr> + constexpr expected_storage_base(in_place_t, Args &&... args) + : m_val(std::forward<Args>(args)...), m_has_val(true) {} + + template <class U, class... Args, + detail::enable_if_t<std::is_constructible< + T, std::initializer_list<U> &, Args &&...>::value> * = nullptr> + constexpr expected_storage_base(in_place_t, std::initializer_list<U> il, + Args &&... args) + : m_val(il, std::forward<Args>(args)...), m_has_val(true) {} + template <class... Args, + detail::enable_if_t<std::is_constructible<E, Args &&...>::value> * = + nullptr> + constexpr explicit expected_storage_base(unexpect_t, Args &&... args) + : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {} + + template <class U, class... Args, + detail::enable_if_t<std::is_constructible< + E, std::initializer_list<U> &, Args &&...>::value> * = nullptr> + constexpr explicit expected_storage_base(unexpect_t, + std::initializer_list<U> il, + Args &&... args) + : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {} + + ~expected_storage_base() { + if (!m_has_val) { + m_unexpect.~unexpected<E>(); + } + } + + union { + T m_val; + unexpected<E> m_unexpect; + char m_no_init; + }; + bool m_has_val; +}; + +// E is trivial, T is not. +template <class T, class E> struct expected_storage_base<T, E, false, true> { + constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {} + constexpr expected_storage_base(no_init_t) : m_no_init(), m_has_val(false) {} + + template <class... Args, + detail::enable_if_t<std::is_constructible<T, Args &&...>::value> * = + nullptr> + constexpr expected_storage_base(in_place_t, Args &&... args) + : m_val(std::forward<Args>(args)...), m_has_val(true) {} + + template <class U, class... Args, + detail::enable_if_t<std::is_constructible< + T, std::initializer_list<U> &, Args &&...>::value> * = nullptr> + constexpr expected_storage_base(in_place_t, std::initializer_list<U> il, + Args &&... args) + : m_val(il, std::forward<Args>(args)...), m_has_val(true) {} + template <class... Args, + detail::enable_if_t<std::is_constructible<E, Args &&...>::value> * = + nullptr> + constexpr explicit expected_storage_base(unexpect_t, Args &&... args) + : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {} + + template <class U, class... Args, + detail::enable_if_t<std::is_constructible< + E, std::initializer_list<U> &, Args &&...>::value> * = nullptr> + constexpr explicit expected_storage_base(unexpect_t, + std::initializer_list<U> il, + Args &&... args) + : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {} + + ~expected_storage_base() { + if (m_has_val) { + m_val.~T(); + } + } + union { + T m_val; + unexpected<E> m_unexpect; + char m_no_init; + }; + bool m_has_val; +}; + +// `T` is `void`, `E` is trivially-destructible +template <class E> struct expected_storage_base<void, E, false, true> { + TL_EXPECTED_MSVC2015_CONSTEXPR expected_storage_base() : m_has_val(true) {} + constexpr expected_storage_base(no_init_t) : m_val(), m_has_val(false) {} + + constexpr expected_storage_base(in_place_t) : m_has_val(true) {} + + template <class... Args, + detail::enable_if_t<std::is_constructible<E, Args &&...>::value> * = + nullptr> + constexpr explicit expected_storage_base(unexpect_t, Args &&... args) + : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {} + + template <class U, class... Args, + detail::enable_if_t<std::is_constructible< + E, std::initializer_list<U> &, Args &&...>::value> * = nullptr> + constexpr explicit expected_storage_base(unexpect_t, + std::initializer_list<U> il, + Args &&... args) + : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {} + + ~expected_storage_base() = default; + struct dummy {}; + union { + unexpected<E> m_unexpect; + dummy m_val; + }; + bool m_has_val; +}; + +// `T` is `void`, `E` is not trivially-destructible +template <class E> struct expected_storage_base<void, E, false, false> { + constexpr expected_storage_base() : m_dummy(), m_has_val(true) {} + constexpr expected_storage_base(no_init_t) : m_dummy(), m_has_val(false) {} + + constexpr expected_storage_base(in_place_t) : m_dummy(), m_has_val(true) {} + + template <class... Args, + detail::enable_if_t<std::is_constructible<E, Args &&...>::value> * = + nullptr> + constexpr explicit expected_storage_base(unexpect_t, Args &&... args) + : m_unexpect(std::forward<Args>(args)...), m_has_val(false) {} + + template <class U, class... Args, + detail::enable_if_t<std::is_constructible< + E, std::initializer_list<U> &, Args &&...>::value> * = nullptr> + constexpr explicit expected_storage_base(unexpect_t, + std::initializer_list<U> il, + Args &&... args) + : m_unexpect(il, std::forward<Args>(args)...), m_has_val(false) {} + + ~expected_storage_base() { + if (!m_has_val) { + m_unexpect.~unexpected<E>(); + } + } + + union { + unexpected<E> m_unexpect; + char m_dummy; + }; + bool m_has_val; +}; + +// This base class provides some handy member functions which can be used in +// further derived classes +template <class T, class E> +struct expected_operations_base : expected_storage_base<T, E> { + using expected_storage_base<T, E>::expected_storage_base; + + template <class... Args> void construct(Args &&... args) noexcept { + new (std::addressof(this->m_val)) T(std::forward<Args>(args)...); + this->m_has_val = true; + } + + template <class Rhs> void construct_with(Rhs &&rhs) noexcept { + new (std::addressof(this->m_val)) T(std::forward<Rhs>(rhs).get()); + this->m_has_val = true; + } + + template <class... Args> void construct_error(Args &&... args) noexcept { + new (std::addressof(this->m_unexpect)) + unexpected<E>(std::forward<Args>(args)...); + this->m_has_val = false; + } + + #ifdef TL_EXPECTED_EXCEPTIONS_ENABLED + + // These assign overloads ensure that the most efficient assignment + // implementation is used while maintaining the strong exception guarantee. + // The problematic case is where rhs has a value, but *this does not. + // + // This overload handles the case where we can just copy-construct `T` + // directly into place without throwing. + template <class U = T, + detail::enable_if_t<std::is_nothrow_copy_constructible<U>::value> + * = nullptr> + void assign(const expected_operations_base &rhs) noexcept { + if (!this->m_has_val && rhs.m_has_val) { + geterr().~unexpected<E>(); + construct(rhs.get()); + } else { + assign_common(rhs); + } + } + + // This overload handles the case where we can attempt to create a copy of + // `T`, then no-throw move it into place if the copy was successful. + template <class U = T, + detail::enable_if_t<!std::is_nothrow_copy_constructible<U>::value && + std::is_nothrow_move_constructible<U>::value> + * = nullptr> + void assign(const expected_operations_base &rhs) noexcept { + if (!this->m_has_val && rhs.m_has_val) { + T tmp = rhs.get(); + geterr().~unexpected<E>(); + construct(std::move(tmp)); + } else { + assign_common(rhs); + } + } + + // This overload is the worst-case, where we have to move-construct the + // unexpected value into temporary storage, then try to copy the T into place. + // If the construction succeeds, then everything is fine, but if it throws, + // then we move the old unexpected value back into place before rethrowing the + // exception. + template <class U = T, + detail::enable_if_t<!std::is_nothrow_copy_constructible<U>::value && + !std::is_nothrow_move_constructible<U>::value> + * = nullptr> + void assign(const expected_operations_base &rhs) { + if (!this->m_has_val && rhs.m_has_val) { + auto tmp = std::move(geterr()); + geterr().~unexpected<E>(); + +#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED + try { + construct(rhs.get()); + } catch (...) { + geterr() = std::move(tmp); + throw; + } +#else + construct(rhs.get()); +#endif + } else { + assign_common(rhs); + } + } + + // These overloads do the same as above, but for rvalues + template <class U = T, + detail::enable_if_t<std::is_nothrow_move_constructible<U>::value> + * = nullptr> + void assign(expected_operations_base &&rhs) noexcept { + if (!this->m_has_val && rhs.m_has_val) { + geterr().~unexpected<E>(); + construct(std::move(rhs).get()); + } else { + assign_common(std::move(rhs)); + } + } + + template <class U = T, + detail::enable_if_t<!std::is_nothrow_move_constructible<U>::value> + * = nullptr> + void assign(expected_operations_base &&rhs) { + if (!this->m_has_val && rhs.m_has_val) { + auto tmp = std::move(geterr()); + geterr().~unexpected<E>(); +#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED + try { + construct(std::move(rhs).get()); + } catch (...) { + geterr() = std::move(tmp); + throw; + } +#else + construct(std::move(rhs).get()); +#endif + } else { + assign_common(std::move(rhs)); + } + } + + #else + + // If exceptions are disabled then we can just copy-construct + void assign(const expected_operations_base &rhs) noexcept { + if (!this->m_has_val && rhs.m_has_val) { + geterr().~unexpected<E>(); + construct(rhs.get()); + } else { + assign_common(rhs); + } + } + + void assign(expected_operations_base &&rhs) noexcept { + if (!this->m_has_val && rhs.m_has_val) { + geterr().~unexpected<E>(); + construct(std::move(rhs).get()); + } else { + assign_common(rhs); + } + } + + #endif + + // The common part of move/copy assigning + template <class Rhs> void assign_common(Rhs &&rhs) { + if (this->m_has_val) { + if (rhs.m_has_val) { + get() = std::forward<Rhs>(rhs).get(); + } else { + destroy_val(); + construct_error(std::forward<Rhs>(rhs).geterr()); + } + } else { + if (!rhs.m_has_val) { + geterr() = std::forward<Rhs>(rhs).geterr(); + } + } + } + + bool has_value() const { return this->m_has_val; } + + TL_EXPECTED_11_CONSTEXPR T &get() & { return this->m_val; } + constexpr const T &get() const & { return this->m_val; } + TL_EXPECTED_11_CONSTEXPR T &&get() && { return std::move(this->m_val); } +#ifndef TL_EXPECTED_NO_CONSTRR + constexpr const T &&get() const && { return std::move(this->m_val); } +#endif + + TL_EXPECTED_11_CONSTEXPR unexpected<E> &geterr() & { + return this->m_unexpect; + } + constexpr const unexpected<E> &geterr() const & { return this->m_unexpect; } + TL_EXPECTED_11_CONSTEXPR unexpected<E> &&geterr() && { + return std::move(this->m_unexpect); + } +#ifndef TL_EXPECTED_NO_CONSTRR + constexpr const unexpected<E> &&geterr() const && { + return std::move(this->m_unexpect); + } +#endif + + TL_EXPECTED_11_CONSTEXPR void destroy_val() { + get().~T(); + } +}; + +// This base class provides some handy member functions which can be used in +// further derived classes +template <class E> +struct expected_operations_base<void, E> : expected_storage_base<void, E> { + using expected_storage_base<void, E>::expected_storage_base; + + template <class... Args> void construct() noexcept { this->m_has_val = true; } + + // This function doesn't use its argument, but needs it so that code in + // levels above this can work independently of whether T is void + template <class Rhs> void construct_with(Rhs &&) noexcept { + this->m_has_val = true; + } + + template <class... Args> void construct_error(Args &&... args) noexcept { + new (std::addressof(this->m_unexpect)) + unexpected<E>(std::forward<Args>(args)...); + this->m_has_val = false; + } + + template <class Rhs> void assign(Rhs &&rhs) noexcept { + if (!this->m_has_val) { + if (rhs.m_has_val) { + geterr().~unexpected<E>(); + construct(); + } else { + geterr() = std::forward<Rhs>(rhs).geterr(); + } + } else { + if (!rhs.m_has_val) { + construct_error(std::forward<Rhs>(rhs).geterr()); + } + } + } + + bool has_value() const { return this->m_has_val; } + + TL_EXPECTED_11_CONSTEXPR unexpected<E> &geterr() & { + return this->m_unexpect; + } + constexpr const unexpected<E> &geterr() const & { return this->m_unexpect; } + TL_EXPECTED_11_CONSTEXPR unexpected<E> &&geterr() && { + return std::move(this->m_unexpect); + } +#ifndef TL_EXPECTED_NO_CONSTRR + constexpr const unexpected<E> &&geterr() const && { + return std::move(this->m_unexpect); + } +#endif + + TL_EXPECTED_11_CONSTEXPR void destroy_val() { + //no-op + } +}; + +// This class manages conditionally having a trivial copy constructor +// This specialization is for when T and E are trivially copy constructible +template <class T, class E, + bool = is_void_or<T, TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T)>:: + value &&TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(E)::value> +struct expected_copy_base : expected_operations_base<T, E> { + using expected_operations_base<T, E>::expected_operations_base; +}; + +// This specialization is for when T or E are not trivially copy constructible +template <class T, class E> +struct expected_copy_base<T, E, false> : expected_operations_base<T, E> { + using expected_operations_base<T, E>::expected_operations_base; + + expected_copy_base() = default; + expected_copy_base(const expected_copy_base &rhs) + : expected_operations_base<T, E>(no_init) { + if (rhs.has_value()) { + this->construct_with(rhs); + } else { + this->construct_error(rhs.geterr()); + } + } + + expected_copy_base(expected_copy_base &&rhs) = default; + expected_copy_base &operator=(const expected_copy_base &rhs) = default; + expected_copy_base &operator=(expected_copy_base &&rhs) = default; +}; + +// This class manages conditionally having a trivial move constructor +// Unfortunately there's no way to achieve this in GCC < 5 AFAIK, since it +// doesn't implement an analogue to std::is_trivially_move_constructible. We +// have to make do with a non-trivial move constructor even if T is trivially +// move constructible +#ifndef TL_EXPECTED_GCC49 +template <class T, class E, + bool = is_void_or<T, std::is_trivially_move_constructible<T>>::value + &&std::is_trivially_move_constructible<E>::value> +struct expected_move_base : expected_copy_base<T, E> { + using expected_copy_base<T, E>::expected_copy_base; +}; +#else +template <class T, class E, bool = false> struct expected_move_base; +#endif +template <class T, class E> +struct expected_move_base<T, E, false> : expected_copy_base<T, E> { + using expected_copy_base<T, E>::expected_copy_base; + + expected_move_base() = default; + expected_move_base(const expected_move_base &rhs) = default; + + expected_move_base(expected_move_base &&rhs) noexcept( + std::is_nothrow_move_constructible<T>::value) + : expected_copy_base<T, E>(no_init) { + if (rhs.has_value()) { + this->construct_with(std::move(rhs)); + } else { + this->construct_error(std::move(rhs.geterr())); + } + } + expected_move_base &operator=(const expected_move_base &rhs) = default; + expected_move_base &operator=(expected_move_base &&rhs) = default; +}; + +// This class manages conditionally having a trivial copy assignment operator +template <class T, class E, + bool = is_void_or< + T, conjunction<TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T), + TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T), + TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T)>>::value + &&TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(E)::value + &&TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(E)::value + &&TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(E)::value> +struct expected_copy_assign_base : expected_move_base<T, E> { + using expected_move_base<T, E>::expected_move_base; +}; + +template <class T, class E> +struct expected_copy_assign_base<T, E, false> : expected_move_base<T, E> { + using expected_move_base<T, E>::expected_move_base; + + expected_copy_assign_base() = default; + expected_copy_assign_base(const expected_copy_assign_base &rhs) = default; + + expected_copy_assign_base(expected_copy_assign_base &&rhs) = default; + expected_copy_assign_base &operator=(const expected_copy_assign_base &rhs) { + this->assign(rhs); + return *this; + } + expected_copy_assign_base & + operator=(expected_copy_assign_base &&rhs) = default; +}; + +// This class manages conditionally having a trivial move assignment operator +// Unfortunately there's no way to achieve this in GCC < 5 AFAIK, since it +// doesn't implement an analogue to std::is_trivially_move_assignable. We have +// to make do with a non-trivial move assignment operator even if T is trivially +// move assignable +#ifndef TL_EXPECTED_GCC49 +template <class T, class E, + bool = + is_void_or<T, conjunction<std::is_trivially_destructible<T>, + std::is_trivially_move_constructible<T>, + std::is_trivially_move_assignable<T>>>:: + value &&std::is_trivially_destructible<E>::value + &&std::is_trivially_move_constructible<E>::value + &&std::is_trivially_move_assignable<E>::value> +struct expected_move_assign_base : expected_copy_assign_base<T, E> { + using expected_copy_assign_base<T, E>::expected_copy_assign_base; +}; +#else +template <class T, class E, bool = false> struct expected_move_assign_base; +#endif + +template <class T, class E> +struct expected_move_assign_base<T, E, false> + : expected_copy_assign_base<T, E> { + using expected_copy_assign_base<T, E>::expected_copy_assign_base; + + expected_move_assign_base() = default; + expected_move_assign_base(const expected_move_assign_base &rhs) = default; + + expected_move_assign_base(expected_move_assign_base &&rhs) = default; + + expected_move_assign_base & + operator=(const expected_move_assign_base &rhs) = default; + + expected_move_assign_base & + operator=(expected_move_assign_base &&rhs) noexcept( + std::is_nothrow_move_constructible<T>::value + &&std::is_nothrow_move_assignable<T>::value) { + this->assign(std::move(rhs)); + return *this; + } +}; + +// expected_delete_ctor_base will conditionally delete copy and move +// constructors depending on whether T is copy/move constructible +template <class T, class E, + bool EnableCopy = (is_copy_constructible_or_void<T>::value && + std::is_copy_constructible<E>::value), + bool EnableMove = (is_move_constructible_or_void<T>::value && + std::is_move_constructible<E>::value)> +struct expected_delete_ctor_base { + expected_delete_ctor_base() = default; + expected_delete_ctor_base(const expected_delete_ctor_base &) = default; + expected_delete_ctor_base(expected_delete_ctor_base &&) noexcept = default; + expected_delete_ctor_base & + operator=(const expected_delete_ctor_base &) = default; + expected_delete_ctor_base & + operator=(expected_delete_ctor_base &&) noexcept = default; +}; + +template <class T, class E> +struct expected_delete_ctor_base<T, E, true, false> { + expected_delete_ctor_base() = default; + expected_delete_ctor_base(const expected_delete_ctor_base &) = default; + expected_delete_ctor_base(expected_delete_ctor_base &&) noexcept = delete; + expected_delete_ctor_base & + operator=(const expected_delete_ctor_base &) = default; + expected_delete_ctor_base & + operator=(expected_delete_ctor_base &&) noexcept = default; +}; + +template <class T, class E> +struct expected_delete_ctor_base<T, E, false, true> { + expected_delete_ctor_base() = default; + expected_delete_ctor_base(const expected_delete_ctor_base &) = delete; + expected_delete_ctor_base(expected_delete_ctor_base &&) noexcept = default; + expected_delete_ctor_base & + operator=(const expected_delete_ctor_base &) = default; + expected_delete_ctor_base & + operator=(expected_delete_ctor_base &&) noexcept = default; +}; + +template <class T, class E> +struct expected_delete_ctor_base<T, E, false, false> { + expected_delete_ctor_base() = default; + expected_delete_ctor_base(const expected_delete_ctor_base &) = delete; + expected_delete_ctor_base(expected_delete_ctor_base &&) noexcept = delete; + expected_delete_ctor_base & + operator=(const expected_delete_ctor_base &) = default; + expected_delete_ctor_base & + operator=(expected_delete_ctor_base &&) noexcept = default; +}; + +// expected_delete_assign_base will conditionally delete copy and move +// constructors depending on whether T and E are copy/move constructible + +// assignable +template <class T, class E, + bool EnableCopy = (is_copy_constructible_or_void<T>::value && + std::is_copy_constructible<E>::value && + is_copy_assignable_or_void<T>::value && + std::is_copy_assignable<E>::value), + bool EnableMove = (is_move_constructible_or_void<T>::value && + std::is_move_constructible<E>::value && + is_move_assignable_or_void<T>::value && + std::is_move_assignable<E>::value)> +struct expected_delete_assign_base { + expected_delete_assign_base() = default; + expected_delete_assign_base(const expected_delete_assign_base &) = default; + expected_delete_assign_base(expected_delete_assign_base &&) noexcept = + default; + expected_delete_assign_base & + operator=(const expected_delete_assign_base &) = default; + expected_delete_assign_base & + operator=(expected_delete_assign_base &&) noexcept = default; +}; + +template <class T, class E> +struct expected_delete_assign_base<T, E, true, false> { + expected_delete_assign_base() = default; + expected_delete_assign_base(const expected_delete_assign_base &) = default; + expected_delete_assign_base(expected_delete_assign_base &&) noexcept = + default; + expected_delete_assign_base & + operator=(const expected_delete_assign_base &) = default; + expected_delete_assign_base & + operator=(expected_delete_assign_base &&) noexcept = delete; +}; + +template <class T, class E> +struct expected_delete_assign_base<T, E, false, true> { + expected_delete_assign_base() = default; + expected_delete_assign_base(const expected_delete_assign_base &) = default; + expected_delete_assign_base(expected_delete_assign_base &&) noexcept = + default; + expected_delete_assign_base & + operator=(const expected_delete_assign_base &) = delete; + expected_delete_assign_base & + operator=(expected_delete_assign_base &&) noexcept = default; +}; + +template <class T, class E> +struct expected_delete_assign_base<T, E, false, false> { + expected_delete_assign_base() = default; + expected_delete_assign_base(const expected_delete_assign_base &) = default; + expected_delete_assign_base(expected_delete_assign_base &&) noexcept = + default; + expected_delete_assign_base & + operator=(const expected_delete_assign_base &) = delete; + expected_delete_assign_base & + operator=(expected_delete_assign_base &&) noexcept = delete; +}; + +// This is needed to be able to construct the expected_default_ctor_base which +// follows, while still conditionally deleting the default constructor. +struct default_constructor_tag { + explicit constexpr default_constructor_tag() = default; +}; + +// expected_default_ctor_base will ensure that expected has a deleted default +// consturctor if T is not default constructible. +// This specialization is for when T is default constructible +template <class T, class E, + bool Enable = + std::is_default_constructible<T>::value || std::is_void<T>::value> +struct expected_default_ctor_base { + constexpr expected_default_ctor_base() noexcept = default; + constexpr expected_default_ctor_base( + expected_default_ctor_base const &) noexcept = default; + constexpr expected_default_ctor_base(expected_default_ctor_base &&) noexcept = + default; + expected_default_ctor_base & + operator=(expected_default_ctor_base const &) noexcept = default; + expected_default_ctor_base & + operator=(expected_default_ctor_base &&) noexcept = default; + + constexpr explicit expected_default_ctor_base(default_constructor_tag) {} +}; + +// This specialization is for when T is not default constructible +template <class T, class E> struct expected_default_ctor_base<T, E, false> { + constexpr expected_default_ctor_base() noexcept = delete; + constexpr expected_default_ctor_base( + expected_default_ctor_base const &) noexcept = default; + constexpr expected_default_ctor_base(expected_default_ctor_base &&) noexcept = + default; + expected_default_ctor_base & + operator=(expected_default_ctor_base const &) noexcept = default; + expected_default_ctor_base & + operator=(expected_default_ctor_base &&) noexcept = default; + + constexpr explicit expected_default_ctor_base(default_constructor_tag) {} +}; +} // namespace detail + +template <class E> class bad_expected_access : public std::exception { +public: + explicit bad_expected_access(E e) : m_val(std::move(e)) {} + + virtual const char *what() const noexcept override { + return "Bad expected access"; + } + + const E &error() const & { return m_val; } + E &error() & { return m_val; } + const E &&error() const && { return std::move(m_val); } + E &&error() && { return std::move(m_val); } + +private: + E m_val; +}; + +/// An `expected<T, E>` object is an object that contains the storage for +/// another object and manages the lifetime of this contained object `T`. +/// Alternatively it could contain the storage for another unexpected object +/// `E`. The contained object may not be initialized after the expected object +/// has been initialized, and may not be destroyed before the expected object +/// has been destroyed. The initialization state of the contained object is +/// tracked by the expected object. +template <class T, class E> +class expected : private detail::expected_move_assign_base<T, E>, + private detail::expected_delete_ctor_base<T, E>, + private detail::expected_delete_assign_base<T, E>, + private detail::expected_default_ctor_base<T, E> { + static_assert(!std::is_reference<T>::value, "T must not be a reference"); + static_assert(!std::is_same<T, std::remove_cv<in_place_t>>::value, + "T must not be in_place_t"); + static_assert(!std::is_same<T, std::remove_cv<unexpect_t>>::value, + "T must not be unexpect_t"); + static_assert(!std::is_same<T, std::remove_cv<unexpected<E>>>::value, + "T must not be unexpected<E>"); + static_assert(!std::is_reference<E>::value, "E must not be a reference"); + + T *valptr() { return std::addressof(this->m_val); } + const T *valptr() const { return std::addressof(this->m_val); } + unexpected<E> *errptr() { return std::addressof(this->m_unexpect); } + const unexpected<E> *errptr() const { return std::addressof(this->m_unexpect); } + + template <class U = T, + detail::enable_if_t<!std::is_void<U>::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR U &val() { + return this->m_val; + } + TL_EXPECTED_11_CONSTEXPR unexpected<E> &err() { return this->m_unexpect; } + + template <class U = T, + detail::enable_if_t<!std::is_void<U>::value> * = nullptr> + constexpr const U &val() const { + return this->m_val; + } + constexpr const unexpected<E> &err() const { return this->m_unexpect; } + + using impl_base = detail::expected_move_assign_base<T, E>; + using ctor_base = detail::expected_default_ctor_base<T, E>; + +public: + typedef T value_type; + typedef E error_type; + typedef unexpected<E> unexpected_type; + +#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ + !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) + template <class F> TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) & { + return and_then_impl(*this, std::forward<F>(f)); + } + template <class F> TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) && { + return and_then_impl(std::move(*this), std::forward<F>(f)); + } + template <class F> constexpr auto and_then(F &&f) const & { + return and_then_impl(*this, std::forward<F>(f)); + } + +#ifndef TL_EXPECTED_NO_CONSTRR + template <class F> constexpr auto and_then(F &&f) const && { + return and_then_impl(std::move(*this), std::forward<F>(f)); + } +#endif + +#else + template <class F> + TL_EXPECTED_11_CONSTEXPR auto + and_then(F &&f) & -> decltype(and_then_impl(std::declval<expected&>(), std::forward<F>(f))) { + return and_then_impl(*this, std::forward<F>(f)); + } + template <class F> + TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) && -> decltype( + and_then_impl(std::declval<expected&&>(), std::forward<F>(f))) { + return and_then_impl(std::move(*this), std::forward<F>(f)); + } + template <class F> + constexpr auto and_then(F &&f) const & -> decltype( + and_then_impl(std::declval<expected const&>(), std::forward<F>(f))) { + return and_then_impl(*this, std::forward<F>(f)); + } + +#ifndef TL_EXPECTED_NO_CONSTRR + template <class F> + constexpr auto and_then(F &&f) const && -> decltype( + and_then_impl(std::declval<expected const&&>(), std::forward<F>(f))) { + return and_then_impl(std::move(*this), std::forward<F>(f)); + } +#endif +#endif + +#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ + !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) + template <class F> TL_EXPECTED_11_CONSTEXPR auto map(F &&f) & { + return expected_map_impl(*this, std::forward<F>(f)); + } + template <class F> TL_EXPECTED_11_CONSTEXPR auto map(F &&f) && { + return expected_map_impl(std::move(*this), std::forward<F>(f)); + } + template <class F> constexpr auto map(F &&f) const & { + return expected_map_impl(*this, std::forward<F>(f)); + } + template <class F> constexpr auto map(F &&f) const && { + return expected_map_impl(std::move(*this), std::forward<F>(f)); + } +#else + template <class F> + TL_EXPECTED_11_CONSTEXPR decltype( + expected_map_impl(std::declval<expected &>(), std::declval<F &&>())) + map(F &&f) & { + return expected_map_impl(*this, std::forward<F>(f)); + } + template <class F> + TL_EXPECTED_11_CONSTEXPR decltype( + expected_map_impl(std::declval<expected>(), std::declval<F &&>())) + map(F &&f) && { + return expected_map_impl(std::move(*this), std::forward<F>(f)); + } + template <class F> + constexpr decltype(expected_map_impl(std::declval<const expected &>(), + std::declval<F &&>())) + map(F &&f) const & { + return expected_map_impl(*this, std::forward<F>(f)); + } + +#ifndef TL_EXPECTED_NO_CONSTRR + template <class F> + constexpr decltype(expected_map_impl(std::declval<const expected &&>(), + std::declval<F &&>())) + map(F &&f) const && { + return expected_map_impl(std::move(*this), std::forward<F>(f)); + } +#endif +#endif + +#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ + !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) + template <class F> TL_EXPECTED_11_CONSTEXPR auto transform(F &&f) & { + return expected_map_impl(*this, std::forward<F>(f)); + } + template <class F> TL_EXPECTED_11_CONSTEXPR auto transform(F &&f) && { + return expected_map_impl(std::move(*this), std::forward<F>(f)); + } + template <class F> constexpr auto transform(F &&f) const & { + return expected_map_impl(*this, std::forward<F>(f)); + } + template <class F> constexpr auto transform(F &&f) const && { + return expected_map_impl(std::move(*this), std::forward<F>(f)); + } +#else + template <class F> + TL_EXPECTED_11_CONSTEXPR decltype( + expected_map_impl(std::declval<expected &>(), std::declval<F &&>())) + transform(F &&f) & { + return expected_map_impl(*this, std::forward<F>(f)); + } + template <class F> + TL_EXPECTED_11_CONSTEXPR decltype( + expected_map_impl(std::declval<expected>(), std::declval<F &&>())) + transform(F &&f) && { + return expected_map_impl(std::move(*this), std::forward<F>(f)); + } + template <class F> + constexpr decltype(expected_map_impl(std::declval<const expected &>(), + std::declval<F &&>())) + transform(F &&f) const & { + return expected_map_impl(*this, std::forward<F>(f)); + } + +#ifndef TL_EXPECTED_NO_CONSTRR + template <class F> + constexpr decltype(expected_map_impl(std::declval<const expected &&>(), + std::declval<F &&>())) + transform(F &&f) const && { + return expected_map_impl(std::move(*this), std::forward<F>(f)); + } +#endif +#endif + +#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ + !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) + template <class F> TL_EXPECTED_11_CONSTEXPR auto map_error(F &&f) & { + return map_error_impl(*this, std::forward<F>(f)); + } + template <class F> TL_EXPECTED_11_CONSTEXPR auto map_error(F &&f) && { + return map_error_impl(std::move(*this), std::forward<F>(f)); + } + template <class F> constexpr auto map_error(F &&f) const & { + return map_error_impl(*this, std::forward<F>(f)); + } + template <class F> constexpr auto map_error(F &&f) const && { + return map_error_impl(std::move(*this), std::forward<F>(f)); + } +#else + template <class F> + TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval<expected &>(), + std::declval<F &&>())) + map_error(F &&f) & { + return map_error_impl(*this, std::forward<F>(f)); + } + template <class F> + TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval<expected &&>(), + std::declval<F &&>())) + map_error(F &&f) && { + return map_error_impl(std::move(*this), std::forward<F>(f)); + } + template <class F> + constexpr decltype(map_error_impl(std::declval<const expected &>(), + std::declval<F &&>())) + map_error(F &&f) const & { + return map_error_impl(*this, std::forward<F>(f)); + } + +#ifndef TL_EXPECTED_NO_CONSTRR + template <class F> + constexpr decltype(map_error_impl(std::declval<const expected &&>(), + std::declval<F &&>())) + map_error(F &&f) const && { + return map_error_impl(std::move(*this), std::forward<F>(f)); + } +#endif +#endif + template <class F> expected TL_EXPECTED_11_CONSTEXPR or_else(F &&f) & { + return or_else_impl(*this, std::forward<F>(f)); + } + + template <class F> expected TL_EXPECTED_11_CONSTEXPR or_else(F &&f) && { + return or_else_impl(std::move(*this), std::forward<F>(f)); + } + + template <class F> expected constexpr or_else(F &&f) const & { + return or_else_impl(*this, std::forward<F>(f)); + } + +#ifndef TL_EXPECTED_NO_CONSTRR + template <class F> expected constexpr or_else(F &&f) const && { + return or_else_impl(std::move(*this), std::forward<F>(f)); + } +#endif + constexpr expected() = default; + constexpr expected(const expected &rhs) = default; + constexpr expected(expected &&rhs) = default; + expected &operator=(const expected &rhs) = default; + expected &operator=(expected &&rhs) = default; + + template <class... Args, + detail::enable_if_t<std::is_constructible<T, Args &&...>::value> * = + nullptr> + constexpr expected(in_place_t, Args &&... args) + : impl_base(in_place, std::forward<Args>(args)...), + ctor_base(detail::default_constructor_tag{}) {} + + template <class U, class... Args, + detail::enable_if_t<std::is_constructible< + T, std::initializer_list<U> &, Args &&...>::value> * = nullptr> + constexpr expected(in_place_t, std::initializer_list<U> il, Args &&... args) + : impl_base(in_place, il, std::forward<Args>(args)...), + ctor_base(detail::default_constructor_tag{}) {} + + template <class G = E, + detail::enable_if_t<std::is_constructible<E, const G &>::value> * = + nullptr, + detail::enable_if_t<!std::is_convertible<const G &, E>::value> * = + nullptr> + explicit constexpr expected(const unexpected<G> &e) + : impl_base(unexpect, e.value()), + ctor_base(detail::default_constructor_tag{}) {} + + template < + class G = E, + detail::enable_if_t<std::is_constructible<E, const G &>::value> * = + nullptr, + detail::enable_if_t<std::is_convertible<const G &, E>::value> * = nullptr> + constexpr expected(unexpected<G> const &e) + : impl_base(unexpect, e.value()), + ctor_base(detail::default_constructor_tag{}) {} + + template < + class G = E, + detail::enable_if_t<std::is_constructible<E, G &&>::value> * = nullptr, + detail::enable_if_t<!std::is_convertible<G &&, E>::value> * = nullptr> + explicit constexpr expected(unexpected<G> &&e) noexcept( + std::is_nothrow_constructible<E, G &&>::value) + : impl_base(unexpect, std::move(e.value())), + ctor_base(detail::default_constructor_tag{}) {} + + template < + class G = E, + detail::enable_if_t<std::is_constructible<E, G &&>::value> * = nullptr, + detail::enable_if_t<std::is_convertible<G &&, E>::value> * = nullptr> + constexpr expected(unexpected<G> &&e) noexcept( + std::is_nothrow_constructible<E, G &&>::value) + : impl_base(unexpect, std::move(e.value())), + ctor_base(detail::default_constructor_tag{}) {} + + template <class... Args, + detail::enable_if_t<std::is_constructible<E, Args &&...>::value> * = + nullptr> + constexpr explicit expected(unexpect_t, Args &&... args) + : impl_base(unexpect, std::forward<Args>(args)...), + ctor_base(detail::default_constructor_tag{}) {} + + template <class U, class... Args, + detail::enable_if_t<std::is_constructible< + E, std::initializer_list<U> &, Args &&...>::value> * = nullptr> + constexpr explicit expected(unexpect_t, std::initializer_list<U> il, + Args &&... args) + : impl_base(unexpect, il, std::forward<Args>(args)...), + ctor_base(detail::default_constructor_tag{}) {} + + template <class U, class G, + detail::enable_if_t<!(std::is_convertible<U const &, T>::value && + std::is_convertible<G const &, E>::value)> * = + nullptr, + detail::expected_enable_from_other<T, E, U, G, const U &, const G &> + * = nullptr> + explicit TL_EXPECTED_11_CONSTEXPR expected(const expected<U, G> &rhs) + : ctor_base(detail::default_constructor_tag{}) { + if (rhs.has_value()) { + this->construct(*rhs); + } else { + this->construct_error(rhs.error()); + } + } + + template <class U, class G, + detail::enable_if_t<(std::is_convertible<U const &, T>::value && + std::is_convertible<G const &, E>::value)> * = + nullptr, + detail::expected_enable_from_other<T, E, U, G, const U &, const G &> + * = nullptr> + TL_EXPECTED_11_CONSTEXPR expected(const expected<U, G> &rhs) + : ctor_base(detail::default_constructor_tag{}) { + if (rhs.has_value()) { + this->construct(*rhs); + } else { + this->construct_error(rhs.error()); + } + } + + template < + class U, class G, + detail::enable_if_t<!(std::is_convertible<U &&, T>::value && + std::is_convertible<G &&, E>::value)> * = nullptr, + detail::expected_enable_from_other<T, E, U, G, U &&, G &&> * = nullptr> + explicit TL_EXPECTED_11_CONSTEXPR expected(expected<U, G> &&rhs) + : ctor_base(detail::default_constructor_tag{}) { + if (rhs.has_value()) { + this->construct(std::move(*rhs)); + } else { + this->construct_error(std::move(rhs.error())); + } + } + + template < + class U, class G, + detail::enable_if_t<(std::is_convertible<U &&, T>::value && + std::is_convertible<G &&, E>::value)> * = nullptr, + detail::expected_enable_from_other<T, E, U, G, U &&, G &&> * = nullptr> + TL_EXPECTED_11_CONSTEXPR expected(expected<U, G> &&rhs) + : ctor_base(detail::default_constructor_tag{}) { + if (rhs.has_value()) { + this->construct(std::move(*rhs)); + } else { + this->construct_error(std::move(rhs.error())); + } + } + + template < + class U = T, + detail::enable_if_t<!std::is_convertible<U &&, T>::value> * = nullptr, + detail::expected_enable_forward_value<T, E, U> * = nullptr> + explicit TL_EXPECTED_MSVC2015_CONSTEXPR expected(U &&v) + : expected(in_place, std::forward<U>(v)) {} + + template < + class U = T, + detail::enable_if_t<std::is_convertible<U &&, T>::value> * = nullptr, + detail::expected_enable_forward_value<T, E, U> * = nullptr> + TL_EXPECTED_MSVC2015_CONSTEXPR expected(U &&v) + : expected(in_place, std::forward<U>(v)) {} + + template < + class U = T, class G = T, + detail::enable_if_t<std::is_nothrow_constructible<T, U &&>::value> * = + nullptr, + detail::enable_if_t<!std::is_void<G>::value> * = nullptr, + detail::enable_if_t< + (!std::is_same<expected<T, E>, detail::decay_t<U>>::value && + !detail::conjunction<std::is_scalar<T>, + std::is_same<T, detail::decay_t<U>>>::value && + std::is_constructible<T, U>::value && + std::is_assignable<G &, U>::value && + std::is_nothrow_move_constructible<E>::value)> * = nullptr> + expected &operator=(U &&v) { + if (has_value()) { + val() = std::forward<U>(v); + } else { + err().~unexpected<E>(); + ::new (valptr()) T(std::forward<U>(v)); + this->m_has_val = true; + } + + return *this; + } + + template < + class U = T, class G = T, + detail::enable_if_t<!std::is_nothrow_constructible<T, U &&>::value> * = + nullptr, + detail::enable_if_t<!std::is_void<U>::value> * = nullptr, + detail::enable_if_t< + (!std::is_same<expected<T, E>, detail::decay_t<U>>::value && + !detail::conjunction<std::is_scalar<T>, + std::is_same<T, detail::decay_t<U>>>::value && + std::is_constructible<T, U>::value && + std::is_assignable<G &, U>::value && + std::is_nothrow_move_constructible<E>::value)> * = nullptr> + expected &operator=(U &&v) { + if (has_value()) { + val() = std::forward<U>(v); + } else { + auto tmp = std::move(err()); + err().~unexpected<E>(); + + #ifdef TL_EXPECTED_EXCEPTIONS_ENABLED + try { + ::new (valptr()) T(std::forward<U>(v)); + this->m_has_val = true; + } catch (...) { + err() = std::move(tmp); + throw; + } + #else + ::new (valptr()) T(std::forward<U>(v)); + this->m_has_val = true; + #endif + } + + return *this; + } + + template <class G = E, + detail::enable_if_t<std::is_nothrow_copy_constructible<G>::value && + std::is_assignable<G &, G>::value> * = nullptr> + expected &operator=(const unexpected<G> &rhs) { + if (!has_value()) { + err() = rhs; + } else { + this->destroy_val(); + ::new (errptr()) unexpected<E>(rhs); + this->m_has_val = false; + } + + return *this; + } + + template <class G = E, + detail::enable_if_t<std::is_nothrow_move_constructible<G>::value && + std::is_move_assignable<G>::value> * = nullptr> + expected &operator=(unexpected<G> &&rhs) noexcept { + if (!has_value()) { + err() = std::move(rhs); + } else { + this->destroy_val(); + ::new (errptr()) unexpected<E>(std::move(rhs)); + this->m_has_val = false; + } + + return *this; + } + + template <class... Args, detail::enable_if_t<std::is_nothrow_constructible< + T, Args &&...>::value> * = nullptr> + void emplace(Args &&... args) { + if (has_value()) { + val() = T(std::forward<Args>(args)...); + } else { + err().~unexpected<E>(); + ::new (valptr()) T(std::forward<Args>(args)...); + this->m_has_val = true; + } + } + + template <class... Args, detail::enable_if_t<!std::is_nothrow_constructible< + T, Args &&...>::value> * = nullptr> + void emplace(Args &&... args) { + if (has_value()) { + val() = T(std::forward<Args>(args)...); + } else { + auto tmp = std::move(err()); + err().~unexpected<E>(); + + #ifdef TL_EXPECTED_EXCEPTIONS_ENABLED + try { + ::new (valptr()) T(std::forward<Args>(args)...); + this->m_has_val = true; + } catch (...) { + err() = std::move(tmp); + throw; + } + #else + ::new (valptr()) T(std::forward<Args>(args)...); + this->m_has_val = true; + #endif + } + } + + template <class U, class... Args, + detail::enable_if_t<std::is_nothrow_constructible< + T, std::initializer_list<U> &, Args &&...>::value> * = nullptr> + void emplace(std::initializer_list<U> il, Args &&... args) { + if (has_value()) { + T t(il, std::forward<Args>(args)...); + val() = std::move(t); + } else { + err().~unexpected<E>(); + ::new (valptr()) T(il, std::forward<Args>(args)...); + this->m_has_val = true; + } + } + + template <class U, class... Args, + detail::enable_if_t<!std::is_nothrow_constructible< + T, std::initializer_list<U> &, Args &&...>::value> * = nullptr> + void emplace(std::initializer_list<U> il, Args &&... args) { + if (has_value()) { + T t(il, std::forward<Args>(args)...); + val() = std::move(t); + } else { + auto tmp = std::move(err()); + err().~unexpected<E>(); + + #ifdef TL_EXPECTED_EXCEPTIONS_ENABLED + try { + ::new (valptr()) T(il, std::forward<Args>(args)...); + this->m_has_val = true; + } catch (...) { + err() = std::move(tmp); + throw; + } + #else + ::new (valptr()) T(il, std::forward<Args>(args)...); + this->m_has_val = true; + #endif + } + } + +private: + using t_is_void = std::true_type; + using t_is_not_void = std::false_type; + using t_is_nothrow_move_constructible = std::true_type; + using move_constructing_t_can_throw = std::false_type; + using e_is_nothrow_move_constructible = std::true_type; + using move_constructing_e_can_throw = std::false_type; + + void swap_where_both_have_value(expected &/*rhs*/ , t_is_void) noexcept { + // swapping void is a no-op + } + + void swap_where_both_have_value(expected &rhs, t_is_not_void) { + using std::swap; + swap(val(), rhs.val()); + } + + void swap_where_only_one_has_value(expected &rhs, t_is_void) noexcept( + std::is_nothrow_move_constructible<E>::value) { + ::new (errptr()) unexpected_type(std::move(rhs.err())); + rhs.err().~unexpected_type(); + std::swap(this->m_has_val, rhs.m_has_val); + } + + void swap_where_only_one_has_value(expected &rhs, t_is_not_void) { + swap_where_only_one_has_value_and_t_is_not_void( + rhs, typename std::is_nothrow_move_constructible<T>::type{}, + typename std::is_nothrow_move_constructible<E>::type{}); + } + + void swap_where_only_one_has_value_and_t_is_not_void( + expected &rhs, t_is_nothrow_move_constructible, + e_is_nothrow_move_constructible) noexcept { + auto temp = std::move(val()); + val().~T(); + ::new (errptr()) unexpected_type(std::move(rhs.err())); + rhs.err().~unexpected_type(); + ::new (rhs.valptr()) T(std::move(temp)); + std::swap(this->m_has_val, rhs.m_has_val); + } + + void swap_where_only_one_has_value_and_t_is_not_void( + expected &rhs, t_is_nothrow_move_constructible, + move_constructing_e_can_throw) { + auto temp = std::move(val()); + val().~T(); +#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED + try { + ::new (errptr()) unexpected_type(std::move(rhs.err())); + rhs.err().~unexpected_type(); + ::new (rhs.valptr()) T(std::move(temp)); + std::swap(this->m_has_val, rhs.m_has_val); + } catch (...) { + val() = std::move(temp); + throw; + } +#else + ::new (errptr()) unexpected_type(std::move(rhs.err())); + rhs.err().~unexpected_type(); + ::new (rhs.valptr()) T(std::move(temp)); + std::swap(this->m_has_val, rhs.m_has_val); +#endif + } + + void swap_where_only_one_has_value_and_t_is_not_void( + expected &rhs, move_constructing_t_can_throw, + t_is_nothrow_move_constructible) { + auto temp = std::move(rhs.err()); + rhs.err().~unexpected_type(); +#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED + try { + ::new (rhs.valptr()) T(val()); + val().~T(); + ::new (errptr()) unexpected_type(std::move(temp)); + std::swap(this->m_has_val, rhs.m_has_val); + } catch (...) { + rhs.err() = std::move(temp); + throw; + } +#else + ::new (rhs.valptr()) T(val()); + val().~T(); + ::new (errptr()) unexpected_type(std::move(temp)); + std::swap(this->m_has_val, rhs.m_has_val); +#endif + } + +public: + template <class OT = T, class OE = E> + detail::enable_if_t<detail::is_swappable<OT>::value && + detail::is_swappable<OE>::value && + (std::is_nothrow_move_constructible<OT>::value || + std::is_nothrow_move_constructible<OE>::value)> + swap(expected &rhs) noexcept( + std::is_nothrow_move_constructible<T>::value + &&detail::is_nothrow_swappable<T>::value + &&std::is_nothrow_move_constructible<E>::value + &&detail::is_nothrow_swappable<E>::value) { + if (has_value() && rhs.has_value()) { + swap_where_both_have_value(rhs, typename std::is_void<T>::type{}); + } else if (!has_value() && rhs.has_value()) { + rhs.swap(*this); + } else if (has_value()) { + swap_where_only_one_has_value(rhs, typename std::is_void<T>::type{}); + } else { + using std::swap; + swap(err(), rhs.err()); + } + } + + constexpr const T *operator->() const { return valptr(); } + TL_EXPECTED_11_CONSTEXPR T *operator->() { return valptr(); } + + template <class U = T, + detail::enable_if_t<!std::is_void<U>::value> * = nullptr> + constexpr const U &operator*() const & { + return val(); + } + template <class U = T, + detail::enable_if_t<!std::is_void<U>::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR U &operator*() & { + return val(); + } + template <class U = T, + detail::enable_if_t<!std::is_void<U>::value> * = nullptr> + constexpr const U &&operator*() const && { + return std::move(val()); + } + template <class U = T, + detail::enable_if_t<!std::is_void<U>::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR U &&operator*() && { + return std::move(val()); + } + + constexpr bool has_value() const noexcept { return this->m_has_val; } + constexpr explicit operator bool() const noexcept { return this->m_has_val; } + + template <class U = T, + detail::enable_if_t<!std::is_void<U>::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR const U &value() const & { + if (!has_value()) + detail::throw_exception(bad_expected_access<E>(err().value())); + return val(); + } + template <class U = T, + detail::enable_if_t<!std::is_void<U>::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR U &value() & { + if (!has_value()) + detail::throw_exception(bad_expected_access<E>(err().value())); + return val(); + } + template <class U = T, + detail::enable_if_t<!std::is_void<U>::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR const U &&value() const && { + if (!has_value()) + detail::throw_exception(bad_expected_access<E>(std::move(err()).value())); + return std::move(val()); + } + template <class U = T, + detail::enable_if_t<!std::is_void<U>::value> * = nullptr> + TL_EXPECTED_11_CONSTEXPR U &&value() && { + if (!has_value()) + detail::throw_exception(bad_expected_access<E>(std::move(err()).value())); + return std::move(val()); + } + + constexpr const E &error() const & { return err().value(); } + TL_EXPECTED_11_CONSTEXPR E &error() & { return err().value(); } + constexpr const E &&error() const && { return std::move(err().value()); } + TL_EXPECTED_11_CONSTEXPR E &&error() && { return std::move(err().value()); } + + template <class U> constexpr T value_or(U &&v) const & { + static_assert(std::is_copy_constructible<T>::value && + std::is_convertible<U &&, T>::value, + "T must be copy-constructible and convertible to from U&&"); + return bool(*this) ? **this : static_cast<T>(std::forward<U>(v)); + } + template <class U> TL_EXPECTED_11_CONSTEXPR T value_or(U &&v) && { + static_assert(std::is_move_constructible<T>::value && + std::is_convertible<U &&, T>::value, + "T must be move-constructible and convertible to from U&&"); + return bool(*this) ? std::move(**this) : static_cast<T>(std::forward<U>(v)); + } +}; + +namespace detail { +template <class Exp> using exp_t = typename detail::decay_t<Exp>::value_type; +template <class Exp> using err_t = typename detail::decay_t<Exp>::error_type; +template <class Exp, class Ret> using ret_t = expected<Ret, err_t<Exp>>; + +#ifdef TL_EXPECTED_CXX14 +template <class Exp, class F, + detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval<F>(), + *std::declval<Exp>()))> +constexpr auto and_then_impl(Exp &&exp, F &&f) { + static_assert(detail::is_expected<Ret>::value, "F must return an expected"); + + return exp.has_value() + ? detail::invoke(std::forward<F>(f), *std::forward<Exp>(exp)) + : Ret(unexpect, std::forward<Exp>(exp).error()); +} + +template <class Exp, class F, + detail::enable_if_t<std::is_void<exp_t<Exp>>::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval<F>()))> +constexpr auto and_then_impl(Exp &&exp, F &&f) { + static_assert(detail::is_expected<Ret>::value, "F must return an expected"); + + return exp.has_value() ? detail::invoke(std::forward<F>(f)) + : Ret(unexpect, std::forward<Exp>(exp).error()); +} +#else +template <class> struct TC; +template <class Exp, class F, + class Ret = decltype(detail::invoke(std::declval<F>(), + *std::declval<Exp>())), + detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr> +auto and_then_impl(Exp &&exp, F &&f) -> Ret { + static_assert(detail::is_expected<Ret>::value, "F must return an expected"); + + return exp.has_value() + ? detail::invoke(std::forward<F>(f), *std::forward<Exp>(exp)) + : Ret(unexpect, std::forward<Exp>(exp).error()); +} + +template <class Exp, class F, + class Ret = decltype(detail::invoke(std::declval<F>())), + detail::enable_if_t<std::is_void<exp_t<Exp>>::value> * = nullptr> +constexpr auto and_then_impl(Exp &&exp, F &&f) -> Ret { + static_assert(detail::is_expected<Ret>::value, "F must return an expected"); + + return exp.has_value() ? detail::invoke(std::forward<F>(f)) + : Ret(unexpect, std::forward<Exp>(exp).error()); +} +#endif + +#ifdef TL_EXPECTED_CXX14 +template <class Exp, class F, + detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval<F>(), + *std::declval<Exp>())), + detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr> +constexpr auto expected_map_impl(Exp &&exp, F &&f) { + using result = ret_t<Exp, detail::decay_t<Ret>>; + return exp.has_value() ? result(detail::invoke(std::forward<F>(f), + *std::forward<Exp>(exp))) + : result(unexpect, std::forward<Exp>(exp).error()); +} + +template <class Exp, class F, + detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval<F>(), + *std::declval<Exp>())), + detail::enable_if_t<std::is_void<Ret>::value> * = nullptr> +auto expected_map_impl(Exp &&exp, F &&f) { + using result = expected<void, err_t<Exp>>; + if (exp.has_value()) { + detail::invoke(std::forward<F>(f), *std::forward<Exp>(exp)); + return result(); + } + + return result(unexpect, std::forward<Exp>(exp).error()); +} + +template <class Exp, class F, + detail::enable_if_t<std::is_void<exp_t<Exp>>::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval<F>())), + detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr> +constexpr auto expected_map_impl(Exp &&exp, F &&f) { + using result = ret_t<Exp, detail::decay_t<Ret>>; + return exp.has_value() ? result(detail::invoke(std::forward<F>(f))) + : result(unexpect, std::forward<Exp>(exp).error()); +} + +template <class Exp, class F, + detail::enable_if_t<std::is_void<exp_t<Exp>>::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval<F>())), + detail::enable_if_t<std::is_void<Ret>::value> * = nullptr> +auto expected_map_impl(Exp &&exp, F &&f) { + using result = expected<void, err_t<Exp>>; + if (exp.has_value()) { + detail::invoke(std::forward<F>(f)); + return result(); + } + + return result(unexpect, std::forward<Exp>(exp).error()); +} +#else +template <class Exp, class F, + detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval<F>(), + *std::declval<Exp>())), + detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr> + +constexpr auto expected_map_impl(Exp &&exp, F &&f) + -> ret_t<Exp, detail::decay_t<Ret>> { + using result = ret_t<Exp, detail::decay_t<Ret>>; + + return exp.has_value() ? result(detail::invoke(std::forward<F>(f), + *std::forward<Exp>(exp))) + : result(unexpect, std::forward<Exp>(exp).error()); +} + +template <class Exp, class F, + detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval<F>(), + *std::declval<Exp>())), + detail::enable_if_t<std::is_void<Ret>::value> * = nullptr> + +auto expected_map_impl(Exp &&exp, F &&f) -> expected<void, err_t<Exp>> { + if (exp.has_value()) { + detail::invoke(std::forward<F>(f), *std::forward<Exp>(exp)); + return {}; + } + + return unexpected<err_t<Exp>>(std::forward<Exp>(exp).error()); +} + +template <class Exp, class F, + detail::enable_if_t<std::is_void<exp_t<Exp>>::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval<F>())), + detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr> + +constexpr auto expected_map_impl(Exp &&exp, F &&f) + -> ret_t<Exp, detail::decay_t<Ret>> { + using result = ret_t<Exp, detail::decay_t<Ret>>; + + return exp.has_value() ? result(detail::invoke(std::forward<F>(f))) + : result(unexpect, std::forward<Exp>(exp).error()); +} + +template <class Exp, class F, + detail::enable_if_t<std::is_void<exp_t<Exp>>::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval<F>())), + detail::enable_if_t<std::is_void<Ret>::value> * = nullptr> + +auto expected_map_impl(Exp &&exp, F &&f) -> expected<void, err_t<Exp>> { + if (exp.has_value()) { + detail::invoke(std::forward<F>(f)); + return {}; + } + + return unexpected<err_t<Exp>>(std::forward<Exp>(exp).error()); +} +#endif + +#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ + !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) +template <class Exp, class F, + detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval<F>(), + std::declval<Exp>().error())), + detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr> +constexpr auto map_error_impl(Exp &&exp, F &&f) { + using result = expected<exp_t<Exp>, detail::decay_t<Ret>>; + return exp.has_value() + ? result(*std::forward<Exp>(exp)) + : result(unexpect, detail::invoke(std::forward<F>(f), + std::forward<Exp>(exp).error())); +} +template <class Exp, class F, + detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval<F>(), + std::declval<Exp>().error())), + detail::enable_if_t<std::is_void<Ret>::value> * = nullptr> +auto map_error_impl(Exp &&exp, F &&f) { + using result = expected<exp_t<Exp>, monostate>; + if (exp.has_value()) { + return result(*std::forward<Exp>(exp)); + } + + detail::invoke(std::forward<F>(f), std::forward<Exp>(exp).error()); + return result(unexpect, monostate{}); +} +template <class Exp, class F, + detail::enable_if_t<std::is_void<exp_t<Exp>>::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval<F>(), + std::declval<Exp>().error())), + detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr> +constexpr auto map_error_impl(Exp &&exp, F &&f) { + using result = expected<exp_t<Exp>, detail::decay_t<Ret>>; + return exp.has_value() + ? result() + : result(unexpect, detail::invoke(std::forward<F>(f), + std::forward<Exp>(exp).error())); +} +template <class Exp, class F, + detail::enable_if_t<std::is_void<exp_t<Exp>>::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval<F>(), + std::declval<Exp>().error())), + detail::enable_if_t<std::is_void<Ret>::value> * = nullptr> +auto map_error_impl(Exp &&exp, F &&f) { + using result = expected<exp_t<Exp>, monostate>; + if (exp.has_value()) { + return result(); + } + + detail::invoke(std::forward<F>(f), std::forward<Exp>(exp).error()); + return result(unexpect, monostate{}); +} +#else +template <class Exp, class F, + detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval<F>(), + std::declval<Exp>().error())), + detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr> +constexpr auto map_error_impl(Exp &&exp, F &&f) + -> expected<exp_t<Exp>, detail::decay_t<Ret>> { + using result = expected<exp_t<Exp>, detail::decay_t<Ret>>; + + return exp.has_value() + ? result(*std::forward<Exp>(exp)) + : result(unexpect, detail::invoke(std::forward<F>(f), + std::forward<Exp>(exp).error())); +} + +template <class Exp, class F, + detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval<F>(), + std::declval<Exp>().error())), + detail::enable_if_t<std::is_void<Ret>::value> * = nullptr> +auto map_error_impl(Exp &&exp, F &&f) -> expected<exp_t<Exp>, monostate> { + using result = expected<exp_t<Exp>, monostate>; + if (exp.has_value()) { + return result(*std::forward<Exp>(exp)); + } + + detail::invoke(std::forward<F>(f), std::forward<Exp>(exp).error()); + return result(unexpect, monostate{}); +} + +template <class Exp, class F, + detail::enable_if_t<std::is_void<exp_t<Exp>>::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval<F>(), + std::declval<Exp>().error())), + detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr> +constexpr auto map_error_impl(Exp &&exp, F &&f) + -> expected<exp_t<Exp>, detail::decay_t<Ret>> { + using result = expected<exp_t<Exp>, detail::decay_t<Ret>>; + + return exp.has_value() + ? result() + : result(unexpect, detail::invoke(std::forward<F>(f), + std::forward<Exp>(exp).error())); +} + +template <class Exp, class F, + detail::enable_if_t<std::is_void<exp_t<Exp>>::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval<F>(), + std::declval<Exp>().error())), + detail::enable_if_t<std::is_void<Ret>::value> * = nullptr> +auto map_error_impl(Exp &&exp, F &&f) -> expected<exp_t<Exp>, monostate> { + using result = expected<exp_t<Exp>, monostate>; + if (exp.has_value()) { + return result(); + } + + detail::invoke(std::forward<F>(f), std::forward<Exp>(exp).error()); + return result(unexpect, monostate{}); +} +#endif + +#ifdef TL_EXPECTED_CXX14 +template <class Exp, class F, + class Ret = decltype(detail::invoke(std::declval<F>(), + std::declval<Exp>().error())), + detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr> +constexpr auto or_else_impl(Exp &&exp, F &&f) { + static_assert(detail::is_expected<Ret>::value, "F must return an expected"); + return exp.has_value() + ? std::forward<Exp>(exp) + : detail::invoke(std::forward<F>(f), std::forward<Exp>(exp).error()); +} + +template <class Exp, class F, + class Ret = decltype(detail::invoke(std::declval<F>(), + std::declval<Exp>().error())), + detail::enable_if_t<std::is_void<Ret>::value> * = nullptr> +detail::decay_t<Exp> or_else_impl(Exp &&exp, F &&f) { + return exp.has_value() + ? std::forward<Exp>(exp) + : (detail::invoke(std::forward<F>(f), std::forward<Exp>(exp).error()), + std::forward<Exp>(exp)); +} +#else +template <class Exp, class F, + class Ret = decltype(detail::invoke(std::declval<F>(), + std::declval<Exp>().error())), + detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr> +auto or_else_impl(Exp &&exp, F &&f) -> Ret { + static_assert(detail::is_expected<Ret>::value, "F must return an expected"); + return exp.has_value() + ? std::forward<Exp>(exp) + : detail::invoke(std::forward<F>(f), std::forward<Exp>(exp).error()); +} + +template <class Exp, class F, + class Ret = decltype(detail::invoke(std::declval<F>(), + std::declval<Exp>().error())), + detail::enable_if_t<std::is_void<Ret>::value> * = nullptr> +detail::decay_t<Exp> or_else_impl(Exp &&exp, F &&f) { + return exp.has_value() + ? std::forward<Exp>(exp) + : (detail::invoke(std::forward<F>(f), std::forward<Exp>(exp).error()), + std::forward<Exp>(exp)); +} +#endif +} // namespace detail + +template <class T, class E, class U, class F> +constexpr bool operator==(const expected<T, E> &lhs, + const expected<U, F> &rhs) { + return (lhs.has_value() != rhs.has_value()) + ? false + : (!lhs.has_value() ? lhs.error() == rhs.error() : *lhs == *rhs); +} +template <class T, class E, class U, class F> +constexpr bool operator!=(const expected<T, E> &lhs, + const expected<U, F> &rhs) { + return (lhs.has_value() != rhs.has_value()) + ? true + : (!lhs.has_value() ? lhs.error() != rhs.error() : *lhs != *rhs); +} + +template <class T, class E, class U> +constexpr bool operator==(const expected<T, E> &x, const U &v) { + return x.has_value() ? *x == v : false; +} +template <class T, class E, class U> +constexpr bool operator==(const U &v, const expected<T, E> &x) { + return x.has_value() ? *x == v : false; +} +template <class T, class E, class U> +constexpr bool operator!=(const expected<T, E> &x, const U &v) { + return x.has_value() ? *x != v : true; +} +template <class T, class E, class U> +constexpr bool operator!=(const U &v, const expected<T, E> &x) { + return x.has_value() ? *x != v : true; +} + +template <class T, class E> +constexpr bool operator==(const expected<T, E> &x, const unexpected<E> &e) { + return x.has_value() ? false : x.error() == e.value(); +} +template <class T, class E> +constexpr bool operator==(const unexpected<E> &e, const expected<T, E> &x) { + return x.has_value() ? false : x.error() == e.value(); +} +template <class T, class E> +constexpr bool operator!=(const expected<T, E> &x, const unexpected<E> &e) { + return x.has_value() ? true : x.error() != e.value(); +} +template <class T, class E> +constexpr bool operator!=(const unexpected<E> &e, const expected<T, E> &x) { + return x.has_value() ? true : x.error() != e.value(); +} + +template <class T, class E, + detail::enable_if_t<(std::is_void<T>::value || + std::is_move_constructible<T>::value) && + detail::is_swappable<T>::value && + std::is_move_constructible<E>::value && + detail::is_swappable<E>::value> * = nullptr> +void swap(expected<T, E> &lhs, + expected<T, E> &rhs) noexcept(noexcept(lhs.swap(rhs))) { + lhs.swap(rhs); +} +} // namespace tl + +#endif diff --git a/lib/utils/mu-error.hh b/lib/utils/mu-error.hh index f911977..7c39587 100644 --- a/lib/utils/mu-error.hh +++ b/lib/utils/mu-error.hh @@ -1,5 +1,5 @@ /* -** Copyright (C) 2019 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> +** Copyright (C) 2019-2020 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ** ** This program is free software; you can redistribute it and/or modify it ** under the terms of the GNU General Public License as published by the @@ -129,8 +129,6 @@ private: std::string what_; }; - } // namespace Mu - #endif /* MU_ERROR_HH__ */ diff --git a/lib/utils/mu-option.hh b/lib/utils/mu-option.hh new file mode 100644 index 0000000..06bb3a3 --- /dev/null +++ b/lib/utils/mu-option.hh @@ -0,0 +1,26 @@ +/* + * Created on 2020-11-08 by Dirk-Jan C. Binnema <dbinnema@logitech.com> + * + * Copyright (c) 2020 Logitech, Inc. All Rights Reserved + * This program is a trade secret of LOGITECH, and it is not to be reproduced, + * published, disclosed to others, copied, adapted, distributed or displayed + * without the prior authorization of LOGITECH. + * + * Licensee agrees to attach or embed this notice on all copies of the program, + * including partial copies or modified versions thereof. + * + */ + +#ifndef MU_OPTION__ +#define MU_OPTION__ + +#include "optional.hpp" + + +namespace Mu { + +/// Either a value of type T, or nothing. +template <typename T> using Option=tl::optional<T>; + +} +#endif /*MU_OPTION__*/ diff --git a/lib/utils/mu-result.hh b/lib/utils/mu-result.hh new file mode 100644 index 0000000..b871353 --- /dev/null +++ b/lib/utils/mu-result.hh @@ -0,0 +1,39 @@ +/* +** Copyright (C) 2019-2020 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> +** +** This program is free software; you can redistribute it and/or modify it +** under the terms of the GNU General Public License as published by the +** Free Software Foundation; either version 3, or (at your option) any +** later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software Foundation, +** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +** +*/ + + +#ifndef MU_RESULT_HH__ +#define MU_RESULT_HH__ + + +#include "expected.hpp" +#include "utils/mu-error.hh" + + +namespace Mu { +/** + * A Result is _either_ some value of type T, _or_ an error. + * + */ +template <typename T> using Result = tl::expected<T, Error>; + +} // namespace Mu + + +#endif /* MU_ERROR_HH__ */ diff --git a/lib/utils/optional.hpp b/lib/utils/optional.hpp new file mode 100644 index 0000000..ad8ae2f --- /dev/null +++ b/lib/utils/optional.hpp @@ -0,0 +1,2062 @@ + +/// +// optional - An implementation of std::optional with extensions +// Written in 2017 by Simon Brand (simonrbrand@gmail.com, @TartanLlama) +// +// Documentation available at https://tl.tartanllama.xyz/ +// +// To the extent possible under law, the author(s) have dedicated all +// copyright and related and neighboring rights to this software to the +// public domain worldwide. This software is distributed without any warranty. +// +// You should have received a copy of the CC0 Public Domain Dedication +// along with this software. If not, see +// <http://creativecommons.org/publicdomain/zero/1.0/>. +/// + +#ifndef TL_OPTIONAL_HPP +#define TL_OPTIONAL_HPP + +#define TL_OPTIONAL_VERSION_MAJOR 1 +#define TL_OPTIONAL_VERSION_MINOR 0 +#define TL_OPTIONAL_VERSION_PATCH 0 + +#include <exception> +#include <functional> +#include <new> +#include <type_traits> +#include <utility> + +#if (defined(_MSC_VER) && _MSC_VER == 1900) +#define TL_OPTIONAL_MSVC2015 +#endif + +#if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 && \ + !defined(__clang__)) +#define TL_OPTIONAL_GCC49 +#endif + +#if (defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 4 && \ + !defined(__clang__)) +#define TL_OPTIONAL_GCC54 +#endif + +#if (defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 5 && \ + !defined(__clang__)) +#define TL_OPTIONAL_GCC55 +#endif + +#if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 && \ + !defined(__clang__)) +// GCC < 5 doesn't support overloading on const&& for member functions +#define TL_OPTIONAL_NO_CONSTRR + +// GCC < 5 doesn't support some standard C++11 type traits +#define TL_OPTIONAL_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \ + std::has_trivial_copy_constructor<T>::value +#define TL_OPTIONAL_IS_TRIVIALLY_COPY_ASSIGNABLE(T) std::has_trivial_copy_assign<T>::value + +// This one will be different for GCC 5.7 if it's ever supported +#define TL_OPTIONAL_IS_TRIVIALLY_DESTRUCTIBLE(T) std::is_trivially_destructible<T>::value + +// GCC 5 < v < 8 has a bug in is_trivially_copy_constructible which breaks std::vector +// for non-copyable types +#elif (defined(__GNUC__) && __GNUC__ < 8 && \ + !defined(__clang__)) +#ifndef TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX +#define TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX +namespace tl { + namespace detail { + template<class T> + struct is_trivially_copy_constructible : std::is_trivially_copy_constructible<T>{}; +#ifdef _GLIBCXX_VECTOR + template<class T, class A> + struct is_trivially_copy_constructible<std::vector<T,A>> + : std::is_trivially_copy_constructible<T>{}; +#endif + } +} +#endif + +#define TL_OPTIONAL_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \ + tl::detail::is_trivially_copy_constructible<T>::value +#define TL_OPTIONAL_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \ + std::is_trivially_copy_assignable<T>::value +#define TL_OPTIONAL_IS_TRIVIALLY_DESTRUCTIBLE(T) std::is_trivially_destructible<T>::value +#else +#define TL_OPTIONAL_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \ + std::is_trivially_copy_constructible<T>::value +#define TL_OPTIONAL_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \ + std::is_trivially_copy_assignable<T>::value +#define TL_OPTIONAL_IS_TRIVIALLY_DESTRUCTIBLE(T) std::is_trivially_destructible<T>::value +#endif + +#if __cplusplus > 201103L +#define TL_OPTIONAL_CXX14 +#endif + +// constexpr implies const in C++11, not C++14 +#if (__cplusplus == 201103L || defined(TL_OPTIONAL_MSVC2015) || \ + defined(TL_OPTIONAL_GCC49)) +#define TL_OPTIONAL_11_CONSTEXPR +#else +#define TL_OPTIONAL_11_CONSTEXPR constexpr +#endif + +namespace tl { +#ifndef TL_MONOSTATE_INPLACE_MUTEX +#define TL_MONOSTATE_INPLACE_MUTEX +/// Used to represent an optional with no data; essentially a bool +class monostate {}; + +/// A tag type to tell optional to construct its value in-place +struct in_place_t { + explicit in_place_t() = default; +}; +/// A tag to tell optional to construct its value in-place +static constexpr in_place_t in_place{}; +#endif + +template <class T> class optional; + +namespace detail { +#ifndef TL_TRAITS_MUTEX +#define TL_TRAITS_MUTEX +// C++14-style aliases for brevity +template <class T> using remove_const_t = typename std::remove_const<T>::type; +template <class T> +using remove_reference_t = typename std::remove_reference<T>::type; +template <class T> using decay_t = typename std::decay<T>::type; +template <bool E, class T = void> +using enable_if_t = typename std::enable_if<E, T>::type; +template <bool B, class T, class F> +using conditional_t = typename std::conditional<B, T, F>::type; + +// std::conjunction from C++17 +template <class...> struct conjunction : std::true_type {}; +template <class B> struct conjunction<B> : B {}; +template <class B, class... Bs> +struct conjunction<B, Bs...> + : std::conditional<bool(B::value), conjunction<Bs...>, B>::type {}; + +#if defined(_LIBCPP_VERSION) && __cplusplus == 201103L +#define TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND +#endif + +// In C++11 mode, there's an issue in libc++'s std::mem_fn +// which results in a hard-error when using it in a noexcept expression +// in some cases. This is a check to workaround the common failing case. +#ifdef TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND +template <class T> struct is_pointer_to_non_const_member_func : std::false_type{}; +template <class T, class Ret, class... Args> +struct is_pointer_to_non_const_member_func<Ret (T::*) (Args...)> : std::true_type{}; +template <class T, class Ret, class... Args> +struct is_pointer_to_non_const_member_func<Ret (T::*) (Args...)&> : std::true_type{}; +template <class T, class Ret, class... Args> +struct is_pointer_to_non_const_member_func<Ret (T::*) (Args...)&&> : std::true_type{}; +template <class T, class Ret, class... Args> +struct is_pointer_to_non_const_member_func<Ret (T::*) (Args...) volatile> : std::true_type{}; +template <class T, class Ret, class... Args> +struct is_pointer_to_non_const_member_func<Ret (T::*) (Args...) volatile&> : std::true_type{}; +template <class T, class Ret, class... Args> +struct is_pointer_to_non_const_member_func<Ret (T::*) (Args...) volatile&&> : std::true_type{}; + +template <class T> struct is_const_or_const_ref : std::false_type{}; +template <class T> struct is_const_or_const_ref<T const&> : std::true_type{}; +template <class T> struct is_const_or_const_ref<T const> : std::true_type{}; +#endif + +// std::invoke from C++17 +// https://stackoverflow.com/questions/38288042/c11-14-invoke-workaround +template <typename Fn, typename... Args, +#ifdef TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND + typename = enable_if_t<!(is_pointer_to_non_const_member_func<Fn>::value + && is_const_or_const_ref<Args...>::value)>, +#endif + typename = enable_if_t<std::is_member_pointer<decay_t<Fn>>::value>, + int = 0> +constexpr auto invoke(Fn &&f, Args &&... args) noexcept( + noexcept(std::mem_fn(f)(std::forward<Args>(args)...))) + -> decltype(std::mem_fn(f)(std::forward<Args>(args)...)) { + return std::mem_fn(f)(std::forward<Args>(args)...); +} + +template <typename Fn, typename... Args, + typename = enable_if_t<!std::is_member_pointer<decay_t<Fn>>::value>> +constexpr auto invoke(Fn &&f, Args &&... args) noexcept( + noexcept(std::forward<Fn>(f)(std::forward<Args>(args)...))) + -> decltype(std::forward<Fn>(f)(std::forward<Args>(args)...)) { + return std::forward<Fn>(f)(std::forward<Args>(args)...); +} + +// std::invoke_result from C++17 +template <class F, class, class... Us> struct invoke_result_impl; + +template <class F, class... Us> +struct invoke_result_impl< + F, decltype(detail::invoke(std::declval<F>(), std::declval<Us>()...), void()), + Us...> { + using type = decltype(detail::invoke(std::declval<F>(), std::declval<Us>()...)); +}; + +template <class F, class... Us> +using invoke_result = invoke_result_impl<F, void, Us...>; + +template <class F, class... Us> +using invoke_result_t = typename invoke_result<F, Us...>::type; + +#if defined(_MSC_VER) && _MSC_VER <= 1900 +// TODO make a version which works with MSVC 2015 +template <class T, class U = T> struct is_swappable : std::true_type {}; + +template <class T, class U = T> struct is_nothrow_swappable : std::true_type {}; +#else +// https://stackoverflow.com/questions/26744589/what-is-a-proper-way-to-implement-is-swappable-to-test-for-the-swappable-concept +namespace swap_adl_tests { +// if swap ADL finds this then it would call std::swap otherwise (same +// signature) +struct tag {}; + +template <class T> tag swap(T &, T &); +template <class T, std::size_t N> tag swap(T (&a)[N], T (&b)[N]); + +// helper functions to test if an unqualified swap is possible, and if it +// becomes std::swap +template <class, class> std::false_type can_swap(...) noexcept(false); +template <class T, class U, + class = decltype(swap(std::declval<T &>(), std::declval<U &>()))> +std::true_type can_swap(int) noexcept(noexcept(swap(std::declval<T &>(), + std::declval<U &>()))); + +template <class, class> std::false_type uses_std(...); +template <class T, class U> +std::is_same<decltype(swap(std::declval<T &>(), std::declval<U &>())), tag> +uses_std(int); + +template <class T> +struct is_std_swap_noexcept + : std::integral_constant<bool, + std::is_nothrow_move_constructible<T>::value && + std::is_nothrow_move_assignable<T>::value> {}; + +template <class T, std::size_t N> +struct is_std_swap_noexcept<T[N]> : is_std_swap_noexcept<T> {}; + +template <class T, class U> +struct is_adl_swap_noexcept + : std::integral_constant<bool, noexcept(can_swap<T, U>(0))> {}; +} // namespace swap_adl_tests + +template <class T, class U = T> +struct is_swappable + : std::integral_constant< + bool, + decltype(detail::swap_adl_tests::can_swap<T, U>(0))::value && + (!decltype(detail::swap_adl_tests::uses_std<T, U>(0))::value || + (std::is_move_assignable<T>::value && + std::is_move_constructible<T>::value))> {}; + +template <class T, std::size_t N> +struct is_swappable<T[N], T[N]> + : std::integral_constant< + bool, + decltype(detail::swap_adl_tests::can_swap<T[N], T[N]>(0))::value && + (!decltype( + detail::swap_adl_tests::uses_std<T[N], T[N]>(0))::value || + is_swappable<T, T>::value)> {}; + +template <class T, class U = T> +struct is_nothrow_swappable + : std::integral_constant< + bool, + is_swappable<T, U>::value && + ((decltype(detail::swap_adl_tests::uses_std<T, U>(0))::value + &&detail::swap_adl_tests::is_std_swap_noexcept<T>::value) || + (!decltype(detail::swap_adl_tests::uses_std<T, U>(0))::value && + detail::swap_adl_tests::is_adl_swap_noexcept<T, + U>::value))> { +}; +#endif +#endif + +// std::void_t from C++17 +template <class...> struct voider { using type = void; }; +template <class... Ts> using void_t = typename voider<Ts...>::type; + +// Trait for checking if a type is a tl::optional +template <class T> struct is_optional_impl : std::false_type {}; +template <class T> struct is_optional_impl<optional<T>> : std::true_type {}; +template <class T> using is_optional = is_optional_impl<decay_t<T>>; + +// Change void to tl::monostate +template <class U> +using fixup_void = conditional_t<std::is_void<U>::value, monostate, U>; + +template <class F, class U, class = invoke_result_t<F, U>> +using get_map_return = optional<fixup_void<invoke_result_t<F, U>>>; + +// Check if invoking F for some Us returns void +template <class F, class = void, class... U> struct returns_void_impl; +template <class F, class... U> +struct returns_void_impl<F, void_t<invoke_result_t<F, U...>>, U...> + : std::is_void<invoke_result_t<F, U...>> {}; +template <class F, class... U> +using returns_void = returns_void_impl<F, void, U...>; + +template <class T, class... U> +using enable_if_ret_void = enable_if_t<returns_void<T &&, U...>::value>; + +template <class T, class... U> +using disable_if_ret_void = enable_if_t<!returns_void<T &&, U...>::value>; + +template <class T, class U> +using enable_forward_value = + detail::enable_if_t<std::is_constructible<T, U &&>::value && + !std::is_same<detail::decay_t<U>, in_place_t>::value && + !std::is_same<optional<T>, detail::decay_t<U>>::value>; + +template <class T, class U, class Other> +using enable_from_other = detail::enable_if_t< + std::is_constructible<T, Other>::value && + !std::is_constructible<T, optional<U> &>::value && + !std::is_constructible<T, optional<U> &&>::value && + !std::is_constructible<T, const optional<U> &>::value && + !std::is_constructible<T, const optional<U> &&>::value && + !std::is_convertible<optional<U> &, T>::value && + !std::is_convertible<optional<U> &&, T>::value && + !std::is_convertible<const optional<U> &, T>::value && + !std::is_convertible<const optional<U> &&, T>::value>; + +template <class T, class U> +using enable_assign_forward = detail::enable_if_t< + !std::is_same<optional<T>, detail::decay_t<U>>::value && + !detail::conjunction<std::is_scalar<T>, + std::is_same<T, detail::decay_t<U>>>::value && + std::is_constructible<T, U>::value && std::is_assignable<T &, U>::value>; + +template <class T, class U, class Other> +using enable_assign_from_other = detail::enable_if_t< + std::is_constructible<T, Other>::value && + std::is_assignable<T &, Other>::value && + !std::is_constructible<T, optional<U> &>::value && + !std::is_constructible<T, optional<U> &&>::value && + !std::is_constructible<T, const optional<U> &>::value && + !std::is_constructible<T, const optional<U> &&>::value && + !std::is_convertible<optional<U> &, T>::value && + !std::is_convertible<optional<U> &&, T>::value && + !std::is_convertible<const optional<U> &, T>::value && + !std::is_convertible<const optional<U> &&, T>::value && + !std::is_assignable<T &, optional<U> &>::value && + !std::is_assignable<T &, optional<U> &&>::value && + !std::is_assignable<T &, const optional<U> &>::value && + !std::is_assignable<T &, const optional<U> &&>::value>; + +// The storage base manages the actual storage, and correctly propagates +// trivial destruction from T. This case is for when T is not trivially +// destructible. +template <class T, bool = ::std::is_trivially_destructible<T>::value> +struct optional_storage_base { + TL_OPTIONAL_11_CONSTEXPR optional_storage_base() noexcept + : m_dummy(), m_has_value(false) {} + + template <class... U> + TL_OPTIONAL_11_CONSTEXPR optional_storage_base(in_place_t, U &&... u) + : m_value(std::forward<U>(u)...), m_has_value(true) {} + + ~optional_storage_base() { + if (m_has_value) { + m_value.~T(); + m_has_value = false; + } + } + + struct dummy {}; + union { + dummy m_dummy; + T m_value; + }; + + bool m_has_value; +}; + +// This case is for when T is trivially destructible. +template <class T> struct optional_storage_base<T, true> { + TL_OPTIONAL_11_CONSTEXPR optional_storage_base() noexcept + : m_dummy(), m_has_value(false) {} + + template <class... U> + TL_OPTIONAL_11_CONSTEXPR optional_storage_base(in_place_t, U &&... u) + : m_value(std::forward<U>(u)...), m_has_value(true) {} + + // No destructor, so this class is trivially destructible + + struct dummy {}; + union { + dummy m_dummy; + T m_value; + }; + + bool m_has_value = false; +}; + +// This base class provides some handy member functions which can be used in +// further derived classes +template <class T> struct optional_operations_base : optional_storage_base<T> { + using optional_storage_base<T>::optional_storage_base; + + void hard_reset() noexcept { + get().~T(); + this->m_has_value = false; + } + + template <class... Args> void construct(Args &&... args) noexcept { + new (std::addressof(this->m_value)) T(std::forward<Args>(args)...); + this->m_has_value = true; + } + + template <class Opt> void assign(Opt &&rhs) { + if (this->has_value()) { + if (rhs.has_value()) { + this->m_value = std::forward<Opt>(rhs).get(); + } else { + this->m_value.~T(); + this->m_has_value = false; + } + } + + else if (rhs.has_value()) { + construct(std::forward<Opt>(rhs).get()); + } + } + + bool has_value() const { return this->m_has_value; } + + TL_OPTIONAL_11_CONSTEXPR T &get() & { return this->m_value; } + TL_OPTIONAL_11_CONSTEXPR const T &get() const & { return this->m_value; } + TL_OPTIONAL_11_CONSTEXPR T &&get() && { return std::move(this->m_value); } +#ifndef TL_OPTIONAL_NO_CONSTRR + constexpr const T &&get() const && { return std::move(this->m_value); } +#endif +}; + +// This class manages conditionally having a trivial copy constructor +// This specialization is for when T is trivially copy constructible +template <class T, bool = TL_OPTIONAL_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T)> +struct optional_copy_base : optional_operations_base<T> { + using optional_operations_base<T>::optional_operations_base; +}; + +// This specialization is for when T is not trivially copy constructible +template <class T> +struct optional_copy_base<T, false> : optional_operations_base<T> { + using optional_operations_base<T>::optional_operations_base; + + optional_copy_base() = default; + optional_copy_base(const optional_copy_base &rhs) { + if (rhs.has_value()) { + this->construct(rhs.get()); + } else { + this->m_has_value = false; + } + } + + optional_copy_base(optional_copy_base &&rhs) = default; + optional_copy_base &operator=(const optional_copy_base &rhs) = default; + optional_copy_base &operator=(optional_copy_base &&rhs) = default; +}; + +// This class manages conditionally having a trivial move constructor +// Unfortunately there's no way to achieve this in GCC < 5 AFAIK, since it +// doesn't implement an analogue to std::is_trivially_move_constructible. We +// have to make do with a non-trivial move constructor even if T is trivially +// move constructible +#ifndef TL_OPTIONAL_GCC49 +template <class T, bool = std::is_trivially_move_constructible<T>::value> +struct optional_move_base : optional_copy_base<T> { + using optional_copy_base<T>::optional_copy_base; +}; +#else +template <class T, bool = false> struct optional_move_base; +#endif +template <class T> struct optional_move_base<T, false> : optional_copy_base<T> { + using optional_copy_base<T>::optional_copy_base; + + optional_move_base() = default; + optional_move_base(const optional_move_base &rhs) = default; + + optional_move_base(optional_move_base &&rhs) noexcept( + std::is_nothrow_move_constructible<T>::value) { + if (rhs.has_value()) { + this->construct(std::move(rhs.get())); + } else { + this->m_has_value = false; + } + } + optional_move_base &operator=(const optional_move_base &rhs) = default; + optional_move_base &operator=(optional_move_base &&rhs) = default; +}; + +// This class manages conditionally having a trivial copy assignment operator +template <class T, bool = TL_OPTIONAL_IS_TRIVIALLY_COPY_ASSIGNABLE(T) && + TL_OPTIONAL_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) && + TL_OPTIONAL_IS_TRIVIALLY_DESTRUCTIBLE(T)> +struct optional_copy_assign_base : optional_move_base<T> { + using optional_move_base<T>::optional_move_base; +}; + +template <class T> +struct optional_copy_assign_base<T, false> : optional_move_base<T> { + using optional_move_base<T>::optional_move_base; + + optional_copy_assign_base() = default; + optional_copy_assign_base(const optional_copy_assign_base &rhs) = default; + + optional_copy_assign_base(optional_copy_assign_base &&rhs) = default; + optional_copy_assign_base &operator=(const optional_copy_assign_base &rhs) { + this->assign(rhs); + return *this; + } + optional_copy_assign_base & + operator=(optional_copy_assign_base &&rhs) = default; +}; + +// This class manages conditionally having a trivial move assignment operator +// Unfortunately there's no way to achieve this in GCC < 5 AFAIK, since it +// doesn't implement an analogue to std::is_trivially_move_assignable. We have +// to make do with a non-trivial move assignment operator even if T is trivially +// move assignable +#ifndef TL_OPTIONAL_GCC49 +template <class T, bool = std::is_trivially_destructible<T>::value + &&std::is_trivially_move_constructible<T>::value + &&std::is_trivially_move_assignable<T>::value> +struct optional_move_assign_base : optional_copy_assign_base<T> { + using optional_copy_assign_base<T>::optional_copy_assign_base; +}; +#else +template <class T, bool = false> struct optional_move_assign_base; +#endif + +template <class T> +struct optional_move_assign_base<T, false> : optional_copy_assign_base<T> { + using optional_copy_assign_base<T>::optional_copy_assign_base; + + optional_move_assign_base() = default; + optional_move_assign_base(const optional_move_assign_base &rhs) = default; + + optional_move_assign_base(optional_move_assign_base &&rhs) = default; + + optional_move_assign_base & + operator=(const optional_move_assign_base &rhs) = default; + + optional_move_assign_base & + operator=(optional_move_assign_base &&rhs) noexcept( + std::is_nothrow_move_constructible<T>::value + &&std::is_nothrow_move_assignable<T>::value) { + this->assign(std::move(rhs)); + return *this; + } +}; + +// optional_delete_ctor_base will conditionally delete copy and move +// constructors depending on whether T is copy/move constructible +template <class T, bool EnableCopy = std::is_copy_constructible<T>::value, + bool EnableMove = std::is_move_constructible<T>::value> +struct optional_delete_ctor_base { + optional_delete_ctor_base() = default; + optional_delete_ctor_base(const optional_delete_ctor_base &) = default; + optional_delete_ctor_base(optional_delete_ctor_base &&) noexcept = default; + optional_delete_ctor_base & + operator=(const optional_delete_ctor_base &) = default; + optional_delete_ctor_base & + operator=(optional_delete_ctor_base &&) noexcept = default; +}; + +template <class T> struct optional_delete_ctor_base<T, true, false> { + optional_delete_ctor_base() = default; + optional_delete_ctor_base(const optional_delete_ctor_base &) = default; + optional_delete_ctor_base(optional_delete_ctor_base &&) noexcept = delete; + optional_delete_ctor_base & + operator=(const optional_delete_ctor_base &) = default; + optional_delete_ctor_base & + operator=(optional_delete_ctor_base &&) noexcept = default; +}; + +template <class T> struct optional_delete_ctor_base<T, false, true> { + optional_delete_ctor_base() = default; + optional_delete_ctor_base(const optional_delete_ctor_base &) = delete; + optional_delete_ctor_base(optional_delete_ctor_base &&) noexcept = default; + optional_delete_ctor_base & + operator=(const optional_delete_ctor_base &) = default; + optional_delete_ctor_base & + operator=(optional_delete_ctor_base &&) noexcept = default; +}; + +template <class T> struct optional_delete_ctor_base<T, false, false> { + optional_delete_ctor_base() = default; + optional_delete_ctor_base(const optional_delete_ctor_base &) = delete; + optional_delete_ctor_base(optional_delete_ctor_base &&) noexcept = delete; + optional_delete_ctor_base & + operator=(const optional_delete_ctor_base &) = default; + optional_delete_ctor_base & + operator=(optional_delete_ctor_base &&) noexcept = default; +}; + +// optional_delete_assign_base will conditionally delete copy and move +// constructors depending on whether T is copy/move constructible + assignable +template <class T, + bool EnableCopy = (std::is_copy_constructible<T>::value && + std::is_copy_assignable<T>::value), + bool EnableMove = (std::is_move_constructible<T>::value && + std::is_move_assignable<T>::value)> +struct optional_delete_assign_base { + optional_delete_assign_base() = default; + optional_delete_assign_base(const optional_delete_assign_base &) = default; + optional_delete_assign_base(optional_delete_assign_base &&) noexcept = + default; + optional_delete_assign_base & + operator=(const optional_delete_assign_base &) = default; + optional_delete_assign_base & + operator=(optional_delete_assign_base &&) noexcept = default; +}; + +template <class T> struct optional_delete_assign_base<T, true, false> { + optional_delete_assign_base() = default; + optional_delete_assign_base(const optional_delete_assign_base &) = default; + optional_delete_assign_base(optional_delete_assign_base &&) noexcept = + default; + optional_delete_assign_base & + operator=(const optional_delete_assign_base &) = default; + optional_delete_assign_base & + operator=(optional_delete_assign_base &&) noexcept = delete; +}; + +template <class T> struct optional_delete_assign_base<T, false, true> { + optional_delete_assign_base() = default; + optional_delete_assign_base(const optional_delete_assign_base &) = default; + optional_delete_assign_base(optional_delete_assign_base &&) noexcept = + default; + optional_delete_assign_base & + operator=(const optional_delete_assign_base &) = delete; + optional_delete_assign_base & + operator=(optional_delete_assign_base &&) noexcept = default; +}; + +template <class T> struct optional_delete_assign_base<T, false, false> { + optional_delete_assign_base() = default; + optional_delete_assign_base(const optional_delete_assign_base &) = default; + optional_delete_assign_base(optional_delete_assign_base &&) noexcept = + default; + optional_delete_assign_base & + operator=(const optional_delete_assign_base &) = delete; + optional_delete_assign_base & + operator=(optional_delete_assign_base &&) noexcept = delete; +}; + +} // namespace detail + +/// A tag type to represent an empty optional +struct nullopt_t { + struct do_not_use {}; + constexpr explicit nullopt_t(do_not_use, do_not_use) noexcept {} +}; +/// Represents an empty optional +static constexpr nullopt_t nullopt{nullopt_t::do_not_use{}, + nullopt_t::do_not_use{}}; + +class bad_optional_access : public std::exception { +public: + bad_optional_access() = default; + const char *what() const noexcept { return "Optional has no value"; } +}; + +/// An optional object is an object that contains the storage for another +/// object and manages the lifetime of this contained object, if any. The +/// contained object may be initialized after the optional object has been +/// initialized, and may be destroyed before the optional object has been +/// destroyed. The initialization state of the contained object is tracked by +/// the optional object. +template <class T> +class optional : private detail::optional_move_assign_base<T>, + private detail::optional_delete_ctor_base<T>, + private detail::optional_delete_assign_base<T> { + using base = detail::optional_move_assign_base<T>; + + static_assert(!std::is_same<T, in_place_t>::value, + "instantiation of optional with in_place_t is ill-formed"); + static_assert(!std::is_same<detail::decay_t<T>, nullopt_t>::value, + "instantiation of optional with nullopt_t is ill-formed"); + +public: +// The different versions for C++14 and 11 are needed because deduced return +// types are not SFINAE-safe. This provides better support for things like +// generic lambdas. C.f. +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0826r0.html +#if defined(TL_OPTIONAL_CXX14) && !defined(TL_OPTIONAL_GCC49) && \ + !defined(TL_OPTIONAL_GCC54) && !defined(TL_OPTIONAL_GCC55) + /// Carries out some operation which returns an optional on the stored + /// object if there is one. + template <class F> TL_OPTIONAL_11_CONSTEXPR auto and_then(F &&f) & { + using result = detail::invoke_result_t<F, T &>; + static_assert(detail::is_optional<result>::value, + "F must return an optional"); + + return has_value() ? detail::invoke(std::forward<F>(f), **this) + : result(nullopt); + } + + template <class F> TL_OPTIONAL_11_CONSTEXPR auto and_then(F &&f) && { + using result = detail::invoke_result_t<F, T &&>; + static_assert(detail::is_optional<result>::value, + "F must return an optional"); + + return has_value() ? detail::invoke(std::forward<F>(f), std::move(**this)) + : result(nullopt); + } + + template <class F> constexpr auto and_then(F &&f) const & { + using result = detail::invoke_result_t<F, const T &>; + static_assert(detail::is_optional<result>::value, + "F must return an optional"); + + return has_value() ? detail::invoke(std::forward<F>(f), **this) + : result(nullopt); + } + +#ifndef TL_OPTIONAL_NO_CONSTRR + template <class F> constexpr auto and_then(F &&f) const && { + using result = detail::invoke_result_t<F, const T &&>; + static_assert(detail::is_optional<result>::value, + "F must return an optional"); + + return has_value() ? detail::invoke(std::forward<F>(f), std::move(**this)) + : result(nullopt); + } +#endif +#else + /// Carries out some operation which returns an optional on the stored + /// object if there is one. + template <class F> + TL_OPTIONAL_11_CONSTEXPR detail::invoke_result_t<F, T &> and_then(F &&f) & { + using result = detail::invoke_result_t<F, T &>; + static_assert(detail::is_optional<result>::value, + "F must return an optional"); + + return has_value() ? detail::invoke(std::forward<F>(f), **this) + : result(nullopt); + } + + template <class F> + TL_OPTIONAL_11_CONSTEXPR detail::invoke_result_t<F, T &&> and_then(F &&f) && { + using result = detail::invoke_result_t<F, T &&>; + static_assert(detail::is_optional<result>::value, + "F must return an optional"); + + return has_value() ? detail::invoke(std::forward<F>(f), std::move(**this)) + : result(nullopt); + } + + template <class F> + constexpr detail::invoke_result_t<F, const T &> and_then(F &&f) const & { + using result = detail::invoke_result_t<F, const T &>; + static_assert(detail::is_optional<result>::value, + "F must return an optional"); + + return has_value() ? detail::invoke(std::forward<F>(f), **this) + : result(nullopt); + } + +#ifndef TL_OPTIONAL_NO_CONSTRR + template <class F> + constexpr detail::invoke_result_t<F, const T &&> and_then(F &&f) const && { + using result = detail::invoke_result_t<F, const T &&>; + static_assert(detail::is_optional<result>::value, + "F must return an optional"); + + return has_value() ? detail::invoke(std::forward<F>(f), std::move(**this)) + : result(nullopt); + } +#endif +#endif + +#if defined(TL_OPTIONAL_CXX14) && !defined(TL_OPTIONAL_GCC49) && \ + !defined(TL_OPTIONAL_GCC54) && !defined(TL_OPTIONAL_GCC55) + /// Carries out some operation on the stored object if there is one. + template <class F> TL_OPTIONAL_11_CONSTEXPR auto map(F &&f) & { + return optional_map_impl(*this, std::forward<F>(f)); + } + + template <class F> TL_OPTIONAL_11_CONSTEXPR auto map(F &&f) && { + return optional_map_impl(std::move(*this), std::forward<F>(f)); + } + + template <class F> constexpr auto map(F &&f) const & { + return optional_map_impl(*this, std::forward<F>(f)); + } + + template <class F> constexpr auto map(F &&f) const && { + return optional_map_impl(std::move(*this), std::forward<F>(f)); + } +#else + /// Carries out some operation on the stored object if there is one. + template <class F> + TL_OPTIONAL_11_CONSTEXPR decltype(optional_map_impl(std::declval<optional &>(), + std::declval<F &&>())) + map(F &&f) & { + return optional_map_impl(*this, std::forward<F>(f)); + } + + template <class F> + TL_OPTIONAL_11_CONSTEXPR decltype(optional_map_impl(std::declval<optional &&>(), + std::declval<F &&>())) + map(F &&f) && { + return optional_map_impl(std::move(*this), std::forward<F>(f)); + } + + template <class F> + constexpr decltype(optional_map_impl(std::declval<const optional &>(), + std::declval<F &&>())) + map(F &&f) const & { + return optional_map_impl(*this, std::forward<F>(f)); + } + +#ifndef TL_OPTIONAL_NO_CONSTRR + template <class F> + constexpr decltype(optional_map_impl(std::declval<const optional &&>(), + std::declval<F &&>())) + map(F &&f) const && { + return optional_map_impl(std::move(*this), std::forward<F>(f)); + } +#endif +#endif + +#if defined(TL_OPTIONAL_CXX14) && !defined(TL_OPTIONAL_GCC49) && \ + !defined(TL_OPTIONAL_GCC54) && !defined(TL_OPTIONAL_GCC55) + /// Carries out some operation on the stored object if there is one. + template <class F> TL_OPTIONAL_11_CONSTEXPR auto transform(F&& f) & { + return optional_map_impl(*this, std::forward<F>(f)); + } + + template <class F> TL_OPTIONAL_11_CONSTEXPR auto transform(F&& f) && { + return optional_map_impl(std::move(*this), std::forward<F>(f)); + } + + template <class F> constexpr auto transform(F&& f) const & { + return optional_map_impl(*this, std::forward<F>(f)); + } + + template <class F> constexpr auto transform(F&& f) const && { + return optional_map_impl(std::move(*this), std::forward<F>(f)); + } +#else + /// Carries out some operation on the stored object if there is one. + template <class F> + TL_OPTIONAL_11_CONSTEXPR decltype(optional_map_impl(std::declval<optional&>(), + std::declval<F&&>())) + transform(F&& f) & { + return optional_map_impl(*this, std::forward<F>(f)); + } + + template <class F> + TL_OPTIONAL_11_CONSTEXPR decltype(optional_map_impl(std::declval<optional&&>(), + std::declval<F&&>())) + transform(F&& f) && { + return optional_map_impl(std::move(*this), std::forward<F>(f)); + } + + template <class F> + constexpr decltype(optional_map_impl(std::declval<const optional&>(), + std::declval<F&&>())) + transform(F&& f) const & { + return optional_map_impl(*this, std::forward<F>(f)); + } + +#ifndef TL_OPTIONAL_NO_CONSTRR + template <class F> + constexpr decltype(optional_map_impl(std::declval<const optional&&>(), + std::declval<F&&>())) + transform(F&& f) const && { + return optional_map_impl(std::move(*this), std::forward<F>(f)); + } +#endif +#endif + + /// Calls `f` if the optional is empty + template <class F, detail::enable_if_ret_void<F> * = nullptr> + optional<T> TL_OPTIONAL_11_CONSTEXPR or_else(F &&f) & { + if (has_value()) + return *this; + + std::forward<F>(f)(); + return nullopt; + } + + template <class F, detail::disable_if_ret_void<F> * = nullptr> + optional<T> TL_OPTIONAL_11_CONSTEXPR or_else(F &&f) & { + return has_value() ? *this : std::forward<F>(f)(); + } + + template <class F, detail::enable_if_ret_void<F> * = nullptr> + optional<T> or_else(F &&f) && { + if (has_value()) + return std::move(*this); + + std::forward<F>(f)(); + return nullopt; + } + + template <class F, detail::disable_if_ret_void<F> * = nullptr> + optional<T> TL_OPTIONAL_11_CONSTEXPR or_else(F &&f) && { + return has_value() ? std::move(*this) : std::forward<F>(f)(); + } + + template <class F, detail::enable_if_ret_void<F> * = nullptr> + optional<T> or_else(F &&f) const & { + if (has_value()) + return *this; + + std::forward<F>(f)(); + return nullopt; + } + + template <class F, detail::disable_if_ret_void<F> * = nullptr> + optional<T> TL_OPTIONAL_11_CONSTEXPR or_else(F &&f) const & { + return has_value() ? *this : std::forward<F>(f)(); + } + +#ifndef TL_OPTIONAL_NO_CONSTRR + template <class F, detail::enable_if_ret_void<F> * = nullptr> + optional<T> or_else(F &&f) const && { + if (has_value()) + return std::move(*this); + + std::forward<F>(f)(); + return nullopt; + } + + template <class F, detail::disable_if_ret_void<F> * = nullptr> + optional<T> or_else(F &&f) const && { + return has_value() ? std::move(*this) : std::forward<F>(f)(); + } +#endif + + /// Maps the stored value with `f` if there is one, otherwise returns `u`. + template <class F, class U> U map_or(F &&f, U &&u) & { + return has_value() ? detail::invoke(std::forward<F>(f), **this) + : std::forward<U>(u); + } + + template <class F, class U> U map_or(F &&f, U &&u) && { + return has_value() ? detail::invoke(std::forward<F>(f), std::move(**this)) + : std::forward<U>(u); + } + + template <class F, class U> U map_or(F &&f, U &&u) const & { + return has_value() ? detail::invoke(std::forward<F>(f), **this) + : std::forward<U>(u); + } + +#ifndef TL_OPTIONAL_NO_CONSTRR + template <class F, class U> U map_or(F &&f, U &&u) const && { + return has_value() ? detail::invoke(std::forward<F>(f), std::move(**this)) + : std::forward<U>(u); + } +#endif + + /// Maps the stored value with `f` if there is one, otherwise calls + /// `u` and returns the result. + template <class F, class U> + detail::invoke_result_t<U> map_or_else(F &&f, U &&u) & { + return has_value() ? detail::invoke(std::forward<F>(f), **this) + : std::forward<U>(u)(); + } + + template <class F, class U> + detail::invoke_result_t<U> map_or_else(F &&f, U &&u) && { + return has_value() ? detail::invoke(std::forward<F>(f), std::move(**this)) + : std::forward<U>(u)(); + } + + template <class F, class U> + detail::invoke_result_t<U> map_or_else(F &&f, U &&u) const & { + return has_value() ? detail::invoke(std::forward<F>(f), **this) + : std::forward<U>(u)(); + } + +#ifndef TL_OPTIONAL_NO_CONSTRR + template <class F, class U> + detail::invoke_result_t<U> map_or_else(F &&f, U &&u) const && { + return has_value() ? detail::invoke(std::forward<F>(f), std::move(**this)) + : std::forward<U>(u)(); + } +#endif + + /// Returns `u` if `*this` has a value, otherwise an empty optional. + template <class U> + constexpr optional<typename std::decay<U>::type> conjunction(U &&u) const { + using result = optional<detail::decay_t<U>>; + return has_value() ? result{u} : result{nullopt}; + } + + /// Returns `rhs` if `*this` is empty, otherwise the current value. + TL_OPTIONAL_11_CONSTEXPR optional disjunction(const optional &rhs) & { + return has_value() ? *this : rhs; + } + + constexpr optional disjunction(const optional &rhs) const & { + return has_value() ? *this : rhs; + } + + TL_OPTIONAL_11_CONSTEXPR optional disjunction(const optional &rhs) && { + return has_value() ? std::move(*this) : rhs; + } + +#ifndef TL_OPTIONAL_NO_CONSTRR + constexpr optional disjunction(const optional &rhs) const && { + return has_value() ? std::move(*this) : rhs; + } +#endif + + TL_OPTIONAL_11_CONSTEXPR optional disjunction(optional &&rhs) & { + return has_value() ? *this : std::move(rhs); + } + + constexpr optional disjunction(optional &&rhs) const & { + return has_value() ? *this : std::move(rhs); + } + + TL_OPTIONAL_11_CONSTEXPR optional disjunction(optional &&rhs) && { + return has_value() ? std::move(*this) : std::move(rhs); + } + +#ifndef TL_OPTIONAL_NO_CONSTRR + constexpr optional disjunction(optional &&rhs) const && { + return has_value() ? std::move(*this) : std::move(rhs); + } +#endif + + /// Takes the value out of the optional, leaving it empty + optional take() { + optional ret = std::move(*this); + reset(); + return ret; + } + + using value_type = T; + + /// Constructs an optional that does not contain a value. + constexpr optional() noexcept = default; + + constexpr optional(nullopt_t) noexcept {} + + /// Copy constructor + /// + /// If `rhs` contains a value, the stored value is direct-initialized with + /// it. Otherwise, the constructed optional is empty. + TL_OPTIONAL_11_CONSTEXPR optional(const optional &rhs) = default; + + /// Move constructor + /// + /// If `rhs` contains a value, the stored value is direct-initialized with + /// it. Otherwise, the constructed optional is empty. + TL_OPTIONAL_11_CONSTEXPR optional(optional &&rhs) = default; + + /// Constructs the stored value in-place using the given arguments. + template <class... Args> + constexpr explicit optional( + detail::enable_if_t<std::is_constructible<T, Args...>::value, in_place_t>, + Args &&... args) + : base(in_place, std::forward<Args>(args)...) {} + + template <class U, class... Args> + TL_OPTIONAL_11_CONSTEXPR explicit optional( + detail::enable_if_t<std::is_constructible<T, std::initializer_list<U> &, + Args &&...>::value, + in_place_t>, + std::initializer_list<U> il, Args &&... args) { + this->construct(il, std::forward<Args>(args)...); + } + + /// Constructs the stored value with `u`. + template < + class U = T, + detail::enable_if_t<std::is_convertible<U &&, T>::value> * = nullptr, + detail::enable_forward_value<T, U> * = nullptr> + constexpr optional(U &&u) : base(in_place, std::forward<U>(u)) {} + + template < + class U = T, + detail::enable_if_t<!std::is_convertible<U &&, T>::value> * = nullptr, + detail::enable_forward_value<T, U> * = nullptr> + constexpr explicit optional(U &&u) : base(in_place, std::forward<U>(u)) {} + + /// Converting copy constructor. + template < + class U, detail::enable_from_other<T, U, const U &> * = nullptr, + detail::enable_if_t<std::is_convertible<const U &, T>::value> * = nullptr> + optional(const optional<U> &rhs) { + if (rhs.has_value()) { + this->construct(*rhs); + } + } + + template <class U, detail::enable_from_other<T, U, const U &> * = nullptr, + detail::enable_if_t<!std::is_convertible<const U &, T>::value> * = + nullptr> + explicit optional(const optional<U> &rhs) { + if (rhs.has_value()) { + this->construct(*rhs); + } + } + + /// Converting move constructor. + template < + class U, detail::enable_from_other<T, U, U &&> * = nullptr, + detail::enable_if_t<std::is_convertible<U &&, T>::value> * = nullptr> + optional(optional<U> &&rhs) { + if (rhs.has_value()) { + this->construct(std::move(*rhs)); + } + } + + template < + class U, detail::enable_from_other<T, U, U &&> * = nullptr, + detail::enable_if_t<!std::is_convertible<U &&, T>::value> * = nullptr> + explicit optional(optional<U> &&rhs) { + if (rhs.has_value()) { + this->construct(std::move(*rhs)); + } + } + + /// Destroys the stored value if there is one. + ~optional() = default; + + /// Assignment to empty. + /// + /// Destroys the current value if there is one. + optional &operator=(nullopt_t) noexcept { + if (has_value()) { + this->m_value.~T(); + this->m_has_value = false; + } + + return *this; + } + + /// Copy assignment. + /// + /// Copies the value from `rhs` if there is one. Otherwise resets the stored + /// value in `*this`. + optional &operator=(const optional &rhs) = default; + + /// Move assignment. + /// + /// Moves the value from `rhs` if there is one. Otherwise resets the stored + /// value in `*this`. + optional &operator=(optional &&rhs) = default; + + /// Assigns the stored value from `u`, destroying the old value if there was + /// one. + template <class U = T, detail::enable_assign_forward<T, U> * = nullptr> + optional &operator=(U &&u) { + if (has_value()) { + this->m_value = std::forward<U>(u); + } else { + this->construct(std::forward<U>(u)); + } + + return *this; + } + + /// Converting copy assignment operator. + /// + /// Copies the value from `rhs` if there is one. Otherwise resets the stored + /// value in `*this`. + template <class U, + detail::enable_assign_from_other<T, U, const U &> * = nullptr> + optional &operator=(const optional<U> &rhs) { + if (has_value()) { + if (rhs.has_value()) { + this->m_value = *rhs; + } else { + this->hard_reset(); + } + } + + if (rhs.has_value()) { + this->construct(*rhs); + } + + return *this; + } + + // TODO check exception guarantee + /// Converting move assignment operator. + /// + /// Moves the value from `rhs` if there is one. Otherwise resets the stored + /// value in `*this`. + template <class U, detail::enable_assign_from_other<T, U, U> * = nullptr> + optional &operator=(optional<U> &&rhs) { + if (has_value()) { + if (rhs.has_value()) { + this->m_value = std::move(*rhs); + } else { + this->hard_reset(); + } + } + + if (rhs.has_value()) { + this->construct(std::move(*rhs)); + } + + return *this; + } + + /// Constructs the value in-place, destroying the current one if there is + /// one. + template <class... Args> T &emplace(Args &&... args) { + static_assert(std::is_constructible<T, Args &&...>::value, + "T must be constructible with Args"); + + *this = nullopt; + this->construct(std::forward<Args>(args)...); + return value(); + } + + template <class U, class... Args> + detail::enable_if_t< + std::is_constructible<T, std::initializer_list<U> &, Args &&...>::value, + T &> + emplace(std::initializer_list<U> il, Args &&... args) { + *this = nullopt; + this->construct(il, std::forward<Args>(args)...); + return value(); + } + + /// Swaps this optional with the other. + /// + /// If neither optionals have a value, nothing happens. + /// If both have a value, the values are swapped. + /// If one has a value, it is moved to the other and the movee is left + /// valueless. + void + swap(optional &rhs) noexcept(std::is_nothrow_move_constructible<T>::value + &&detail::is_nothrow_swappable<T>::value) { + using std::swap; + if (has_value()) { + if (rhs.has_value()) { + swap(**this, *rhs); + } else { + new (std::addressof(rhs.m_value)) T(std::move(this->m_value)); + this->m_value.T::~T(); + } + } else if (rhs.has_value()) { + new (std::addressof(this->m_value)) T(std::move(rhs.m_value)); + rhs.m_value.T::~T(); + } + swap(this->m_has_value, rhs.m_has_value); + } + + /// Returns a pointer to the stored value + constexpr const T *operator->() const { + return std::addressof(this->m_value); + } + + TL_OPTIONAL_11_CONSTEXPR T *operator->() { + return std::addressof(this->m_value); + } + + /// Returns the stored value + TL_OPTIONAL_11_CONSTEXPR T &operator*() & { return this->m_value; } + + constexpr const T &operator*() const & { return this->m_value; } + + TL_OPTIONAL_11_CONSTEXPR T &&operator*() && { + return std::move(this->m_value); + } + +#ifndef TL_OPTIONAL_NO_CONSTRR + constexpr const T &&operator*() const && { return std::move(this->m_value); } +#endif + + /// Returns whether or not the optional has a value + constexpr bool has_value() const noexcept { return this->m_has_value; } + + constexpr explicit operator bool() const noexcept { + return this->m_has_value; + } + + /// Returns the contained value if there is one, otherwise throws bad_optional_access + TL_OPTIONAL_11_CONSTEXPR T &value() & { + if (has_value()) + return this->m_value; + throw bad_optional_access(); + } + TL_OPTIONAL_11_CONSTEXPR const T &value() const & { + if (has_value()) + return this->m_value; + throw bad_optional_access(); + } + TL_OPTIONAL_11_CONSTEXPR T &&value() && { + if (has_value()) + return std::move(this->m_value); + throw bad_optional_access(); + } + +#ifndef TL_OPTIONAL_NO_CONSTRR + TL_OPTIONAL_11_CONSTEXPR const T &&value() const && { + if (has_value()) + return std::move(this->m_value); + throw bad_optional_access(); + } +#endif + + /// Returns the stored value if there is one, otherwise returns `u` + template <class U> constexpr T value_or(U &&u) const & { + static_assert(std::is_copy_constructible<T>::value && + std::is_convertible<U &&, T>::value, + "T must be copy constructible and convertible from U"); + return has_value() ? **this : static_cast<T>(std::forward<U>(u)); + } + + template <class U> TL_OPTIONAL_11_CONSTEXPR T value_or(U &&u) && { + static_assert(std::is_move_constructible<T>::value && + std::is_convertible<U &&, T>::value, + "T must be move constructible and convertible from U"); + return has_value() ? **this : static_cast<T>(std::forward<U>(u)); + } + + /// Destroys the stored value if one exists, making the optional empty + void reset() noexcept { + if (has_value()) { + this->m_value.~T(); + this->m_has_value = false; + } + } +}; // namespace tl + +/// Compares two optional objects +template <class T, class U> +inline constexpr bool operator==(const optional<T> &lhs, + const optional<U> &rhs) { + return lhs.has_value() == rhs.has_value() && + (!lhs.has_value() || *lhs == *rhs); +} +template <class T, class U> +inline constexpr bool operator!=(const optional<T> &lhs, + const optional<U> &rhs) { + return lhs.has_value() != rhs.has_value() || + (lhs.has_value() && *lhs != *rhs); +} +template <class T, class U> +inline constexpr bool operator<(const optional<T> &lhs, + const optional<U> &rhs) { + return rhs.has_value() && (!lhs.has_value() || *lhs < *rhs); +} +template <class T, class U> +inline constexpr bool operator>(const optional<T> &lhs, + const optional<U> &rhs) { + return lhs.has_value() && (!rhs.has_value() || *lhs > *rhs); +} +template <class T, class U> +inline constexpr bool operator<=(const optional<T> &lhs, + const optional<U> &rhs) { + return !lhs.has_value() || (rhs.has_value() && *lhs <= *rhs); +} +template <class T, class U> +inline constexpr bool operator>=(const optional<T> &lhs, + const optional<U> &rhs) { + return !rhs.has_value() || (lhs.has_value() && *lhs >= *rhs); +} + +/// Compares an optional to a `nullopt` +template <class T> +inline constexpr bool operator==(const optional<T> &lhs, nullopt_t) noexcept { + return !lhs.has_value(); +} +template <class T> +inline constexpr bool operator==(nullopt_t, const optional<T> &rhs) noexcept { + return !rhs.has_value(); +} +template <class T> +inline constexpr bool operator!=(const optional<T> &lhs, nullopt_t) noexcept { + return lhs.has_value(); +} +template <class T> +inline constexpr bool operator!=(nullopt_t, const optional<T> &rhs) noexcept { + return rhs.has_value(); +} +template <class T> +inline constexpr bool operator<(const optional<T> &, nullopt_t) noexcept { + return false; +} +template <class T> +inline constexpr bool operator<(nullopt_t, const optional<T> &rhs) noexcept { + return rhs.has_value(); +} +template <class T> +inline constexpr bool operator<=(const optional<T> &lhs, nullopt_t) noexcept { + return !lhs.has_value(); +} +template <class T> +inline constexpr bool operator<=(nullopt_t, const optional<T> &) noexcept { + return true; +} +template <class T> +inline constexpr bool operator>(const optional<T> &lhs, nullopt_t) noexcept { + return lhs.has_value(); +} +template <class T> +inline constexpr bool operator>(nullopt_t, const optional<T> &) noexcept { + return false; +} +template <class T> +inline constexpr bool operator>=(const optional<T> &, nullopt_t) noexcept { + return true; +} +template <class T> +inline constexpr bool operator>=(nullopt_t, const optional<T> &rhs) noexcept { + return !rhs.has_value(); +} + +/// Compares the optional with a value. +template <class T, class U> +inline constexpr bool operator==(const optional<T> &lhs, const U &rhs) { + return lhs.has_value() ? *lhs == rhs : false; +} +template <class T, class U> +inline constexpr bool operator==(const U &lhs, const optional<T> &rhs) { + return rhs.has_value() ? lhs == *rhs : false; +} +template <class T, class U> +inline constexpr bool operator!=(const optional<T> &lhs, const U &rhs) { + return lhs.has_value() ? *lhs != rhs : true; +} +template <class T, class U> +inline constexpr bool operator!=(const U &lhs, const optional<T> &rhs) { + return rhs.has_value() ? lhs != *rhs : true; +} +template <class T, class U> +inline constexpr bool operator<(const optional<T> &lhs, const U &rhs) { + return lhs.has_value() ? *lhs < rhs : true; +} +template <class T, class U> +inline constexpr bool operator<(const U &lhs, const optional<T> &rhs) { + return rhs.has_value() ? lhs < *rhs : false; +} +template <class T, class U> +inline constexpr bool operator<=(const optional<T> &lhs, const U &rhs) { + return lhs.has_value() ? *lhs <= rhs : true; +} +template <class T, class U> +inline constexpr bool operator<=(const U &lhs, const optional<T> &rhs) { + return rhs.has_value() ? lhs <= *rhs : false; +} +template <class T, class U> +inline constexpr bool operator>(const optional<T> &lhs, const U &rhs) { + return lhs.has_value() ? *lhs > rhs : false; +} +template <class T, class U> +inline constexpr bool operator>(const U &lhs, const optional<T> &rhs) { + return rhs.has_value() ? lhs > *rhs : true; +} +template <class T, class U> +inline constexpr bool operator>=(const optional<T> &lhs, const U &rhs) { + return lhs.has_value() ? *lhs >= rhs : false; +} +template <class T, class U> +inline constexpr bool operator>=(const U &lhs, const optional<T> &rhs) { + return rhs.has_value() ? lhs >= *rhs : true; +} + +template <class T, + detail::enable_if_t<std::is_move_constructible<T>::value> * = nullptr, + detail::enable_if_t<detail::is_swappable<T>::value> * = nullptr> +void swap(optional<T> &lhs, + optional<T> &rhs) noexcept(noexcept(lhs.swap(rhs))) { + return lhs.swap(rhs); +} + +namespace detail { +struct i_am_secret {}; +} // namespace detail + +template <class T = detail::i_am_secret, class U, + class Ret = + detail::conditional_t<std::is_same<T, detail::i_am_secret>::value, + detail::decay_t<U>, T>> +inline constexpr optional<Ret> make_optional(U &&v) { + return optional<Ret>(std::forward<U>(v)); +} + +template <class T, class... Args> +inline constexpr optional<T> make_optional(Args &&... args) { + return optional<T>(in_place, std::forward<Args>(args)...); +} +template <class T, class U, class... Args> +inline constexpr optional<T> make_optional(std::initializer_list<U> il, + Args &&... args) { + return optional<T>(in_place, il, std::forward<Args>(args)...); +} + +#if __cplusplus >= 201703L +template <class T> optional(T)->optional<T>; +#endif + +/// \exclude +namespace detail { +#ifdef TL_OPTIONAL_CXX14 +template <class Opt, class F, + class Ret = decltype(detail::invoke(std::declval<F>(), + *std::declval<Opt>())), + detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr> +constexpr auto optional_map_impl(Opt &&opt, F &&f) { + return opt.has_value() + ? detail::invoke(std::forward<F>(f), *std::forward<Opt>(opt)) + : optional<Ret>(nullopt); +} + +template <class Opt, class F, + class Ret = decltype(detail::invoke(std::declval<F>(), + *std::declval<Opt>())), + detail::enable_if_t<std::is_void<Ret>::value> * = nullptr> +auto optional_map_impl(Opt &&opt, F &&f) { + if (opt.has_value()) { + detail::invoke(std::forward<F>(f), *std::forward<Opt>(opt)); + return make_optional(monostate{}); + } + + return optional<monostate>(nullopt); +} +#else +template <class Opt, class F, + class Ret = decltype(detail::invoke(std::declval<F>(), + *std::declval<Opt>())), + detail::enable_if_t<!std::is_void<Ret>::value> * = nullptr> + +constexpr auto optional_map_impl(Opt &&opt, F &&f) -> optional<Ret> { + return opt.has_value() + ? detail::invoke(std::forward<F>(f), *std::forward<Opt>(opt)) + : optional<Ret>(nullopt); +} + +template <class Opt, class F, + class Ret = decltype(detail::invoke(std::declval<F>(), + *std::declval<Opt>())), + detail::enable_if_t<std::is_void<Ret>::value> * = nullptr> + +auto optional_map_impl(Opt &&opt, F &&f) -> optional<monostate> { + if (opt.has_value()) { + detail::invoke(std::forward<F>(f), *std::forward<Opt>(opt)); + return monostate{}; + } + + return nullopt; +} +#endif +} // namespace detail + +/// Specialization for when `T` is a reference. `optional<T&>` acts similarly +/// to a `T*`, but provides more operations and shows intent more clearly. +template <class T> class optional<T &> { +public: +// The different versions for C++14 and 11 are needed because deduced return +// types are not SFINAE-safe. This provides better support for things like +// generic lambdas. C.f. +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0826r0.html +#if defined(TL_OPTIONAL_CXX14) && !defined(TL_OPTIONAL_GCC49) && \ + !defined(TL_OPTIONAL_GCC54) && !defined(TL_OPTIONAL_GCC55) + + /// Carries out some operation which returns an optional on the stored + /// object if there is one. + template <class F> TL_OPTIONAL_11_CONSTEXPR auto and_then(F &&f) & { + using result = detail::invoke_result_t<F, T &>; + static_assert(detail::is_optional<result>::value, + "F must return an optional"); + + return has_value() ? detail::invoke(std::forward<F>(f), **this) + : result(nullopt); + } + + template <class F> TL_OPTIONAL_11_CONSTEXPR auto and_then(F &&f) && { + using result = detail::invoke_result_t<F, T &>; + static_assert(detail::is_optional<result>::value, + "F must return an optional"); + + return has_value() ? detail::invoke(std::forward<F>(f), **this) + : result(nullopt); + } + + template <class F> constexpr auto and_then(F &&f) const & { + using result = detail::invoke_result_t<F, const T &>; + static_assert(detail::is_optional<result>::value, + "F must return an optional"); + + return has_value() ? detail::invoke(std::forward<F>(f), **this) + : result(nullopt); + } + +#ifndef TL_OPTIONAL_NO_CONSTRR + template <class F> constexpr auto and_then(F &&f) const && { + using result = detail::invoke_result_t<F, const T &>; + static_assert(detail::is_optional<result>::value, + "F must return an optional"); + + return has_value() ? detail::invoke(std::forward<F>(f), **this) + : result(nullopt); + } +#endif +#else + /// Carries out some operation which returns an optional on the stored + /// object if there is one. + template <class F> + TL_OPTIONAL_11_CONSTEXPR detail::invoke_result_t<F, T &> and_then(F &&f) & { + using result = detail::invoke_result_t<F, T &>; + static_assert(detail::is_optional<result>::value, + "F must return an optional"); + + return has_value() ? detail::invoke(std::forward<F>(f), **this) + : result(nullopt); + } + + template <class F> + TL_OPTIONAL_11_CONSTEXPR detail::invoke_result_t<F, T &> and_then(F &&f) && { + using result = detail::invoke_result_t<F, T &>; + static_assert(detail::is_optional<result>::value, + "F must return an optional"); + + return has_value() ? detail::invoke(std::forward<F>(f), **this) + : result(nullopt); + } + + template <class F> + constexpr detail::invoke_result_t<F, const T &> and_then(F &&f) const & { + using result = detail::invoke_result_t<F, const T &>; + static_assert(detail::is_optional<result>::value, + "F must return an optional"); + + return has_value() ? detail::invoke(std::forward<F>(f), **this) + : result(nullopt); + } + +#ifndef TL_OPTIONAL_NO_CONSTRR + template <class F> + constexpr detail::invoke_result_t<F, const T &> and_then(F &&f) const && { + using result = detail::invoke_result_t<F, const T &>; + static_assert(detail::is_optional<result>::value, + "F must return an optional"); + + return has_value() ? detail::invoke(std::forward<F>(f), **this) + : result(nullopt); + } +#endif +#endif + +#if defined(TL_OPTIONAL_CXX14) && !defined(TL_OPTIONAL_GCC49) && \ + !defined(TL_OPTIONAL_GCC54) && !defined(TL_OPTIONAL_GCC55) + /// Carries out some operation on the stored object if there is one. + template <class F> TL_OPTIONAL_11_CONSTEXPR auto map(F &&f) & { + return detail::optional_map_impl(*this, std::forward<F>(f)); + } + + template <class F> TL_OPTIONAL_11_CONSTEXPR auto map(F &&f) && { + return detail::optional_map_impl(std::move(*this), std::forward<F>(f)); + } + + template <class F> constexpr auto map(F &&f) const & { + return detail::optional_map_impl(*this, std::forward<F>(f)); + } + + template <class F> constexpr auto map(F &&f) const && { + return detail::optional_map_impl(std::move(*this), std::forward<F>(f)); + } +#else + /// Carries out some operation on the stored object if there is one. + template <class F> + TL_OPTIONAL_11_CONSTEXPR decltype(detail::optional_map_impl(std::declval<optional &>(), + std::declval<F &&>())) + map(F &&f) & { + return detail::optional_map_impl(*this, std::forward<F>(f)); + } + + template <class F> + TL_OPTIONAL_11_CONSTEXPR decltype(detail::optional_map_impl(std::declval<optional &&>(), + std::declval<F &&>())) + map(F &&f) && { + return detail::optional_map_impl(std::move(*this), std::forward<F>(f)); + } + + template <class F> + constexpr decltype(detail::optional_map_impl(std::declval<const optional &>(), + std::declval<F &&>())) + map(F &&f) const & { + return detail::optional_map_impl(*this, std::forward<F>(f)); + } + +#ifndef TL_OPTIONAL_NO_CONSTRR + template <class F> + constexpr decltype(detail::optional_map_impl(std::declval<const optional &&>(), + std::declval<F &&>())) + map(F &&f) const && { + return detail::optional_map_impl(std::move(*this), std::forward<F>(f)); + } +#endif +#endif + +#if defined(TL_OPTIONAL_CXX14) && !defined(TL_OPTIONAL_GCC49) && \ + !defined(TL_OPTIONAL_GCC54) && !defined(TL_OPTIONAL_GCC55) + /// Carries out some operation on the stored object if there is one. + template <class F> TL_OPTIONAL_11_CONSTEXPR auto transform(F&& f) & { + return detail::optional_map_impl(*this, std::forward<F>(f)); + } + + template <class F> TL_OPTIONAL_11_CONSTEXPR auto transform(F&& f) && { + return detail::optional_map_impl(std::move(*this), std::forward<F>(f)); + } + + template <class F> constexpr auto transform(F&& f) const & { + return detail::optional_map_impl(*this, std::forward<F>(f)); + } + + template <class F> constexpr auto transform(F&& f) const && { + return detail::optional_map_impl(std::move(*this), std::forward<F>(f)); + } +#else + /// Carries out some operation on the stored object if there is one. + template <class F> + TL_OPTIONAL_11_CONSTEXPR decltype(detail::optional_map_impl(std::declval<optional&>(), + std::declval<F&&>())) + transform(F&& f) & { + return detail::optional_map_impl(*this, std::forward<F>(f)); + } + + /// \group map + /// \synopsis template <class F> auto transform(F &&f) &&; + template <class F> + TL_OPTIONAL_11_CONSTEXPR decltype(detail::optional_map_impl(std::declval<optional&&>(), + std::declval<F&&>())) + transform(F&& f) && { + return detail::optional_map_impl(std::move(*this), std::forward<F>(f)); + } + + template <class F> + constexpr decltype(detail::optional_map_impl(std::declval<const optional&>(), + std::declval<F&&>())) + transform(F&& f) const & { + return detail::optional_map_impl(*this, std::forward<F>(f)); + } + +#ifndef TL_OPTIONAL_NO_CONSTRR + template <class F> + constexpr decltype(detail::optional_map_impl(std::declval<const optional&&>(), + std::declval<F&&>())) + transform(F&& f) const && { + return detail::optional_map_impl(std::move(*this), std::forward<F>(f)); + } +#endif +#endif + + /// Calls `f` if the optional is empty + template <class F, detail::enable_if_ret_void<F> * = nullptr> + optional<T> TL_OPTIONAL_11_CONSTEXPR or_else(F &&f) & { + if (has_value()) + return *this; + + std::forward<F>(f)(); + return nullopt; + } + + template <class F, detail::disable_if_ret_void<F> * = nullptr> + optional<T> TL_OPTIONAL_11_CONSTEXPR or_else(F &&f) & { + return has_value() ? *this : std::forward<F>(f)(); + } + + template <class F, detail::enable_if_ret_void<F> * = nullptr> + optional<T> or_else(F &&f) && { + if (has_value()) + return std::move(*this); + + std::forward<F>(f)(); + return nullopt; + } + + template <class F, detail::disable_if_ret_void<F> * = nullptr> + optional<T> TL_OPTIONAL_11_CONSTEXPR or_else(F &&f) && { + return has_value() ? std::move(*this) : std::forward<F>(f)(); + } + + template <class F, detail::enable_if_ret_void<F> * = nullptr> + optional<T> or_else(F &&f) const & { + if (has_value()) + return *this; + + std::forward<F>(f)(); + return nullopt; + } + + template <class F, detail::disable_if_ret_void<F> * = nullptr> + optional<T> TL_OPTIONAL_11_CONSTEXPR or_else(F &&f) const & { + return has_value() ? *this : std::forward<F>(f)(); + } + +#ifndef TL_OPTIONAL_NO_CONSTRR + template <class F, detail::enable_if_ret_void<F> * = nullptr> + optional<T> or_else(F &&f) const && { + if (has_value()) + return std::move(*this); + + std::forward<F>(f)(); + return nullopt; + } + + template <class F, detail::disable_if_ret_void<F> * = nullptr> + optional<T> or_else(F &&f) const && { + return has_value() ? std::move(*this) : std::forward<F>(f)(); + } +#endif + + /// Maps the stored value with `f` if there is one, otherwise returns `u` + template <class F, class U> U map_or(F &&f, U &&u) & { + return has_value() ? detail::invoke(std::forward<F>(f), **this) + : std::forward<U>(u); + } + + template <class F, class U> U map_or(F &&f, U &&u) && { + return has_value() ? detail::invoke(std::forward<F>(f), std::move(**this)) + : std::forward<U>(u); + } + + template <class F, class U> U map_or(F &&f, U &&u) const & { + return has_value() ? detail::invoke(std::forward<F>(f), **this) + : std::forward<U>(u); + } + +#ifndef TL_OPTIONAL_NO_CONSTRR + template <class F, class U> U map_or(F &&f, U &&u) const && { + return has_value() ? detail::invoke(std::forward<F>(f), std::move(**this)) + : std::forward<U>(u); + } +#endif + + /// Maps the stored value with `f` if there is one, otherwise calls + /// `u` and returns the result. + template <class F, class U> + detail::invoke_result_t<U> map_or_else(F &&f, U &&u) & { + return has_value() ? detail::invoke(std::forward<F>(f), **this) + : std::forward<U>(u)(); + } + + template <class F, class U> + detail::invoke_result_t<U> map_or_else(F &&f, U &&u) && { + return has_value() ? detail::invoke(std::forward<F>(f), std::move(**this)) + : std::forward<U>(u)(); + } + + template <class F, class U> + detail::invoke_result_t<U> map_or_else(F &&f, U &&u) const & { + return has_value() ? detail::invoke(std::forward<F>(f), **this) + : std::forward<U>(u)(); + } + +#ifndef TL_OPTIONAL_NO_CONSTRR + template <class F, class U> + detail::invoke_result_t<U> map_or_else(F &&f, U &&u) const && { + return has_value() ? detail::invoke(std::forward<F>(f), std::move(**this)) + : std::forward<U>(u)(); + } +#endif + + /// Returns `u` if `*this` has a value, otherwise an empty optional. + template <class U> + constexpr optional<typename std::decay<U>::type> conjunction(U &&u) const { + using result = optional<detail::decay_t<U>>; + return has_value() ? result{u} : result{nullopt}; + } + + /// Returns `rhs` if `*this` is empty, otherwise the current value. + TL_OPTIONAL_11_CONSTEXPR optional disjunction(const optional &rhs) & { + return has_value() ? *this : rhs; + } + + constexpr optional disjunction(const optional &rhs) const & { + return has_value() ? *this : rhs; + } + + TL_OPTIONAL_11_CONSTEXPR optional disjunction(const optional &rhs) && { + return has_value() ? std::move(*this) : rhs; + } + +#ifndef TL_OPTIONAL_NO_CONSTRR + constexpr optional disjunction(const optional &rhs) const && { + return has_value() ? std::move(*this) : rhs; + } +#endif + + TL_OPTIONAL_11_CONSTEXPR optional disjunction(optional &&rhs) & { + return has_value() ? *this : std::move(rhs); + } + + constexpr optional disjunction(optional &&rhs) const & { + return has_value() ? *this : std::move(rhs); + } + + TL_OPTIONAL_11_CONSTEXPR optional disjunction(optional &&rhs) && { + return has_value() ? std::move(*this) : std::move(rhs); + } + +#ifndef TL_OPTIONAL_NO_CONSTRR + constexpr optional disjunction(optional &&rhs) const && { + return has_value() ? std::move(*this) : std::move(rhs); + } +#endif + + /// Takes the value out of the optional, leaving it empty + optional take() { + optional ret = std::move(*this); + reset(); + return ret; + } + + using value_type = T &; + + /// Constructs an optional that does not contain a value. + constexpr optional() noexcept : m_value(nullptr) {} + + constexpr optional(nullopt_t) noexcept : m_value(nullptr) {} + + /// Copy constructor + /// + /// If `rhs` contains a value, the stored value is direct-initialized with + /// it. Otherwise, the constructed optional is empty. + TL_OPTIONAL_11_CONSTEXPR optional(const optional &rhs) noexcept = default; + + /// Move constructor + /// + /// If `rhs` contains a value, the stored value is direct-initialized with + /// it. Otherwise, the constructed optional is empty. + TL_OPTIONAL_11_CONSTEXPR optional(optional &&rhs) = default; + + /// Constructs the stored value with `u`. + template <class U = T, + detail::enable_if_t<!detail::is_optional<detail::decay_t<U>>::value> + * = nullptr> + constexpr optional(U &&u) noexcept : m_value(std::addressof(u)) { + static_assert(std::is_lvalue_reference<U>::value, "U must be an lvalue"); + } + + template <class U> + constexpr explicit optional(const optional<U> &rhs) noexcept : optional(*rhs) {} + + /// No-op + ~optional() = default; + + /// Assignment to empty. + /// + /// Destroys the current value if there is one. + optional &operator=(nullopt_t) noexcept { + m_value = nullptr; + return *this; + } + + /// Copy assignment. + /// + /// Rebinds this optional to the referee of `rhs` if there is one. Otherwise + /// resets the stored value in `*this`. + optional &operator=(const optional &rhs) = default; + + /// Rebinds this optional to `u`. + template <class U = T, + detail::enable_if_t<!detail::is_optional<detail::decay_t<U>>::value> + * = nullptr> + optional &operator=(U &&u) { + static_assert(std::is_lvalue_reference<U>::value, "U must be an lvalue"); + m_value = std::addressof(u); + return *this; + } + + /// Converting copy assignment operator. + /// + /// Rebinds this optional to the referee of `rhs` if there is one. Otherwise + /// resets the stored value in `*this`. + template <class U> optional &operator=(const optional<U> &rhs) noexcept { + m_value = std::addressof(rhs.value()); + return *this; + } + + /// Rebinds this optional to `u`. + template <class U = T, + detail::enable_if_t<!detail::is_optional<detail::decay_t<U>>::value> + * = nullptr> + optional &emplace(U &&u) noexcept { + return *this = std::forward<U>(u); + } + + void swap(optional &rhs) noexcept { std::swap(m_value, rhs.m_value); } + + /// Returns a pointer to the stored value + constexpr const T *operator->() const noexcept { return m_value; } + + TL_OPTIONAL_11_CONSTEXPR T *operator->() noexcept { return m_value; } + + /// Returns the stored value + TL_OPTIONAL_11_CONSTEXPR T &operator*() noexcept { return *m_value; } + + constexpr const T &operator*() const noexcept { return *m_value; } + + constexpr bool has_value() const noexcept { return m_value != nullptr; } + + constexpr explicit operator bool() const noexcept { + return m_value != nullptr; + } + + /// Returns the contained value if there is one, otherwise throws bad_optional_access + TL_OPTIONAL_11_CONSTEXPR T &value() { + if (has_value()) + return *m_value; + throw bad_optional_access(); + } + TL_OPTIONAL_11_CONSTEXPR const T &value() const { + if (has_value()) + return *m_value; + throw bad_optional_access(); + } + + /// Returns the stored value if there is one, otherwise returns `u` + template <class U> constexpr T value_or(U &&u) const & noexcept { + static_assert(std::is_copy_constructible<T>::value && + std::is_convertible<U &&, T>::value, + "T must be copy constructible and convertible from U"); + return has_value() ? **this : static_cast<T>(std::forward<U>(u)); + } + + /// \group value_or + template <class U> TL_OPTIONAL_11_CONSTEXPR T value_or(U &&u) && noexcept { + static_assert(std::is_move_constructible<T>::value && + std::is_convertible<U &&, T>::value, + "T must be move constructible and convertible from U"); + return has_value() ? **this : static_cast<T>(std::forward<U>(u)); + } + + /// Destroys the stored value if one exists, making the optional empty + void reset() noexcept { m_value = nullptr; } + +private: + T *m_value; +}; // namespace tl + + + +} // namespace tl + +namespace std { +// TODO SFINAE +template <class T> struct hash<tl::optional<T>> { + ::std::size_t operator()(const tl::optional<T> &o) const { + if (!o.has_value()) + return 0; + + return std::hash<tl::detail::remove_const_t<T>>()(*o); + } +}; +} // namespace std + +#endif |
