diff options
author | Johannes Stoelp <johannes.stoelp@gmail.com> | 2023-07-02 22:33:19 +0200 |
---|---|---|
committer | Johannes Stoelp <johannes.stoelp@gmail.com> | 2023-07-02 22:33:19 +0200 |
commit | d9743696562fc85643e016d778ee994f0b9296db (patch) | |
tree | 4cf08da0eb8569e227a412a6da45d1c476ffff78 /option.h | |
download | cpp-utils-d9743696562fc85643e016d778ee994f0b9296db.tar.gz cpp-utils-d9743696562fc85643e016d778ee994f0b9296db.zip |
add initial state of bitfield and option
Diffstat (limited to 'option.h')
-rw-r--r-- | option.h | 130 |
1 files changed, 130 insertions, 0 deletions
diff --git a/option.h b/option.h new file mode 100644 index 0000000..b9ebe2f --- /dev/null +++ b/option.h @@ -0,0 +1,130 @@ +#include <cassert> +#include <new> // placement new +#include <type_traits> +#include <utility> // move + +namespace option { +namespace impl { +/// Definition of std::is_trivially_destructible_v for older c++ std versions. +template <typename T> +constexpr bool is_trivially_destructible_v = + std::is_trivially_destructible<T>::value; + +/// Definition of std::enable_if_t for older c++ std versions. +template <bool Cond, typename T = bool> +using enable_if_t = typename std::enable_if<Cond, T>::type; +} // namespace impl + +/// The NONE type. +struct none {}; + +// The OPTION type. +template <typename T> +struct option { + static_assert(!std::is_reference<T>::value, "T must not be a reference type"); + + // -- CONSTRUCTOR - NONE ----------------------------------------------------- + + constexpr option() = default; + constexpr option(none) : option() {} + + // -- CONSTRUCTOR - VALUE ---------------------------------------------------- + + constexpr option(const option& op) : m_has_value{op.has_value()} { + if (op.has_value()) { + // Copy construct from inner VALUE of OP. + new (m_value) T(op.value()); + } + } + + constexpr option(option&& op) : m_has_value{op.has_value()} { + if (op.m_has_value) { + // Move construct from inner VALUE of OP. + new (m_value) T(std::move(op.take())); + } + } + + template <typename U = T> + constexpr option(T&& val) : m_has_value{true} { + new (m_value) T(std::move(val)); + } + + // -- DESTRUCTOR ------------------------------------------------------------- + + ~option() { + reset(); + } + + // -- MODIFIER --------------------------------------------------------------- + + template <typename... Params> + constexpr T& emplace(Params&&... params) { + reset(); + new (m_value) T(std::forward<Params>(params)...); + m_has_value = true; + return value(); + } + + // -- OPERATOR --------------------------------------------------------------- + + /// Conversion to BOOL, true iff option holds a VALUE. + /// + /// Marked as explicit to disable implicit conversion of option objects to + /// bool in the following case: + /// + /// if (opt1 == opt2) { + /// ... + /// } + constexpr explicit operator bool() const { + return has_value(); + } + + // -- ACCESSOR --------------------------------------------------------------- + + constexpr bool has_value() const { + return m_has_value; + } + + constexpr T& value() & { + assert(m_has_value); + return *reinterpret_cast<T*>(m_value); + } + + constexpr const T& value() const& { + assert(m_has_value); + return *reinterpret_cast<const T*>(m_value); + } + + constexpr T value() && { + return take(); + } + + constexpr T take() { + assert(m_has_value); + T val = std::move(value()); + reset(); + return val; + } + + // -- INTERNAL --------------------------------------------------------------- + + private: + template <typename U = T, + impl::enable_if_t<!impl::is_trivially_destructible_v<U>> = true> + constexpr void reset() { + if (m_has_value) { + value().~T(); + m_has_value = false; + } + } + + template <typename U = T, + impl::enable_if_t<impl::is_trivially_destructible_v<U>> = true> + constexpr void reset() { + m_has_value = false; + } + + alignas(T) char m_value[sizeof(T)]; + bool m_has_value{false}; +}; +} // namespace option |