// Copyright 2016 Glyn Matthews. // Copyright (C) 2011 - 2012 Andrzej Krzemienski. // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) /** * \file * \brief Contains an implementation of C++17 optional (n3793). * * \sa https://github.com/akrzemi1/Optional * \sa http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3848.html */ #ifndef NETWORK_OPTIONAL_INC #define NETWORK_OPTIONAL_INC #include #include #include #include #include #if !defined(DOXYGEN_SHOULD_SKIP_THIS) #ifdef NDEBUG #define NETWORK_ASSERTED_EXPRESSION(CHECK, EXPR) (EXPR) #else #define NETWORK_ASSERTED_EXPRESSION(CHECK, EXPR) \ ((CHECK) ? (EXPR) : (fail(#CHECK, __FILE__, __LINE__), (EXPR))) inline void fail(const char *, const char *, unsigned) {} #endif // NDEBUG #endif // !defined(DOXYGEN_SHOULD_SKIP_THIS) namespace network { /** * \ingroup optional * \class nullopt_t optional.hpp network/uri.hpp * \brief Disengaged state indicator. * \sa optional */ struct nullopt_t { #if !defined(DOXYGEN_SHOULD_SKIP_THIS) struct init {}; constexpr nullopt_t(init) {} #endif // !defined(DOXYGEN_SHOULD_SKIP_THIS) }; /** * \ingroup optional * \brief Used to indicate a *disengaged* state for optional objects. */ constexpr nullopt_t nullopt{nullopt_t::init{}}; /** * \ingroup optional * \class bad_optional_access optional.hpp network/uri.hpp * \brief Exception thrown when the value member function is called when the * optional object is disengaged. */ class bad_optional_access : public std::logic_error { public: /** * \brief Constructor. * \param what_arg The error message. */ explicit bad_optional_access(const std::string &what_arg) : std::logic_error(what_arg) {} /** * \brief Constructor. * \param what_arg The error message. */ explicit bad_optional_access(const char *what_arg) : std::logic_error(what_arg) {} }; #if !defined(DOXYGEN_SHOULD_SKIP_THIS) namespace details { struct dummy_t {}; template union trivially_destructible_optional_storage { static_assert(std::is_trivially_destructible::value, ""); dummy_t dummy_; T value_; constexpr trivially_destructible_optional_storage() : dummy_{} {} constexpr trivially_destructible_optional_storage(const T &v) : value_{v} {} ~trivially_destructible_optional_storage() = default; }; template union optional_storage { dummy_t dummy_; T value_; constexpr optional_storage() : dummy_{} {} constexpr optional_storage(const T &v) : value_{v} {} ~optional_storage() {} }; template class trivially_destructible_optional_base { public: typedef T value_type; constexpr trivially_destructible_optional_base() noexcept : init_(false), storage_{} {} constexpr trivially_destructible_optional_base(const T &v) : init_(true), storage_{v} {} constexpr trivially_destructible_optional_base(T &&v) : init_(true), storage_{std::move(v)} {} ~trivially_destructible_optional_base() = default; protected: bool init_; optional_storage storage_; }; template class optional_base { public: typedef T value_type; constexpr optional_base() noexcept : init_(false), storage_{} {} constexpr optional_base(const T &v) : init_(true), storage_{v} {} constexpr optional_base(T &&v) : init_(true), storage_{std::move(v)} {} ~optional_base() { if (init_) { storage_.value_.T::~T(); } } protected: bool init_; optional_storage storage_; }; } // namespace details #endif // !defined(DOXYGEN_SHOULD_SKIP_THIS) /** * \ingroup optional * \class optional optional.hpp network/uri.hpp * \brief An implementation of C++17 optional (n3793) */ #if !defined(DOXYGEN_SHOULD_SKIP_THIS) template using optional_base = typename std::conditional::value, details::trivially_destructible_optional_base, details::optional_base>::type; #endif // !defined(DOXYGEN_SHOULD_SKIP_THIS) template #if !defined(DOXYGEN_SHOULD_SKIP_THIS) class optional : optional_base { #else class optional { #endif // !defined(DOXYGEN_SHOULD_SKIP_THIS) typedef optional_base base_type; public: /** * \brief Optional value type. */ typedef T value_type; /** * \brief Constructor. * \post *disengaged*. */ constexpr optional() : optional_base() {} /** * \brief Constructor. * \post *disengaged*. */ constexpr optional(nullopt_t) noexcept : optional_base() {} /** * \brief Copy constructor. * \param other The other optional object. */ optional(const optional &other) { if (other) { ::new (static_cast(ptr())) T(*other); base_type::init_ = true; } } /** * \brief Move constructor. * \param other The other optional object. */ optional(optional &&other) noexcept { if (other) { ::new (static_cast(ptr())) T(std::move(other.storage_.value_)); base_type::init_ = true; } } /** * \brief Constructor. * \param value The value with which to initialize the optional object. * \post *engaged* */ constexpr optional(const T &value) : optional_base(value) {} /** * \brief Constructor. * \param value The value with which to initialize the optional object. * \post *engaged* */ constexpr optional(T &&value) : optional_base(std::move(value)) {} /** * \brief Assignment operator. * \post *disengaged*. * \returns \c *this. */ optional &operator=(nullopt_t) noexcept { if (base_type::init_) { ptr()->T::~T(); } base_type::init_ = false; return *this; } /** * \brief Copy assignment operator. * \param other The other optional object. * \returns \c *this. */ optional &operator=(const optional &other) { if (bool(*this) && !other) { ptr()->T::~T(); base_type::init_ = false; } else if (!(*this) && bool(other)) { ::new (static_cast(ptr())) T(*other); base_type::init_ = true; } else if (bool(*this) && bool(other)) { base_type::storage_.value_ = *other; } return *this; } /** * \brief Move assignment operator. * \param other The other optional object. * \returns \c *this. */ optional &operator=(optional &&other) noexcept { if (bool(*this) && !other) { ptr()->T::~T(); base_type::init_ = false; } else if (!(*this) && bool(other)) { ::new (static_cast(ptr())) T(std::move(*other)); base_type::init_ = true; } else if (bool(*this) && bool(other)) { base_type::storage_.value_ = std::move(*other); } return *this; } /** * \brief Destructor. */ ~optional() = default; /** * \brief Swap function. * \param other The other optional object. */ void swap(optional &other) noexcept { if (bool(*this) && !other) { ::new (static_cast(other.ptr())) T(std::move(**this)); ptr()->T::~T(); std::swap(base_type::init_, other.base_type::init_); } else if (!(*this) && bool(other)) { ::new (static_cast(ptr())) T(std::move(*other)); other.ptr()->T::~T(); std::swap(base_type::init_, other.init_); } else if (bool(*this) && bool(other)) { std::swap(**this, *other); } } /** * \brief Observer. * \pre *engaged* * \returns The underlying optional value. */ constexpr T const *operator->() const { return NETWORK_ASSERTED_EXPRESSION(bool(*this), ptr()); } /** * \brief Observer. * \pre *engaged* * \returns The underlying optional value. */ T *operator->() { return NETWORK_ASSERTED_EXPRESSION(bool(*this), ptr()); } /** * \brief Observer. * \pre *engaged* * \returns The underlying optional value. */ constexpr T const &operator*() const { return NETWORK_ASSERTED_EXPRESSION(bool(*this), base_type::storage_.value_); } /** * \brief Observer. * \pre *engaged* * \returns The underlying optional value. */ T &operator*() { return NETWORK_ASSERTED_EXPRESSION(bool(*this), base_type::storage_.value_); } /** * \brief Operator bool overloads. * \returns \c true if the optional is *engaged*, \c false if it is * *disengaged*. */ constexpr explicit operator bool() const noexcept { return base_type::init_; } /** * \returns The underlying optional value, if \c bool(*this). * \throws A bad_optional_access if \c !*this. */ constexpr T const &value() const { return *this ? base_type::storage_.value_ : (throw bad_optional_access("Uninitialized optional value"), base_type::storage_.value_); } /** * \returns The underlying optional value, if \c bool(*this). * \throws A bad_optional_access if \c !*this. */ T &value() { return *this ? base_type::storage_.value_ : (throw bad_optional_access("Uninitialized optional value"), base_type::storage_.value_); } /** * \returns bool(*this) ? **this : * static_cast(std::forward(v)). \pre \c * std::is_copy_constructible::value is \c true and * std::is_convertible::value is \c true. */ template T value_or(U &&other) const & { static_assert(std::is_copy_constructible::value, "Must be copy constructible."); static_assert(std::is_convertible::value, "U must be convertible to T."); return bool(*this) ? **this : static_cast(std::forward(other)); } /** * \returns bool(*this) ? std::move(**this) : * static_cast(std::forward(v)). \pre * std::is_move_constructible::value is \c true and * std::is_convertible::value is \c true. */ template T value_or(U &&other) && { static_assert(std::is_copy_constructible::value, "Must be copy constructible."); static_assert(std::is_convertible::value, "U must be convertible to T."); return bool(*this) ? std::move(**this) : static_cast(std::forward(other)); } private: T *ptr() { return std::addressof(base_type::storage_.value_); } }; /** * \brief Equality operator. */ template bool operator==(const optional &lhs, const optional &rhs) { if (bool(lhs) != bool(rhs)) { return false; } else if (!bool(lhs)) { return true; } else { return *lhs == *rhs; } } /** * \brief Inequality operator. */ template bool operator!=(const optional &lhs, const optional &rhs) { return !(lhs == rhs); } /** * \brief Comparison operator. */ template bool operator<(const optional &lhs, const optional &rhs) { if (!rhs) { return false; } else if (!lhs) { return true; } else { return *lhs < *rhs; } } /** * \brief Comparison operator. * \returns rhs < lhs. */ template bool operator>(const optional &lhs, const optional &rhs) { return rhs < lhs; } /** * \brief Comparison operator. * \returns !(rhs < lhs). */ template bool operator<=(const optional &lhs, const optional &rhs) { return !(rhs < lhs); } /** * \brief Comparison operator. * \returns !(rhs > lhs). */ template bool operator>=(const optional &lhs, const optional &rhs) { return !(lhs < rhs); } /** * \brief Equality operator. * \returns \c !x. */ template bool operator==(const optional &x, nullopt_t) noexcept { return !x; } /** * \brief Equality operator. * \returns \c !x. */ template bool operator==(nullopt_t, const optional &x) noexcept { return !x; } /** * \brief Inequality operator. * \returns \c bool(x). */ template bool operator!=(const optional &x, nullopt_t) noexcept { return bool(x); } /** * \brief Inequality operator. * \returns \c bool(x). */ template bool operator!=(nullopt_t, const optional &x) noexcept { return bool(x); } /** * \brief Comparison operator. * \returns \c false. */ template bool operator<(const optional &x, nullopt_t) noexcept { return false; } /** * \brief Comparison operator. * \returns \c bool(x). */ template bool operator<(nullopt_t, const optional &x) noexcept { return bool(x); } /** * \brief Comparison operator. * \returns \c !x. */ template bool operator<=(const optional &x, nullopt_t) noexcept { return !x; } /** * \brief Comparison operator. * \returns \c true. */ template bool operator<=(nullopt_t, const optional &x) noexcept { return true; } /** * \brief Comparison operator. * \returns \c bool(x). */ template bool operator>(const optional &x, nullopt_t) noexcept { return bool(x); } /** * \brief Comparison operator. * \returns \c false. */ template bool operator>(nullopt_t, const optional &x) noexcept { return false; } /** * \brief Comparison operator. * \returns \c true. */ template bool operator>=(const optional &x, nullopt_t) noexcept { return true; } /** * \brief Comparison operator. * \returns \c !x. */ template bool operator>=(nullopt_t, const optional &x) noexcept { return !x; } /** * \brief Equality operator. * \returns bool(x) ? *x == v : false. */ template bool operator==(const optional &x, const T &v) { return bool(x) ? *x == v : false; } /** * \brief Equality operator. * \returns bool(x) ? v == *x : false. */ template bool operator==(const T &v, const optional &x) { return bool(x) ? v == *x : false; } /** * \brief Inequality operator. * \returns bool(x) ? !(*x == v) : true. */ template bool operator!=(const optional &x, const T &v) { return bool(x) ? !(*x == v) : true; } /** * \brief Inequality operator. * \returns bool(x) ? !(v == *x) : true. */ template bool operator!=(const T &v, const optional &x) { return bool(x) ? !(v == *x) : true; } /** * \brief Comparison operator. * \returns bool(x) ? *x < v : true. */ template bool operator<(const optional &x, const T &v) { return bool(x) ? *x < v : true; } /** * \brief Comparison operator. * \returns bool(x) ? v < *x : false. */ template bool operator<(const T &v, const optional &x) { return bool(x) ? v < *x : false; } /** * \brief Comparison operator. * \returns bool(x) ? *x < v : true. */ template bool operator>(const optional &x, const T &v) { return bool(x) ? *x < v : true; } /** * \brief Comparison operator. * \returns bool(x) ? v < *x : false. */ template bool operator>(const T &v, const optional &x) { return bool(x) ? v < *x : false; } /** * \brief Comparison operator. * \returns !(x < v). */ template bool operator>=(const optional &x, const T &v) { return !(x < v); } /** * \brief Comparison operator. * \returns !(v < x). */ template bool operator>=(const T &v, const optional &x) { return !(v < x); } /** * \brief Comparison operator. * \returns !(x > v). */ template bool operator<=(const optional &x, const T &v) { return !(x > v); } /** * \brief Comparison operator. * \returns !(v > x). */ template bool operator<=(const T &v, const optional &x) { return !(v > x); } /** * \ingroup optional * \brief Swap function. * \param lhs The first optional object. * \param rhs The second optional object. * * Calls: * \code{.cpp} * lhs.swap(rhs); * \endcode */ template inline void swap(optional &lhs, optional &rhs) noexcept(noexcept(lhs.swap(rhs))) { return lhs.swap(rhs); } /** * \ingroup optional * \brief A helper function to contruct an optional object. * \returns optional::type>(std::forward(value)). */ template inline constexpr optional::type> make_optional( T &&value) { return optional::type>(std::forward(value)); } } // namespace network #endif // NETWORK_OPTIONAL_INC