From 9f58a6c3994cc811598fa6d06cc97f209f19bcfc Mon Sep 17 00:00:00 2001 From: q66 Date: Sun, 28 Jun 2015 21:28:29 +0100 Subject: [PATCH] add octa::Maybe, representing optional values for simpler error handling --- octa/functional.hh | 2 +- octa/maybe.hh | 430 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 431 insertions(+), 1 deletion(-) create mode 100644 octa/maybe.hh diff --git a/octa/functional.hh b/octa/functional.hh index ea81c1c..09a53ce 100644 --- a/octa/functional.hh +++ b/octa/functional.hh @@ -176,7 +176,7 @@ template T from_big_endian(T x) { return FromBigEndian()(x); } /* hash */ template struct ToHash { - using Argument = const T &; + using Argument = T; using Result = octa::Size; octa::Size operator()(const T &v) const { diff --git a/octa/maybe.hh b/octa/maybe.hh new file mode 100644 index 0000000..445a057 --- /dev/null +++ b/octa/maybe.hh @@ -0,0 +1,430 @@ +/* Option type implementation. Inspired by libc++. + * + * This file is part of OctaSTD. See COPYING.md for futher information. + */ + +#ifndef OCTA_MAYBE_HH +#define OCTA_MAYBE_HH + +#include "octa/types.hh" +#include "octa/type_traits.hh" +#include "octa/memory.hh" +#include "octa/utility.hh" +#include "octa/initializer_list.hh" +#include "octa/functional.hh" + +namespace octa { + +struct InPlace {}; +constexpr InPlace in_place = InPlace(); + +struct Nothing { + explicit constexpr Nothing(int) {} +}; +constexpr Nothing nothing = Nothing(0); + +namespace detail { + template::value> + class MaybeStorage { + protected: + using Value = T; + union { + char p_null_state; + Value p_value; + }; + bool p_engaged = false; + + constexpr MaybeStorage(): p_null_state('\0') {} + + MaybeStorage(const MaybeStorage &v): p_engaged(v.p_engaged) { + if (p_engaged) ::new(octa::address_of(p_value)) Value(v.p_value); + } + + MaybeStorage(MaybeStorage &&v): p_engaged(v.p_engaged) { + if (p_engaged) + ::new(octa::address_of(p_value)) Value(octa::move(v.p_value)); + } + + constexpr MaybeStorage(const Value &v): p_value(v), p_engaged(true) {} + constexpr MaybeStorage(Value &&v): p_value(octa::move(v)), p_engaged(true) {} + + template constexpr MaybeStorage(octa::InPlace, A &&...args): + p_value(octa::forward(args)...), p_engaged(true) {} + + ~MaybeStorage() { + if (p_engaged) p_value.~Value(); + } + }; + + template + class MaybeStorage { + protected: + using Value = T; + union { + char p_null_state; + Value p_value; + }; + bool p_engaged = false; + + constexpr MaybeStorage(): p_null_state('\0') {} + + MaybeStorage(const MaybeStorage &v): p_engaged(v.p_engaged) { + if (p_engaged) ::new(octa::address_of(p_value)) Value(v.p_value); + } + + MaybeStorage(MaybeStorage &&v): p_engaged(v.p_engaged) { + if (p_engaged) + ::new(octa::address_of(p_value)) Value(octa::move(v.p_value)); + } + + constexpr MaybeStorage(const Value &v): p_value(v), p_engaged(true) {} + constexpr MaybeStorage(Value &&v): + p_value(octa::move(v)), p_engaged(true) {} + + template + constexpr MaybeStorage(octa::InPlace, A &&...args): + p_value(octa::forward(args)...), p_engaged(true) {} + }; +} + +template +class Maybe: private octa::detail::MaybeStorage { + using Base = octa::detail::MaybeStorage; +public: + using Value = T; + + static_assert(!octa::IsReference::value, + "Initialization of Maybe with a reference type is not allowed."); + static_assert(!octa::IsSame, octa::InPlace>::value, + "Initialization of Maybe with InPlace is not allowed."); + static_assert(!octa::IsSame, octa::Nothing>::value, + "Initialization of Maybe with Nothing is not allowed."); + static_assert(octa::IsObject::value, + "Initialization of Maybe with non-object type is not allowed."); + static_assert(octa::IsDestructible::value, + "Initialization of Maybe with a non-destructible object is not allowed."); + + constexpr Maybe() {} + Maybe(const Maybe &) = default; + Maybe(Maybe &&) = default; + constexpr Maybe(octa::Nothing) {} + constexpr Maybe(const Value &v): Base(v) {} + constexpr Maybe(Value &&v): Base(octa::move(v)) {} + + template::value>> + constexpr explicit Maybe(octa::InPlace, A &&...args): Base(octa::in_place, + octa::forward(args)...) {} + + template &, A...>::value>> + constexpr explicit + Maybe(octa::InPlace, std::initializer_list il, A &&...args): + Base(octa::in_place, il, octa::forward(args)...) {} + + ~Maybe() = default; + + Maybe &operator=(Nothing) { + if (this->p_engaged) { + this->p_value.~Value(); + this->p_engaged = false; + } + return *this; + } + + Maybe &operator=(const Maybe &v) { + if (this->p_engaged == v.p_engaged) { + if (this->p_engaged) this->p_value = v.p_value; + } else { + if (this->p_engaged) + this->p_value.~Value(); + else + ::new(octa::address_of(this->p_value)) Value(v.p_value); + this->p_engaged = v.p_engaged; + } + return *this; + } + + Maybe &operator=(Maybe &&v) { + if (this->p_engaged == v.p_engaged) { + if (this->p_engaged) this->p_value = octa::move(v.p_value); + } else { + if (this->p_engaged) + this->p_value.~Value(); + else { + ::new(octa::address_of(this->p_value)) + Value(octa::move(v.p_value)); + } + this->p_engaged = v.p_engaged; + } + return *this; + } + + template, Value>::value && + octa::IsConstructible::value && + octa::IsAssignable::value + >> + Maybe &operator=(U &&v) { + if (this->p_engaged) { + this->p_value = octa::forward(v); + } else { + ::new(octa::address_of(this->p_value)) Value(octa::forward(v)); + this->p_engaged = true; + } + return *this; + } + + template::value + >> + void emplace(A &&...args) { + *this = octa::nothing; + ::new(octa::address_of(this->p_value)) + Value(octa::forward(args)...); + this->p_engaged = true; + } + + template &, A...>::value + >> + void emplace(std::initializer_list il, A &&...args) { + *this = octa::nothing; + ::new(octa::address_of(this->p_value)) + Value(il, octa::forward(args)...); + this->p_engaged = true; + } + + constexpr const Value *operator->() const { + return octa::address_of(this->p_value); + } + + Value *operator->() { + return octa::address_of(this->p_value); + } + + constexpr const Value &operator*() const { + return this->p_value; + } + + Value &operator*() { + return this->p_value; + } + + constexpr explicit operator bool() const { return this->p_engaged; } + + constexpr const Value &value() const { + return this->p_value; + } + + Value &value() { + return this->p_value; + } + + template + constexpr Value value_or(U &&v) const & { + static_assert(octa::IsCopyConstructible::value, + "Maybe::value_or: T must be copy constructible"); + static_assert(octa::IsConvertible::value, + "Maybe::value_or: U must be convertible to T"); + return this->p_engaged ? this->p_value : Value(octa::forward(v)); + } + + template + Value value_or(U &&v) && { + static_assert(octa::IsMoveConstructible::value, + "Maybe::value_or: T must be copy constructible"); + static_assert(octa::IsConvertible::value, + "Maybe::value_or: U must be convertible to T"); + return this->p_engaged ? octa::move(this->p_value) + : Value(octa::forward(v)); + } + + void swap(Maybe &v) { + if (this->p_engaged == v.p_engaged) { + if (this->p_engaged) octa::swap(this->p_value, v.p_value); + } else { + if (this->p_engaged) { + ::new(octa::address_of(v.p_value)) + Value(octa::move(this->p_value)); + this->p_value.~Value(); + } else { + ::new(octa::address_of(this->p_value)) + Value(octa::move(v.p_value)); + v.p_value.~Value(); + } + octa::swap(this->p_engaged, v.p_engaged); + } + } + + octa::Size to_hash() const { + return this->p_engaged ? octa::ToHash()(this->p_value) : 0; + } +}; + +/* maybe vs maybe */ + +template +static inline constexpr bool operator==(const Maybe &a, const Maybe &b) { + return (bool(a) != bool(b)) ? false : (!bool(a) ? true : (*a == *b)); +} + +template +static inline constexpr bool operator!=(const Maybe &a, const Maybe &b) { + return !(a == b); +} + +template +static inline constexpr bool operator<(const Maybe &a, const Maybe &b) { + return !bool(b) ? false : (!bool(a) ? true : (*a < *b)); +} + +template +static inline constexpr bool operator>(const Maybe &a, const Maybe &b) { + return b < a; +} + +template +static inline constexpr bool operator<=(const Maybe &a, const Maybe &b) { + return !(b < a); +} + +template +static inline constexpr bool operator>=(const Maybe &a, const Maybe &b) { + return !(a < b); +} + +/* maybe vs nothing */ + +template +static inline constexpr bool operator==(const Maybe &v, Nothing) { + return !bool(v); +} + +template +static inline constexpr bool operator==(Nothing, const Maybe &v) { + return !bool(v); +} + +template +static inline constexpr bool operator!=(const Maybe &v, Nothing) { + return bool(v); +} + +template +static inline constexpr bool operator!=(Nothing, const Maybe &v) { + return bool(v); +} + +template +static inline constexpr bool operator<(const Maybe &, Nothing) { + return false; +} + +template +static inline constexpr bool operator<(Nothing, const Maybe &v) { + return bool(v); +} + +template +static inline constexpr bool operator<=(const Maybe &v, Nothing) { + return !bool(v); +} + +template +static inline constexpr bool operator<=(Nothing, const Maybe &) { + return true; +} + +template +static inline constexpr bool operator>(const Maybe &v, Nothing) { + return bool(v); +} + +template +static inline constexpr bool operator>(Nothing, const Maybe &) { + return false; +} + +template +static inline constexpr bool operator>=(const Maybe &, Nothing) { + return true; +} + +template +static inline constexpr bool operator>=(Nothing, const Maybe &v) { + return !bool(v); +} + +/* maybe vs T */ + +template +static inline constexpr bool operator==(const Maybe &a, const T &b) { + return bool(a) ? (*a == b) : false; +} + +template +static inline constexpr bool operator==(const T &b, const Maybe &a) { + return bool(a) ? (*a == b) : false; +} + +template +static inline constexpr bool operator!=(const Maybe &a, const T &b) { + return bool(a) ? !(*a == b) : true; +} + +template +static inline constexpr bool operator!=(const T &b, const Maybe &a) { + return bool(a) ? !(*a == b) : true; +} + +template +static inline constexpr bool operator<(const Maybe &a, const T &b) { + return bool(a) ? octa::Less()(*a, b) : true; +} + +template +static inline constexpr bool operator<(const T &b, const Maybe &a) { + return bool(a) ? octa::Less()(b, *a) : false; +} + +template +static inline constexpr bool operator<=(const Maybe &a, const T &b) { + return !(a > b); +} + +template +static inline constexpr bool operator<=(const T &b, const Maybe &a) { + return !(b > a); +} + +template +static inline constexpr bool operator>(const Maybe &a, const T &b) { + return bool(a) ? (b < a) : true; +} + +template +static inline constexpr bool operator>(const T &b, const Maybe &a) { + return bool(a) ? (a < b) : true; +} + +template +static inline constexpr bool operator>=(const Maybe &a, const T &b) { + return !(a < b); +} + +template +static inline constexpr bool operator>=(const T &b, const Maybe &a) { + return !(b < a); +} + +/* make maybe */ + +template +constexpr Maybe> make_maybe(T &&v) { + return Maybe>(octa::forward(v)); +} + +} /* namespace octa */ + +#endif \ No newline at end of file