///
// optional - An implementation of std::optional with extensions
// Written in 2017 by Sy Brand (tartanllama@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
// .
///
#ifndef TL_OPTIONAL_HPP
#define TL_OPTIONAL_HPP
#define TL_OPTIONAL_VERSION_MAJOR 1
#define TL_OPTIONAL_VERSION_MINOR 1
#define TL_OPTIONAL_VERSION_PATCH 0
#include
#include
#include
#include
#include
#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::value
#define TL_OPTIONAL_IS_TRIVIALLY_COPY_ASSIGNABLE(T) std::has_trivial_copy_assign::value
// This one will be different for GCC 5.7 if it's ever supported
#define TL_OPTIONAL_IS_TRIVIALLY_DESTRUCTIBLE(T) AuIsTriviallyDestructible::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
struct is_trivially_copy_constructible : std::is_trivially_copy_constructible
{ };
#ifdef _GLIBCXX_VECTOR
template
struct is_trivially_copy_constructible>
: std::is_trivially_copy_constructible
{ };
#endif
}
}
#endif
#define TL_OPTIONAL_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \
tl::detail::is_trivially_copy_constructible::value
#define TL_OPTIONAL_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \
std::is_trivially_copy_assignable::value
#define TL_OPTIONAL_IS_TRIVIALLY_DESTRUCTIBLE(T) AuIsTriviallyDestructible::value
#else
#define TL_OPTIONAL_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \
AuIsTriviallyCopyConstructible::value
#define TL_OPTIONAL_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \
AuIsTriviallyCopyAssignable::value
#define TL_OPTIONAL_IS_TRIVIALLY_DESTRUCTIBLE(T) AuIsTriviallyDestructible::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 optional;
namespace detail {
#ifndef TL_TRAITS_MUTEX
#define TL_TRAITS_MUTEX
// C++14-style aliases for brevity
template using remove_const_t = typename AuRemoveConst::type;
template
using remove_reference_t = typename AuRemoveReference::type;
template using decay_t = typename AuDecay::type;
template
using enable_if_t = typename AuEnableIf::type;
template
using conditional_t = typename AuConditional::type;
// std::conjunction from C++17
template struct conjunction : AuTrueType
{ };
template struct conjunction : B
{ };
template
struct conjunction
: AuConditional, 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 struct is_pointer_to_non_const_member_func : AuFalseType
{ };
template
struct is_pointer_to_non_const_member_func : AuTrueType
{ };
template
struct is_pointer_to_non_const_member_func : AuTrueType
{ };
template
struct is_pointer_to_non_const_member_func : AuTrueType
{ };
template
struct is_pointer_to_non_const_member_func : AuTrueType
{ };
template
struct is_pointer_to_non_const_member_func : AuTrueType
{ };
template
struct is_pointer_to_non_const_member_func : AuTrueType
{ };
template struct is_const_or_const_ref : AuFalseType
{ };
template struct is_const_or_const_ref : AuTrueType
{ };
template struct is_const_or_const_ref : AuTrueType
{ };
#endif
// std::invoke from C++17
// https://stackoverflow.com/questions/38288042/c11-14-invoke-workaround
template ::value
&&is_const_or_const_ref::value)>,
#endif
typename = enable_if_t>::value>,
int = 0>
constexpr auto invoke(Fn &&f, Args &&... args) noexcept(
noexcept(std::mem_fn(f)(AuForward(args)...)))
-> decltype(std::mem_fn(f)(AuForward(args)...))
{
return std::mem_fn(f)(AuForward(args)...);
}
template >::value>>
constexpr auto invoke(Fn &&f, Args &&... args) noexcept(
noexcept(AuForward(f)(AuForward(args)...)))
-> decltype(AuForward(f)(AuForward(args)...))
{
return AuForward(f)(AuForward(args)...);
}
// std::invoke_result from C++17
template struct invoke_result_impl;
template
struct invoke_result_impl<
F, decltype(detail::invoke(AuDeclVal(), AuDeclVal()...), void()),
Us...>
{
using type = decltype(detail::invoke(AuDeclVal(), AuDeclVal()...));
};
template
using invoke_result = invoke_result_impl;
template
using invoke_result_t = typename invoke_result::type;
#if defined(_MSC_VER) && _MSC_VER <= 1900
// TODO make a version which works with MSVC 2015
template struct is_swappable : AuTrueType
{ };
template struct is_nothrow_swappable : AuTrueType
{ };
#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 tag swap(T &, T &);
template 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 AuFalseType can_swap(...) noexcept(false);
template (), AuDeclVal()))>
AuTrueType can_swap(int) noexcept(noexcept(swap(AuDeclVal(),
AuDeclVal())));
template AuFalseType uses_std(...);
template
AuIsSame(), AuDeclVal())), tag>
uses_std(int);
template
struct is_std_swap_noexcept
: AuBoolType<
AuIsNothrowMoveConstructible::value &&
AuIsNothrowMoveAssignable::value>
{ };
template
struct is_std_swap_noexcept : is_std_swap_noexcept
{ };
template
struct is_adl_swap_noexcept
: AuBoolType(0))>
{ };
} // namespace swap_adl_tests
template
struct is_swappable
: AuBoolType<
decltype(detail::swap_adl_tests::can_swap(0))::value &&
(!decltype(detail::swap_adl_tests::uses_std(0))::value ||
(AuIsMoveAssignable::value &&
AuIsMoveConstructible::value))>
{ };
template
struct is_swappable
: AuBoolType<
decltype(detail::swap_adl_tests::can_swap(0))::value &&
(!decltype(
detail::swap_adl_tests::uses_std(0))::value ||
is_swappable::value)>
{ };
template
struct is_nothrow_swappable
: AuBoolType<
is_swappable::value &&
((decltype(detail::swap_adl_tests::uses_std(0))::value
&&detail::swap_adl_tests::is_std_swap_noexcept::value) ||
(!decltype(detail::swap_adl_tests::uses_std(0))::value &&
detail::swap_adl_tests::is_adl_swap_noexcept::value))>
{ };
#endif
#endif
// std::void_t from C++17
template struct voider
{
using type = void;
};
template using void_t = typename voider::type;
// Trait for checking if a type is a tl::optional
template struct is_optional_impl : AuFalseType
{ };
template struct is_optional_impl> : AuTrueType
{ };
template using is_optional = is_optional_impl>;
// Change void to tl::monostate
template
using fixup_void = conditional_t::value, monostate, U>;
template >
using get_map_return = optional>>;
// Check if invoking F for some Us returns void
template struct returns_void_impl;
template
struct returns_void_impl>, U...>
: AuIsVoid>
{ };
template
using returns_void = AuIsVoid>; // returns_void_impl;
template
using enable_if_ret_void = enable_if_t::value>;
template
using disable_if_ret_void = enable_if_t::value>;
template
using enable_forward_value =
detail::enable_if_t::value &&
!AuIsSame, in_place_t>::value &&
!AuIsSame, detail::decay_t>::value>;
template
using enable_from_other = detail::enable_if_t<
AuIsConstructible::value &&
!AuIsConstructible &>::value &&
!AuIsConstructible &&>::value &&
!AuIsConstructible &>::value &&
!AuIsConstructible &&>::value &&
!AuIsConvertible &, T>::value &&
!AuIsConvertible &&, T>::value &&
!AuIsConvertible &, T>::value &&
!AuIsConvertible &&, T>::value>;
template
using enable_assign_forward = detail::enable_if_t<
!AuIsSame, detail::decay_t>::value &&
!detail::conjunction,
AuIsSame>>::value &&
AuIsConstructible::value &&AuIsAssignable::value>;
template
using enable_assign_from_other = detail::enable_if_t<
AuIsConstructible::value &&
AuIsAssignable::value &&
!AuIsConstructible &>::value &&
!AuIsConstructible &&>::value &&
!AuIsConstructible &>::value &&
!AuIsConstructible &&>::value &&
!AuIsConvertible &, T>::value &&
!AuIsConvertible &&, T>::value &&
!AuIsConvertible &, T>::value &&
!AuIsConvertible &&, T>::value &&
!AuIsAssignable &>::value &&
!AuIsAssignable &&>::value &&
!AuIsAssignable &>::value &&
!AuIsAssignable &&>::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 ::value>
struct optional_storage_base
{
TL_OPTIONAL_11_CONSTEXPR optional_storage_base() noexcept
: m_dummy(), m_has_value(false)
{ }
template
TL_OPTIONAL_11_CONSTEXPR optional_storage_base(in_place_t, U &&... u)
: m_value(AuForward(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 struct optional_storage_base
{
TL_OPTIONAL_11_CONSTEXPR optional_storage_base() noexcept
: m_dummy(), m_has_value(false)
{ }
template
TL_OPTIONAL_11_CONSTEXPR optional_storage_base(in_place_t, U &&... u)
: m_value(AuForward(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 struct optional_operations_base : optional_storage_base
{
using optional_storage_base::optional_storage_base;
void hard_reset() noexcept
{
get().~T();
this->m_has_value = false;
}
template void construct(Args &&... args)
{
new (std::addressof(this->m_value)) T(AuForward(args)...);
this->m_has_value = true;
}
template void assign(Opt &&rhs)
{
if (this->has_value())
{
if (rhs.has_value())
{
this->m_value = AuForward(rhs).get();
}
else
{
this->m_value.~T();
this->m_has_value = false;
}
}
else if (rhs.has_value())
{
construct(AuForward(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 AuMove(this->m_value);
}
#ifndef TL_OPTIONAL_NO_CONSTRR
constexpr const T &&get() const &&
{
return AuMove(this->m_value);
}
#endif
};
// This class manages conditionally having a trivial copy constructor
// This specialization is for when T is trivially copy constructible
template
struct optional_copy_base : optional_operations_base
{
using optional_operations_base::optional_operations_base;
};
// This specialization is for when T is not trivially copy constructible
template
struct optional_copy_base : optional_operations_base
{
using optional_operations_base::optional_operations_base;
optional_copy_base() = default;
optional_copy_base(const optional_copy_base &rhs)
: optional_operations_base()
{
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 ::value>
struct optional_move_base : optional_copy_base
{
using optional_copy_base::optional_copy_base;
};
#else
template struct optional_move_base;
#endif
template struct optional_move_base : optional_copy_base
{
using optional_copy_base::optional_copy_base;
optional_move_base() = default;
optional_move_base(const optional_move_base &rhs) = default;
optional_move_base(optional_move_base &&rhs) noexcept(
AuIsNothrowMoveConstructible::value)
{
if (rhs.has_value())
{
this->construct(AuMove(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
struct optional_copy_assign_base : optional_move_base
{
using optional_move_base::optional_move_base;
};
template
struct optional_copy_assign_base : optional_move_base
{
using optional_move_base::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 AuIsTriviallyAssignable. We have
// to make do with a non-trivial move assignment operator even if T is trivially
// move assignable
#ifndef TL_OPTIONAL_GCC49
template ::value
&&AuIsTriviallyMoveConstructible::value
&&AuIsTriviallyMoveAssignable::value>
struct optional_move_assign_base : optional_copy_assign_base
{
using optional_copy_assign_base::optional_copy_assign_base;
};
#else
template struct optional_move_assign_base;
#endif
template
struct optional_move_assign_base : optional_copy_assign_base
{
using optional_copy_assign_base::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(
AuIsNothrowMoveConstructible::value
&&AuIsNothrowMoveAssignable::value)
{
this->assign(AuMove(rhs));
return *this;
}
};
// optional_delete_ctor_base will conditionally delete copy and move
// constructors depending on whether T is copy/move constructible
template ::value,
bool EnableMove = AuIsMoveConstructible::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 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 = delete;
optional_delete_ctor_base &
operator=(const optional_delete_ctor_base &) = default;
optional_delete_ctor_base &
operator=(optional_delete_ctor_base &&) noexcept = default;
};
template struct optional_delete_ctor_base
{
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 struct optional_delete_ctor_base
{
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 ::value &&
AuIsCopyAssignable::value),
bool EnableMove = (AuIsMoveConstructible::value &&
AuIsMoveAssignable::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 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 = delete;
};
template 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 &) = delete;
optional_delete_assign_base &
operator=(optional_delete_assign_base &&) noexcept = default;
};
template 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 &) = 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 optional : private detail::optional_move_assign_base,
private detail::optional_delete_ctor_base,
private detail::optional_delete_assign_base
{
using base = detail::optional_move_assign_base;
static_assert(!AuIsSame::value,
"instantiation of optional with in_place_t is ill-formed");
static_assert(!AuIsSame, 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 TL_OPTIONAL_11_CONSTEXPR auto and_then(F &&f) &
{
using result = detail::invoke_result_t;
static_assert(detail::is_optional::value,
"F must return an optional");
return has_value() ? detail::invoke(AuForward(f), **this)
: result(nullopt);
}
template TL_OPTIONAL_11_CONSTEXPR auto and_then(F &&f) &&
{
using result = detail::invoke_result_t