///
// optional - An implementation of std::optional with extensions
// Written in 2017 by Simon Brand (@TartanLlama)
//
// 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
// .
///
#include
#include
#include
#include
#include
#if __cplusplus == 201103L || _MSC_VER == 1900
/// \exclude
#define TL_OPTIONAL_11_CONSTEXPR
#else
/// \exclude
#define TL_OPTIONAL_11_CONSTEXPR constexpr
#endif
#if _MSC_VER == 1900
/// \exclude
#define TL_OPTIONAL_MSVC_2015_CONSTEXPR
#else
/// \exclude
#define TL_OPTIONAL_MSVC_2015_CONSTEXPR constexpr
#endif
namespace tl {
/// \brief Used to represent an optional with no data; essentially a bool
class monostate {};
/// \brief A tag type to tell optional to construct its value in-place
struct in_place_t {
explicit in_place_t() = default;
};
/// \brief A tag to tell optional to construct its value in-place
static constexpr in_place_t in_place{};
template class optional;
/// \exclude
namespace detail {
template using remove_cv_t = typename std::remove_cv::type;
template using remove_const_t = typename std::remove_const::type;
template
using remove_volatile_t = typename std::remove_volatile::type;
template using add_cv_t = typename std::add_cv::type;
template using add_const_t = typename std::add_const::type;
template using add_volatile_t = typename std::add_volatile::type;
template
using remove_reference_t = typename std::remove_reference::type;
template
using add_lvalue_reference_t = typename std::add_lvalue_reference::type;
template
using add_rvalue_reference_t = typename std::add_rvalue_reference::type;
template
using remove_pointer_t = typename std::remove_pointer::type;
template using add_pointer_t = typename std::add_pointer::type;
template using make_signed_t = typename std::make_signed::type;
template using make_unsigned_t = typename std::make_unsigned::type;
template using remove_extent_t = typename std::remove_extent::type;
template
using remove_all_extents_t = typename std::remove_all_extents::type;
template
using aligned_storage_t = typename std::aligned_storage::type;
template
using aligned_union_t = typename std::aligned_union::type;
template using decay_t = typename std::decay::type;
template
using enable_if_t = typename std::enable_if::type;
template
using conditional_t = typename std::conditional::type;
template
using common_type_t = typename std::common_type::type;
template
using underlying_type_t = typename std::underlying_type::type;
template using result_of_t = typename std::result_of::type;
template struct conjunction : std::true_type {};
template struct conjunction : B {};
template
struct conjunction
: std::conditional, B>::type {};
template struct voider { using type = void; };
template using void_t = typename voider::type;
template struct is_optional_impl : std::false_type {};
template struct is_optional_impl> : std::true_type {};
template using is_optional = is_optional_impl>;
// https://stackoverflow.com/questions/38288042/c11-14-invoke-workaround
template >{}>,
int = 0>
constexpr auto invoke(Fn &&f, Args &&... args) noexcept(
noexcept(std::mem_fn(f)(std::forward(args)...)))
-> decltype(std::mem_fn(f)(std::forward(args)...)) {
return std::mem_fn(f)(std::forward(args)...);
}
template >{}>>
constexpr auto invoke(Fn &&f, Args &&... args) noexcept(
noexcept(std::forward(f)(std::forward(args)...)))
-> decltype(std::forward(f)(std::forward(args)...)) {
return std::forward(f)(std::forward(args)...);
}
template struct invoke_result_impl;
template
struct invoke_result_impl<
F, decltype(invoke(std::declval(), std::declval()...), void()),
Us...> {
using type = decltype(invoke(std::declval(), std::declval()...));
};
template
using invoke_result = invoke_result_impl;
template
using invoke_result_t = typename invoke_result::type;
template
using fixup_void = conditional_t::value, monostate, U>;
template struct get_invoke_optional_ret {
using type = invoke_result_t<
conditional_t::value,
typename remove_reference_t::value_type &,
typename remove_reference_t::value_type &&>,
U...>;
};
template
using get_invoke_ret = typename conditional_t::value,
get_invoke_optional_ret,
invoke_result>::type;
template
using get_map_return = optional>>;
template
using returns_void = std::is_void>;
template
using disable_if_optional = enable_if_t::value>;
template
using enable_if_optional = enable_if_t::value>;
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 &&
!std::is_same, in_place_t>::value &&
!std::is_same, detail::decay_t>::value>;
template
using enable_from_other = detail::enable_if_t<
std::is_constructible::value &&
!std::is_constructible &>::value &&
!std::is_constructible &&>::value &&
!std::is_constructible &>::value &&
!std::is_constructible &&>::value &&
!std::is_convertible &, T>::value &&
!std::is_convertible &&, T>::value &&
!std::is_convertible &, T>::value &&
!std::is_convertible &&, T>::value>;
template
using enable_assign_forward = detail::enable_if_t<
!std::is_same, detail::decay_t>::value &&
!detail::conjunction,
std::is_same>>::value &&
std::is_constructible::value && std::is_assignable::value>;
template
using enable_assign_from_other = detail::enable_if_t<
std::is_constructible::value &&
std::is_assignable::value &&
!std::is_constructible &>::value &&
!std::is_constructible &&>::value &&
!std::is_constructible &>::value &&
!std::is_constructible &&>::value &&
!std::is_convertible &, T>::value &&
!std::is_convertible &&, T>::value &&
!std::is_convertible &, T>::value &&
!std::is_convertible &&, T>::value &&
!std::is_assignable &>::value &&
!std::is_assignable &&>::value &&
!std::is_assignable &>::value &&
!std::is_assignable &&>::value>;
#ifdef _MSC_VER
// TODO make a version which works with MSVC
template struct is_swappable : std::true_type {};
template 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 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 std::false_type can_swap(...) noexcept(false);
template (), std::declval()))>
std::true_type can_swap(int) noexcept(noexcept(swap(std::declval(),
std::declval())));
template std::false_type uses_std(...);
template
std::is_same(), std::declval())), tag>
uses_std(int);
template
struct is_std_swap_noexcept
: std::integral_constant::value &&
std::is_nothrow_move_assignable::value> {};
template
struct is_std_swap_noexcept : is_std_swap_noexcept {};
template
struct is_adl_swap_noexcept
: std::integral_constant(0))> {};
} // namespace swap_adl_tests
template
struct is_swappable
: std::integral_constant<
bool,
decltype(detail::swap_adl_tests::can_swap(0))::value &&
(!decltype(detail::swap_adl_tests::uses_std(0))::value ||
(std::is_move_assignable::value &&
std::is_move_constructible::value))> {};
template
struct is_swappable
: std::integral_constant<
bool,
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
: std::integral_constant<
bool,
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
template ::value>
struct optional_storage_base {
TL_OPTIONAL_MSVC_2015_CONSTEXPR optional_storage_base() noexcept : m_dummy(), m_has_value(false) {}
template
TL_OPTIONAL_MSVC_2015_CONSTEXPR optional_storage_base(in_place_t, U &&... u) noexcept
: m_value(std::forward(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;
};
template struct optional_storage_base {
TL_OPTIONAL_MSVC_2015_CONSTEXPR optional_storage_base() noexcept
: m_dummy(), m_has_value(false) {}
template
TL_OPTIONAL_MSVC_2015_CONSTEXPR optional_storage_base(in_place_t,
U &&... u) noexcept
: m_value(std::forward(u)...), m_has_value(true) {}
~optional_storage_base() = default;
struct dummy {};
union {
dummy m_dummy;
T m_value;
};
bool m_has_value = false;
};
} // namespace detail
/// \brief 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 {}
};
/// \brief Represents an empty optional
/// \synopsis static constexpr nullopt_t nullopt;
///
/// *Examples*:
/// ```
/// tl::optional a = tl::nullopt;
/// void foo (tl::optional);
/// foo(tl::nullopt); //pass 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_storage_base {
using base = detail::optional_storage_base;
public:
/// \group and_then
/// Carries out some operation which returns an optional on the stored object if there is one.
/// \requires `std::invoke(std::forward(f), value())` returns a `std::optional` for some `U`.
/// \returns Let `U` be the result of `std::invoke(std::forward(f), value())`. Returns a `std::optional`. The return value is empty if `*this` is empty, otherwise the return value of `std::invoke(std::forward(f), value())` is returned.
/// \group and_then
/// \synopsis template \nconstexpr auto and_then(F &&f) &;
template
TL_OPTIONAL_11_CONSTEXPR detail::invoke_result_t 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(std::forward(f), **this)
: result(nullopt);
}
/// \group and_then
/// \synopsis template \nconstexpr auto and_then(F &&f) &&;
template
TL_OPTIONAL_11_CONSTEXPR detail::invoke_result_t 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(std::forward(f), std::move(**this))
: result(nullopt);
}
/// \group and_then
/// \synopsis template \nconstexpr auto and_then(F &&f) const &;
template
constexpr detail::invoke_result_t and_then(F &&f) const & {
using result = detail::invoke_result_t;
static_assert(detail::is_optional::value,
"F must return an optional");
return has_value() ? detail::invoke(std::forward(f), **this)
: result(nullopt);
}
/// \group and_then
/// \synopsis template \nconstexpr auto and_then(F &&f) const &&;
template
constexpr detail::invoke_result_t and_then(F &&f) const && {
using result = detail::invoke_result_t;
static_assert(detail::is_optional::value,
"F must return an optional");
return has_value() ? detail::invoke(std::forward(f), std::move(**this))
: result(nullopt);
}
/// \brief Carries out some operation on the stored object if there is one.
/// \returns Let `U` be the result of `std::invoke(std::forward(f), value())`. Returns a `std::optional`. The return value is empty if `*this` is empty, otherwise an `optional` is constructed from the return value of `std::invoke(std::forward(f), value())` and is returned.
/// \group map
/// \synopsis template auto map(F &&f) &;
template * = nullptr,
detail::disable_if_ret_void * = nullptr>
detail::get_map_return map(F &&f) &
noexcept(noexcept(detail::invoke(std::forward(f),
std::declval()))) {
using result = detail::get_map_return;
return has_value() ? detail::invoke(std::forward(f), **this)
: result(nullopt);
}
/// \exclude
template * = nullptr,
detail::enable_if_ret_void * = nullptr>
detail::get_map_return map(F &&f) & {
if (!has_value())
return nullopt;
detail::invoke(std::forward(f), **this);
return monostate{};
}
/// \exclude
template * = nullptr,
detail::disable_if_ret_void * = nullptr>
detail::get_map_return map(F &&f) & {
using result = detail::get_map_return;
return (f.has_value() && has_value())
? detail::invoke(*std::forward(f), **this)
: result(nullopt);
}
/// \exclude
template * = nullptr,
detail::enable_if_ret_void * = nullptr>
detail::get_map_return map(F &&f) & {
if (!f.has_value() || !has_value())
return nullopt;
detail::invoke(*std::forward(f), **this);
return monostate{};
}
/// \group map
/// \synopsis template auto map(F &&f) &&;
template * = nullptr,
detail::disable_if_ret_void * = nullptr>
detail::get_map_return map(F &&f) && {
using result = detail::get_map_return;
return has_value() ? detail::invoke(std::forward(f), std::move(**this))
: result(nullopt);
}
/// \exclude
template * = nullptr,
detail::enable_if_ret_void