2019-06-30 11:21:29 -07:00
|
|
|
#ifndef AL_OPTIONAL_H
|
|
|
|
#define AL_OPTIONAL_H
|
|
|
|
|
2019-07-29 09:29:35 -07:00
|
|
|
#include <initializer_list>
|
2019-06-30 11:21:29 -07:00
|
|
|
#include <type_traits>
|
2019-07-29 09:29:35 -07:00
|
|
|
#include <utility>
|
2019-06-30 11:21:29 -07:00
|
|
|
|
2019-07-01 01:28:33 -07:00
|
|
|
#include "almalloc.h"
|
|
|
|
|
2019-06-30 11:21:29 -07:00
|
|
|
namespace al {
|
|
|
|
|
|
|
|
struct nullopt_t { };
|
|
|
|
struct in_place_t { };
|
|
|
|
|
|
|
|
constexpr nullopt_t nullopt{};
|
|
|
|
constexpr in_place_t in_place{};
|
|
|
|
|
2021-01-02 13:17:16 -08:00
|
|
|
|
2021-10-08 15:41:33 -07:00
|
|
|
/* Base storage struct for an optional. Defines a trivial destructor, for types
|
|
|
|
* that can be trivially destructed.
|
|
|
|
*/
|
2021-01-02 13:17:16 -08:00
|
|
|
template<typename T, bool = std::is_trivially_destructible<T>::value>
|
2021-10-08 15:41:33 -07:00
|
|
|
struct optstore_base {
|
2019-07-01 17:34:02 -07:00
|
|
|
bool mHasValue{false};
|
|
|
|
union {
|
2021-01-02 13:17:16 -08:00
|
|
|
char mDummy;
|
2019-07-01 17:34:02 -07:00
|
|
|
T mValue;
|
|
|
|
};
|
|
|
|
|
2021-10-08 15:41:33 -07:00
|
|
|
optstore_base() { }
|
2021-01-02 13:17:16 -08:00
|
|
|
template<typename ...Args>
|
2021-10-08 15:41:33 -07:00
|
|
|
explicit optstore_base(in_place_t, Args&& ...args)
|
2021-01-02 13:17:16 -08:00
|
|
|
: mHasValue{true}, mValue{std::forward<Args>(args)...}
|
|
|
|
{ }
|
2021-10-08 15:41:33 -07:00
|
|
|
~optstore_base() = default;
|
2021-01-02 13:17:16 -08:00
|
|
|
};
|
|
|
|
|
2021-10-08 15:41:33 -07:00
|
|
|
/* Specialization needing a non-trivial destructor. */
|
2021-01-02 13:17:16 -08:00
|
|
|
template<typename T>
|
2021-10-08 15:41:33 -07:00
|
|
|
struct optstore_base<T, false> {
|
2021-01-02 13:17:16 -08:00
|
|
|
bool mHasValue{false};
|
|
|
|
union {
|
|
|
|
char mDummy;
|
|
|
|
T mValue;
|
|
|
|
};
|
|
|
|
|
2021-10-08 15:41:33 -07:00
|
|
|
optstore_base() { }
|
2021-01-02 13:17:16 -08:00
|
|
|
template<typename ...Args>
|
2021-10-08 15:41:33 -07:00
|
|
|
explicit optstore_base(in_place_t, Args&& ...args)
|
2021-01-02 13:17:16 -08:00
|
|
|
: mHasValue{true}, mValue{std::forward<Args>(args)...}
|
|
|
|
{ }
|
2021-10-08 15:41:33 -07:00
|
|
|
~optstore_base() { if(mHasValue) al::destroy_at(std::addressof(mValue)); }
|
2021-01-02 13:17:16 -08:00
|
|
|
};
|
|
|
|
|
2021-10-08 15:41:33 -07:00
|
|
|
/* Next level of storage, which defines helpers to construct and destruct the
|
|
|
|
* stored object.
|
|
|
|
*/
|
2021-01-02 13:17:16 -08:00
|
|
|
template<typename T>
|
2021-10-08 15:41:33 -07:00
|
|
|
struct optstore_helper : public optstore_base<T> {
|
|
|
|
using optstore_base<T>::optstore_base;
|
2021-01-02 13:17:16 -08:00
|
|
|
|
2019-07-01 17:34:02 -07:00
|
|
|
template<typename... Args>
|
2021-10-08 15:41:33 -07:00
|
|
|
void construct(Args&& ...args) noexcept(noexcept(T{std::forward<Args>(args)...}))
|
2019-07-01 17:34:02 -07:00
|
|
|
{
|
2021-10-08 15:41:33 -07:00
|
|
|
al::construct_at(std::addressof(this->mValue), std::forward<Args>(args)...);
|
|
|
|
this->mHasValue = true;
|
2019-07-01 17:34:02 -07:00
|
|
|
}
|
|
|
|
|
2021-10-08 15:41:33 -07:00
|
|
|
void reset() noexcept
|
|
|
|
{
|
|
|
|
if(this->mHasValue)
|
|
|
|
al::destroy_at(std::addressof(this->mValue));
|
|
|
|
this->mHasValue = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void assign(const optstore_helper &rhs)
|
|
|
|
{
|
|
|
|
if(!rhs.mHasValue)
|
|
|
|
this->reset();
|
|
|
|
else if(this->mHasValue)
|
|
|
|
this->mValue = rhs.mValue;
|
|
|
|
else
|
|
|
|
this->construct(rhs.mValue);
|
|
|
|
}
|
|
|
|
|
|
|
|
void assign(optstore_helper&& rhs)
|
|
|
|
{
|
|
|
|
if(!rhs.mHasValue)
|
|
|
|
this->reset();
|
|
|
|
else if(this->mHasValue)
|
|
|
|
this->mValue = std::move(rhs.mValue);
|
|
|
|
else
|
|
|
|
this->construct(std::move(rhs.mValue));
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Define copy and move constructors and assignment operators, which may or may
|
|
|
|
* not be trivial. Default definition is completely trivial.
|
|
|
|
*/
|
|
|
|
template<typename T, bool trivial_copy = std::is_trivially_copy_constructible<T>::value,
|
|
|
|
bool trivial_move = std::is_trivially_move_constructible<T>::value,
|
|
|
|
/* Trivial assignment is dependent on trivial construction. */
|
|
|
|
bool = trivial_copy && std::is_trivially_copy_assignable<T>::value,
|
|
|
|
bool = trivial_move && std::is_trivially_move_assignable<T>::value>
|
|
|
|
struct optional_storage : optstore_helper<T> {
|
|
|
|
using optstore_helper<T>::optstore_helper;
|
|
|
|
optional_storage(const optional_storage&) = default;
|
|
|
|
optional_storage( optional_storage&&) = default;
|
|
|
|
optional_storage& operator=(const optional_storage&) = default;
|
|
|
|
optional_storage& operator=( optional_storage&&) = default;
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Non-trivial move assignment. */
|
|
|
|
template<typename T>
|
|
|
|
struct optional_storage<T, true, true, true, false> : optstore_helper<T> {
|
|
|
|
using optstore_helper<T>::optstore_helper;
|
|
|
|
optional_storage(const optional_storage&) = default;
|
|
|
|
optional_storage(optional_storage&&) = default;
|
|
|
|
optional_storage& operator=(const optional_storage&) = default;
|
|
|
|
optional_storage& operator=(optional_storage&& rhs)
|
|
|
|
{ this->assign(std::move(rhs)); return *this; }
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Non-trivial move construction. */
|
|
|
|
template<typename T>
|
|
|
|
struct optional_storage<T, true, false, true, false> : optstore_helper<T> {
|
|
|
|
using optstore_helper<T>::optstore_helper;
|
|
|
|
optional_storage(const optional_storage&) = default;
|
|
|
|
optional_storage(optional_storage&& rhs)
|
|
|
|
{ if(rhs.mHasValue) this->construct(std::move(rhs.mValue)); }
|
|
|
|
optional_storage& operator=(const optional_storage&) = default;
|
|
|
|
optional_storage& operator=(optional_storage&& rhs)
|
|
|
|
{ this->assign(std::move(rhs)); return *this; }
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Non-trivial copy assignment. */
|
|
|
|
template<typename T>
|
|
|
|
struct optional_storage<T, true, true, false, true> : optstore_helper<T> {
|
|
|
|
using optstore_helper<T>::optstore_helper;
|
|
|
|
optional_storage(const optional_storage&) = default;
|
|
|
|
optional_storage(optional_storage&&) = default;
|
|
|
|
optional_storage& operator=(const optional_storage &rhs)
|
|
|
|
{ this->assign(rhs); return *this; }
|
|
|
|
optional_storage& operator=(optional_storage&&) = default;
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Non-trivial copy construction. */
|
|
|
|
template<typename T>
|
|
|
|
struct optional_storage<T, false, true, false, true> : optstore_helper<T> {
|
|
|
|
using optstore_helper<T>::optstore_helper;
|
|
|
|
optional_storage(const optional_storage &rhs)
|
|
|
|
{ if(rhs.mHasValue) this->construct(rhs.mValue); }
|
|
|
|
optional_storage(optional_storage&&) = default;
|
|
|
|
optional_storage& operator=(const optional_storage &rhs)
|
|
|
|
{ this->assign(rhs); return *this; }
|
|
|
|
optional_storage& operator=(optional_storage&&) = default;
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Non-trivial assignment. */
|
|
|
|
template<typename T>
|
|
|
|
struct optional_storage<T, true, true, false, false> : optstore_helper<T> {
|
|
|
|
using optstore_helper<T>::optstore_helper;
|
|
|
|
optional_storage(const optional_storage&) = default;
|
|
|
|
optional_storage(optional_storage&&) = default;
|
|
|
|
optional_storage& operator=(const optional_storage &rhs)
|
|
|
|
{ this->assign(rhs); return *this; }
|
|
|
|
optional_storage& operator=(optional_storage&& rhs)
|
|
|
|
{ this->assign(std::move(rhs)); return *this; }
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Non-trivial assignment, non-trivial move construction. */
|
|
|
|
template<typename T>
|
|
|
|
struct optional_storage<T, true, false, false, false> : optstore_helper<T> {
|
|
|
|
using optstore_helper<T>::optstore_helper;
|
|
|
|
optional_storage(const optional_storage&) = default;
|
|
|
|
optional_storage(optional_storage&& rhs)
|
|
|
|
{ if(rhs.mHasValue) this->construct(std::move(rhs.mValue)); }
|
|
|
|
optional_storage& operator=(const optional_storage &rhs)
|
|
|
|
{ this->assign(rhs); return *this; }
|
|
|
|
optional_storage& operator=(optional_storage&& rhs)
|
|
|
|
{ this->assign(std::move(rhs)); return *this; }
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Non-trivial assignment, non-trivial copy construction. */
|
|
|
|
template<typename T>
|
|
|
|
struct optional_storage<T, false, true, false, false> : optstore_helper<T> {
|
|
|
|
using optstore_helper<T>::optstore_helper;
|
|
|
|
optional_storage(const optional_storage &rhs)
|
|
|
|
{ if(rhs.mHasValue) this->construct(rhs.mValue); }
|
|
|
|
optional_storage(optional_storage&&) = default;
|
|
|
|
optional_storage& operator=(const optional_storage &rhs)
|
|
|
|
{ this->assign(rhs); return *this; }
|
|
|
|
optional_storage& operator=(optional_storage&& rhs)
|
|
|
|
{ this->assign(std::move(rhs)); return *this; }
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Completely non-trivial. */
|
|
|
|
template<typename T>
|
|
|
|
struct optional_storage<T, false, false, false, false> : optstore_helper<T> {
|
|
|
|
using optstore_helper<T>::optstore_helper;
|
|
|
|
optional_storage(const optional_storage &rhs)
|
|
|
|
{ if(rhs.mHasValue) this->construct(rhs.mValue); }
|
|
|
|
optional_storage(optional_storage&& rhs)
|
|
|
|
{ if(rhs.mHasValue) this->construct(std::move(rhs.mValue)); }
|
|
|
|
optional_storage& operator=(const optional_storage &rhs)
|
|
|
|
{ this->assign(rhs); return *this; }
|
|
|
|
optional_storage& operator=(optional_storage&& rhs)
|
|
|
|
{ this->assign(std::move(rhs)); return *this; }
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
class optional {
|
|
|
|
using storage_t = optional_storage<T>;
|
|
|
|
|
|
|
|
storage_t mStore;
|
|
|
|
|
2019-06-30 11:21:29 -07:00
|
|
|
public:
|
|
|
|
using value_type = T;
|
|
|
|
|
2021-01-08 19:23:17 -08:00
|
|
|
optional() = default;
|
2021-10-08 15:41:33 -07:00
|
|
|
optional(const optional&) = default;
|
|
|
|
optional(optional&&) = default;
|
2019-06-30 11:21:29 -07:00
|
|
|
optional(nullopt_t) noexcept { }
|
2021-01-02 13:17:16 -08:00
|
|
|
template<typename ...Args>
|
|
|
|
explicit optional(in_place_t, Args&& ...args)
|
|
|
|
: mStore{al::in_place, std::forward<Args>(args)...}
|
2019-07-01 16:27:49 -07:00
|
|
|
{ }
|
2021-01-02 13:17:16 -08:00
|
|
|
~optional() = default;
|
2019-06-30 11:21:29 -07:00
|
|
|
|
2020-12-27 06:43:59 -08:00
|
|
|
std::enable_if_t<std::is_copy_constructible<T>::value && std::is_copy_assignable<T>::value,
|
2021-10-08 15:41:33 -07:00
|
|
|
optional&> operator=(const optional&) = default;
|
2020-12-27 06:43:59 -08:00
|
|
|
std::enable_if_t<std::is_move_constructible<T>::value && std::is_move_assignable<T>::value,
|
2021-10-08 15:41:33 -07:00
|
|
|
optional&> operator=(optional&&) = default;
|
|
|
|
optional& operator=(nullopt_t) noexcept { mStore.reset(); return *this; }
|
2020-12-27 06:43:59 -08:00
|
|
|
template<typename U=T>
|
2021-01-02 13:17:16 -08:00
|
|
|
std::enable_if_t<std::is_constructible<T, U>::value
|
|
|
|
&& std::is_assignable<T&, U>::value
|
|
|
|
&& !std::is_same<std::decay_t<U>, optional<T>>::value
|
|
|
|
&& (!std::is_same<std::decay_t<U>, T>::value || !std::is_scalar<U>::value),
|
2020-12-27 06:43:59 -08:00
|
|
|
optional&> operator=(U&& rhs)
|
2019-07-01 16:27:49 -07:00
|
|
|
{
|
2021-10-08 15:41:33 -07:00
|
|
|
if(mStore.mHasValue)
|
2021-01-02 13:17:16 -08:00
|
|
|
mStore.mValue = std::forward<U>(rhs);
|
2019-07-01 16:27:49 -07:00
|
|
|
else
|
2021-10-08 15:41:33 -07:00
|
|
|
mStore.construct(std::forward<U>(rhs));
|
2019-07-01 16:27:49 -07:00
|
|
|
return *this;
|
|
|
|
}
|
2019-06-30 11:21:29 -07:00
|
|
|
|
2021-01-02 13:17:16 -08:00
|
|
|
const T* operator->() const { return std::addressof(mStore.mValue); }
|
|
|
|
T* operator->() { return std::addressof(mStore.mValue); }
|
2021-07-31 14:21:46 -07:00
|
|
|
const T& operator*() const& { return mStore.mValue; }
|
2021-01-02 13:17:16 -08:00
|
|
|
T& operator*() & { return mStore.mValue; }
|
|
|
|
const T&& operator*() const&& { return std::move(mStore.mValue); }
|
|
|
|
T&& operator*() && { return std::move(mStore.mValue); }
|
2019-06-30 11:21:29 -07:00
|
|
|
|
2021-01-02 13:17:16 -08:00
|
|
|
operator bool() const noexcept { return mStore.mHasValue; }
|
|
|
|
bool has_value() const noexcept { return mStore.mHasValue; }
|
2019-06-30 11:21:29 -07:00
|
|
|
|
2021-01-02 13:17:16 -08:00
|
|
|
T& value() & { return mStore.mValue; }
|
|
|
|
const T& value() const& { return mStore.mValue; }
|
|
|
|
T&& value() && { return std::move(mStore.mValue); }
|
|
|
|
const T&& value() const&& { return std::move(mStore.mValue); }
|
2019-06-30 11:21:29 -07:00
|
|
|
|
|
|
|
template<typename U>
|
|
|
|
T value_or(U&& defval) const&
|
|
|
|
{ return bool{*this} ? **this : static_cast<T>(std::forward<U>(defval)); }
|
|
|
|
template<typename U>
|
|
|
|
T value_or(U&& defval) &&
|
|
|
|
{ return bool{*this} ? std::move(**this) : static_cast<T>(std::forward<U>(defval)); }
|
|
|
|
|
2021-10-08 15:41:33 -07:00
|
|
|
void reset() noexcept { mStore.reset(); }
|
2019-06-30 11:21:29 -07:00
|
|
|
};
|
|
|
|
|
2019-07-01 17:25:58 -07:00
|
|
|
template<typename T>
|
2020-03-22 21:15:12 -07:00
|
|
|
inline optional<std::decay_t<T>> make_optional(T&& arg)
|
|
|
|
{ return optional<std::decay_t<T>>{in_place, std::forward<T>(arg)}; }
|
2019-07-01 17:25:58 -07:00
|
|
|
|
|
|
|
template<typename T, typename... Args>
|
|
|
|
inline optional<T> make_optional(Args&& ...args)
|
|
|
|
{ return optional<T>{in_place, std::forward<Args>(args)...}; }
|
|
|
|
|
|
|
|
template<typename T, typename U, typename... Args>
|
|
|
|
inline optional<T> make_optional(std::initializer_list<U> il, Args&& ...args)
|
|
|
|
{ return optional<T>{in_place, il, std::forward<Args>(args)...}; }
|
|
|
|
|
2019-06-30 11:21:29 -07:00
|
|
|
} // namespace al
|
|
|
|
|
2021-01-01 15:35:46 -08:00
|
|
|
#endif /* AL_OPTIONAL_H */
|