diff options
Diffstat (limited to 'common/aloptional.h')
-rw-r--r-- | common/aloptional.h | 149 |
1 files changed, 149 insertions, 0 deletions
diff --git a/common/aloptional.h b/common/aloptional.h new file mode 100644 index 00000000..269cba0e --- /dev/null +++ b/common/aloptional.h @@ -0,0 +1,149 @@ +#ifndef AL_OPTIONAL_H +#define AL_OPTIONAL_H + +#include <initializer_list> +#include <type_traits> +#include <utility> + +#include "almalloc.h" + +namespace al { + +#define REQUIRES(...) bool rt_=true, typename std::enable_if<rt_ && (__VA_ARGS__),bool>::type = true + +struct nullopt_t { }; +struct in_place_t { }; + +constexpr nullopt_t nullopt{}; +constexpr in_place_t in_place{}; + +template<typename T> +class optional { + bool mHasValue{false}; + union { + char mDummy[sizeof(T)]{}; + T mValue; + }; + + template<typename... Args> + void DoConstruct(Args&& ...args) + { + ::new (std::addressof(mValue)) T{std::forward<Args>(args)...}; + mHasValue = true; + } + +public: + using value_type = T; + + optional() noexcept = default; + optional(nullopt_t) noexcept { } + template<REQUIRES(std::is_copy_constructible<T>::value)> + optional(const optional &rhs) { if(rhs) DoConstruct(*rhs); } + template<REQUIRES(std::is_move_constructible<T>::value)> + optional(optional&& rhs) { if(rhs) DoConstruct(std::move(*rhs)); } + template<typename... Args, REQUIRES(std::is_constructible<T, Args...>::value)> + explicit optional(in_place_t, Args&& ...args) : mHasValue{true} + , mValue{std::forward<Args>(args)...} + { } + template<typename U, typename... Args, REQUIRES(std::is_constructible<T, std::initializer_list<U>&, Args...>::value)> + explicit optional(in_place_t, std::initializer_list<U> il, Args&& ...args) + : mHasValue{true}, mValue{il, std::forward<Args>(args)...} + { } + template<typename U=value_type, REQUIRES(std::is_constructible<T, U&&>::value && + !std::is_same<typename std::decay<U>::type, in_place_t>::value && + !std::is_same<typename std::decay<U>::type, optional<T>>::value && + std::is_constructible<U&&, T>::value)> + constexpr explicit optional(U&& value) : mHasValue{true}, mValue{std::forward<U>(value)} + { } + template<typename U=value_type, REQUIRES(std::is_constructible<T, U&&>::value && + !std::is_same<typename std::decay<U>::type, in_place_t>::value && + !std::is_same<typename std::decay<U>::type, optional<T>>::value && + !std::is_constructible<U&&, T>::value)> + constexpr optional(U&& value) : mHasValue{true}, mValue{std::forward<U>(value)} + { } + ~optional() { if(mHasValue) al::destroy_at(std::addressof(mValue)); } + + optional& operator=(nullopt_t) noexcept { reset(); return *this; } + template<REQUIRES(std::is_copy_constructible<T>::value && std::is_copy_assignable<T>::value)> + optional& operator=(const optional &rhs) + { + if(!rhs) + reset(); + else if(*this) + mValue = *rhs; + else + DoConstruct(*rhs); + return *this; + } + template<REQUIRES(std::is_move_constructible<T>::value && std::is_move_assignable<T>::value)> + optional& operator=(optional&& rhs) + { + if(!rhs) + reset(); + else if(*this) + mValue = std::move(*rhs); + else + DoConstruct(std::move(*rhs)); + return *this; + } + template<typename U=T, REQUIRES(std::is_constructible<T, U>::value && + std::is_assignable<T&, U>::value && + !std::is_same<typename std::decay<U>::type, optional<T>>::value && + (!std::is_same<typename std::decay<U>::type, T>::value || + !std::is_scalar<U>::value))> + optional& operator=(U&& rhs) + { + if(*this) + mValue = std::forward<U>(rhs); + else + DoConstruct(std::forward<U>(rhs)); + return *this; + } + + const T* operator->() const { return std::addressof(mValue); } + T* operator->() { return std::addressof(mValue); } + const T& operator*() const& { return mValue; } + T& operator*() & { return mValue; } + const T&& operator*() const&& { return std::move(mValue); } + T&& operator*() && { return std::move(mValue); } + + operator bool() const noexcept { return mHasValue; } + bool has_value() const noexcept { return mHasValue; } + + T& value() & { return mValue; } + const T& value() const& { return mValue; } + T&& value() && { return std::move(mValue); } + const T&& value() const&& { return std::move(mValue); } + + 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)); } + + void reset() noexcept + { + if(mHasValue) + al::destroy_at(std::addressof(mValue)); + mHasValue = false; + } +}; + +template<typename T> +inline optional<typename std::decay<T>::type> make_optional(T&& arg) +{ return optional<typename std::decay<T>::type>{in_place, std::forward<T>(arg)}; } + +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)...}; } + +#undef REQUIRES + +} // namespace al + +#endif /* AL_SPAN_H */ |