///
// 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
// .
///
#ifndef TL_OPTIONAL_HPP
#define TL_OPTIONAL_HPP
#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)
#define TL_OPTIONAL_GCC49
#endif
#if (defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 4)
#define TL_OPTIONAL_GCC54
#endif
#if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9)
#define TL_OPTIONAL_NO_CONSTRR
#endif
#if __cplusplus > 201103L
#define TL_OPTIONAL_CXX14
#endif
#if (__cplusplus == 201103L || defined(TL_OPTIONAL_MSVC2015) || \
defined(TL_OPTIONAL_GCC49)) && \
!defined(TL_OPTIONAL_GCC50)
/// \exclude
#define TL_OPTIONAL_11_CONSTEXPR
#else
/// \exclude
#define TL_OPTIONAL_11_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 {
// C++14-style aliases for brevity
template using remove_const_t = typename std::remove_const::type;
template
using remove_reference_t = typename std::remove_reference::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;
// std::conjunction from C++17
template struct conjunction : std::true_type {};
template struct conjunction : B {};
template
struct conjunction
: std::conditional, B>::type {};
// 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 : std::false_type {};
template struct is_optional_impl> : std::true_type {};
template using is_optional = is_optional_impl>;
// std::invoke from C++17
// 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)...);
}
// std::invoke_result from C++17
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;
// 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...>
: std::is_void> {};
template
using returns_void = 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 &&
!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_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) 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_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) 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:
// The different versions for C++14 and 11 are needed because deduced return
// types are not SFINAE-safe. C.f.
// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0826r0.html
#ifdef TL_OPTIONAL_CXX14
/// \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 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(std::forward(f), **this)
: result(nullopt);
}
/// \group and_then
/// \synopsis template \nconstexpr auto and_then(F &&f) &&;
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(std::forward(f), std::move(**this))
: result(nullopt);
}
/// \group and_then
/// \synopsis template \nconstexpr auto and_then(F &&f) const &;
template constexpr auto 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);
}
#ifndef TL_OPTIONAL_NO_CONSTRR
/// \group and_then
/// \synopsis template \nconstexpr auto and_then(F &&f) const &&;
template constexpr auto 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);
}
#endif
#else
/// \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);
}
#ifndef TL_OPTIONAL_NO_CONSTRR
/// \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);
}
#endif
#endif
#ifdef TL_OPTIONAL_CXX14
/// \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 TL_OPTIONAL_11_CONSTEXPR auto map(F &&f) & {
return map_impl(*this, std::forward(f));
}
template TL_OPTIONAL_11_CONSTEXPR auto map(F &&f) && {
return map_impl(std::move(*this), std::forward(f));
}
template constexpr auto map(F &&f) const & {
return map_impl(*this, std::forward(f));
}
template constexpr auto map(F &&f) const && {
return map_impl(std::move(*this), std::forward(f));
}
#else
/// \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
TL_OPTIONAL_11_CONSTEXPR decltype(map_impl(std::declval(),
std::declval()))
map(F &&f) & {
return map_impl(*this, std::forward(f));
}
template
TL_OPTIONAL_11_CONSTEXPR decltype(map_impl(std::declval(),
std::declval()))
map(F &&f) && {
return map_impl(std::move(*this), std::forward(f));
}
template
constexpr decltype(map_impl(std::declval(),
std::declval()))
map(F &&f) const & {
return map_impl(*this, std::forward(f));
}
#ifndef TL_OPTIONAL_NO_CONSTRR
template
constexpr decltype(map_impl(std::declval(),
std::declval()))
map(F &&f) const && {
return map_impl(std::move(*this), std::forward(f));
}
#endif
#endif
/// \brief Calls `f` if the optional is empty
/// \requires `std::invoke_result_t` must be void or convertible to
/// `optional`. \effects If `*this` has a value, returns `*this`.
/// Otherwise, if `f` returns `void`, calls `std::forward(f)` and returns
/// `std::nullopt`. Otherwise, returns `std::forward(f)()`. \group or_else
/// \synopsis template optional or_else (F &&f) &;
template * = nullptr>
optional TL_OPTIONAL_11_CONSTEXPR or_else(F &&f) & {
if (has_value())
return *this;
std::forward(f)();
return nullopt;
}
/// \exclude
template * = nullptr>
optional TL_OPTIONAL_11_CONSTEXPR or_else(F &&f) & {
return has_value() ? *this : std::forward(f)();
}
/// \group or_else
/// \synopsis template optional or_else (F &&f) &&;
template * = nullptr>
optional or_else(F &&f) && {
if (has_value())
return std::move(*this);
std::forward