libshit

Just some random shit
git clone https://git.neptards.moe/neptards/libshit.git
Log | Files | Refs | Submodules | README | LICENSE

optional (15571B)


      1 // -*- c++ -*-
      2 #pragma once
      3 
      4 #include <stdexcept>
      5 #include <utility>
      6 
      7 namespace std
      8 {
      9   struct nullopt_t { explicit constexpr nullopt_t(int) {} };
     10   inline constexpr nullopt_t nullopt{0};
     11 
     12   namespace LibshitOptionalDetail
     13   {
     14     // base+destructor
     15     template <typename T, bool = std::is_trivially_destructible_v<T>>
     16     struct Cnt0
     17     {
     18       union
     19       {
     20         char dummy;
     21         T data;
     22       };
     23       bool valid = false;
     24 
     25       constexpr Cnt0() noexcept {}
     26       template <typename... Args>
     27       constexpr Cnt0(std::in_place_t, Args&&... args)
     28         : data(std::forward<Args>(args)...), valid(true) {}
     29 
     30       void Reset() { data.~T(); valid = false; }
     31 
     32       template <typename X, typename U>
     33       void GenCtor(U&& o)
     34       {
     35         if (!o.valid) return;
     36         new (this->buf) T(std::forward<X>(o.data));
     37         this->valid = true;
     38       }
     39 
     40       template <typename X, typename U>
     41       void GenOpEq(U&& o)
     42       {
     43         if (valid && !o.valid) Reset();
     44         if (!valid && o.valid)
     45         {
     46           new (&data) T(std::forward<X>(o.data));
     47           this->valid = true;
     48         }
     49         if (valid && o.valid) data = std::forward<X>(o.data);
     50       }
     51     };
     52 
     53     template <typename T> struct Cnt0<T, false> : Cnt0<T, true>
     54     { ~Cnt0() noexcept { if (this->valid) this->Reset(); } };
     55 
     56 
     57     // copy ctor
     58     template <typename T, bool = std::is_copy_constructible_v<T>,
     59               bool = std::is_trivially_copy_constructible_v<T>>
     60     struct Cnt1;
     61 
     62     template <typename T> struct Cnt1<T, true, true> : Cnt0<T>
     63     { using Cnt0<T>::Cnt0; };
     64 
     65     template <typename T> struct Cnt1<T, false, false> : Cnt0<T>
     66     {
     67       using Cnt0<T>::Cnt0;
     68       Cnt1(const Cnt1&) = delete;
     69       Cnt1(Cnt1&&) = default;
     70       Cnt1& operator=(const Cnt1&) = default;
     71       Cnt1& operator=(Cnt1&&) = default;
     72     };
     73 
     74     template <typename T> struct Cnt1<T, true, false> : Cnt0<T>
     75     {
     76       using Cnt0<T>::Cnt0;
     77       Cnt1(const Cnt1& o) { this->template GenCtor<const T&>(o); }
     78       Cnt1(Cnt1&&) = default;
     79       Cnt1& operator=(const Cnt1&) = default;
     80       Cnt1& operator=(Cnt1&&) = default;
     81     };
     82 
     83     // move ctor
     84     template <typename T, bool = std::is_move_constructible_v<T>,
     85               bool = std::is_trivially_move_constructible_v<T>>
     86     struct Cnt2;
     87 
     88     template <typename T> struct Cnt2<T, true, true> : Cnt1<T>
     89     { using Cnt1<T>::Cnt1; };
     90 
     91     template <typename T> struct Cnt2<T, false, false> : Cnt1<T>
     92     {
     93       using Cnt1<T>::Cnt1;
     94       Cnt2(const Cnt2&) = default;
     95       Cnt2(Cnt2&&) = delete;
     96       Cnt2& operator=(const Cnt2&) = default;
     97       Cnt2& operator=(Cnt2&&) = default;
     98     };
     99 
    100     template <typename T> struct Cnt2<T, true, false> : Cnt1<T>
    101     {
    102       using Cnt1<T>::Cnt1;
    103       Cnt2(const Cnt2&) = default;
    104       Cnt2(Cnt2&& o) noexcept(std::is_nothrow_move_constructible_v<T>)
    105       { this->template GenCtor<T>(std::move(o)); }
    106       Cnt2& operator=(const Cnt2&) = default;
    107       Cnt2& operator=(Cnt2&&) = default;
    108     };
    109 
    110     // copy op=
    111     template <
    112       typename T,
    113       bool = std::is_copy_constructible_v<T> && std::is_copy_assignable_v<T>,
    114       bool = std::is_trivially_copy_constructible_v<T> &&
    115         std::is_trivially_copy_assignable_v<T> &&
    116         std::is_trivially_destructible_v<T>>
    117     struct Cnt3;
    118 
    119     template <typename T> struct Cnt3<T, true, true> : Cnt2<T>
    120     { using Cnt2<T>::Cnt2; };
    121 
    122     template <typename T> struct Cnt3<T, false, false> : Cnt2<T>
    123     {
    124       using Cnt2<T>::Cnt2;
    125       Cnt3(const Cnt3&) = default;
    126       Cnt3(Cnt3&&) = default;
    127       Cnt3& operator=(const Cnt3&) = delete;
    128       Cnt3& operator=(Cnt3&&) = default;
    129     };
    130 
    131     template <typename T> struct Cnt3<T, true, false> : Cnt2<T>
    132     {
    133       using Cnt2<T>::Cnt2;
    134       Cnt3(const Cnt3&) = default;
    135       Cnt3(Cnt3&&) = default;
    136       Cnt3& operator=(const Cnt3& o)
    137       { this->template GenOpEq<const T&>(o); return *this; }
    138       Cnt3& operator=(Cnt3&&) = default;
    139     };
    140 
    141     // move op=
    142     template <
    143       typename T,
    144       bool = std::is_move_constructible_v<T> && std::is_move_assignable_v<T>,
    145       bool = std::is_trivially_move_constructible_v<T> &&
    146         std::is_trivially_move_assignable_v<T> &&
    147         std::is_trivially_destructible_v<T>>
    148     struct Cnt4;
    149 
    150     template <typename T> struct Cnt4<T, true, true> : Cnt3<T>
    151     { using Cnt3<T>::Cnt3; };
    152 
    153     template <typename T> struct Cnt4<T, false, false> : Cnt3<T>
    154     {
    155       using Cnt3<T>::Cnt3;
    156       Cnt4(const Cnt4&) = default;
    157       Cnt4(Cnt4&&) = default;
    158       Cnt4& operator=(const Cnt4&) = default;
    159       Cnt4& operator=(Cnt4&&) = delete;
    160     };
    161 
    162     template <typename T> struct Cnt4<T, true, false> : Cnt3<T>
    163     {
    164       using Cnt3<T>::Cnt3;
    165       Cnt4(const Cnt4&) = default;
    166       Cnt4(Cnt4&&) = default;
    167       Cnt4& operator=(const Cnt4& o) = default;
    168       Cnt4& operator=(Cnt4&& o)
    169         noexcept(std::is_nothrow_move_assignable_v<T> &&
    170                  std::is_nothrow_move_constructible_v<T>)
    171       { this->template GenOpEq<T>(std::move(o)); return *this; }
    172     };
    173   }
    174 
    175 
    176   struct bad_optional_access : std::exception
    177   {
    178     const char* what() const noexcept override
    179     { return "bad_exception_access"; }
    180   };
    181 
    182   template <typename T>
    183   class optional : private LibshitOptionalDetail::Cnt4<T>
    184   {
    185     template <typename U> friend class optional;
    186 
    187     using Base = LibshitOptionalDetail::Cnt4<T>;
    188 
    189     template <typename U>
    190     inline static constexpr const bool ConvCtor =
    191       !std::is_constructible_v<T, std::optional<U>&> &&
    192       !std::is_constructible_v<T, const std::optional<U>&> &&
    193       !std::is_constructible_v<T, std::optional<U>&&> &&
    194       !std::is_constructible_v<T, const std::optional<U>&&> &&
    195       !std::is_convertible_v<std::optional<U>&, T> &&
    196       !std::is_convertible_v<const std::optional<U>&, T> &&
    197       !std::is_convertible_v<std::optional<U>&&, T> &&
    198       !std::is_convertible_v<const std::optional<U>&&, T>;
    199 
    200     template <typename U>
    201     inline static constexpr const bool ConvOpEq = ConvCtor<U> &&
    202       !std::is_assignable_v<T&, std::optional<U>&> &&
    203       !std::is_assignable_v<T&, const std::optional<U>&> &&
    204       !std::is_assignable_v<T&, std::optional<U>&&> &&
    205       !std::is_assignable_v<T&, const std::optional<U>&&>;
    206 
    207     template <typename U>
    208     inline static constexpr const bool ValCtor =
    209       std::is_constructible_v<T, U&&> &&
    210       !std::is_same_v<std::in_place_t, std::decay_t<U>> &&
    211       !std::is_same_v<std::optional<T>, std::decay_t<U>>;
    212 
    213   public:
    214     using value_type = T;
    215 
    216     constexpr optional() noexcept {}
    217     constexpr optional(nullopt_t) noexcept {}
    218     constexpr optional(const optional&) = default;
    219     constexpr optional(optional&&) = default;
    220 
    221     template <typename U, std::enable_if_t<
    222       std::is_constructible_v<T, const U&> && ConvCtor<U> &&
    223       std::is_convertible_v<const U&, T>>* = nullptr>
    224     optional(const optional<U>& o)
    225     { this->template GenCtor<const U&>(o); }
    226 
    227     template <typename U, std::enable_if_t<
    228       std::is_constructible_v<T, const U&> && ConvCtor<U> &&
    229       !std::is_convertible_v<const U&, T>>* = nullptr>
    230     explicit optional(const optional<U>& o)
    231     { this->template GenCtor<const U&>(o); }
    232 
    233     template <typename U, std::enable_if_t<
    234       std::is_constructible_v<T, U&&> && ConvCtor<U> &&
    235       std::is_convertible_v<U&&, T>>* = nullptr>
    236     optional(optional<U>&& o)
    237     { this->template GenCtor<U>(std::move(o)); }
    238 
    239     template <typename U, std::enable_if_t<
    240       std::is_constructible_v<T, U&&> && ConvCtor<U> &&
    241       !std::is_convertible_v<U&&, T>>* = nullptr>
    242     explicit optional(optional<U>&& o)
    243     { this->template GenCtor<U>(std::move(o)); }
    244 
    245     template <typename... Args,
    246               std::enable_if_t<std::is_constructible_v<T, Args...>>* = nullptr>
    247     constexpr explicit optional(std::in_place_t, Args&&... args)
    248       : Base(in_place, std::forward<Args>(args)...) {}
    249 
    250     template <typename U, typename... Args, std::enable_if_t<
    251       std::is_constructible_v<T, std::initializer_list<U>&, Args&&...>>* = nullptr>
    252     constexpr explicit optional(
    253       std::in_place_t, std::initializer_list<U> list, Args&&... args)
    254       : Base(in_place, list, std::forward<Args>(args)...) {}
    255 
    256     template <typename U = value_type, std::enable_if_t<
    257       ValCtor<U> && std::is_convertible_v<U&&, T>>* = nullptr>
    258     constexpr optional(U&& value)
    259       : Base(in_place, std::forward<U>(value)) {}
    260 
    261     template <typename U = value_type, std::enable_if_t<
    262       ValCtor<U> && !std::is_convertible_v<U&&, T>>* = nullptr>
    263     explicit constexpr optional(U&& value)
    264       : Base(in_place, std::forward<U>(value)) {}
    265 
    266     // ---
    267 
    268     optional& operator=(const optional&) = default;
    269     optional& operator=(optional&&) = default;
    270 
    271     optional& operator=(std::nullopt_t) noexcept
    272     { reset(); return *this; }
    273 
    274     template <typename U = T, std::enable_if_t<
    275       !std::is_same_v<std::decay_t<U>, std::optional<T>> &&
    276       std::is_constructible_v<T, U> && std::is_assignable_v<T&, U> && (
    277         !std::is_scalar_v<T> || !std::is_same_v<std::decay_t<U>, T>)>* = nullptr>
    278     optional& operator=(U&& value)
    279     {
    280       if (this->valid) this->data = std::forward<U>(value);
    281       else { new (&this->data) T(std::forward<U>(value)); this->valid = true; }
    282       return *this;
    283     }
    284 
    285     template <typename U, std::enable_if_t<
    286       ConvOpEq<U> && std::is_constructible_v<T, const U&> &&
    287       std::is_assignable_v<T&, const U&>>* = nullptr>
    288     optional& operator=(const optional<U>& o)
    289     { this->template GenOpEq<const U&>(o); return *this; }
    290 
    291     template <typename U, std::enable_if_t<
    292       ConvOpEq<U> && std::is_constructible_v<T, U> &&
    293       std::is_assignable_v<T&, U>>* = nullptr>
    294     optional& operator=(optional<U>&& o)
    295     { this->template GenOpEq<U>(std::move(o)); return *this; }
    296 
    297     // ---
    298 
    299     constexpr T* operator->() { return &this->data; }
    300     constexpr const T* operator->() const { return &this->data; }
    301 
    302     constexpr T& operator*() & { return this->data; }
    303     constexpr const T& operator*() const & { return this->data; }
    304     constexpr T&& operator*() && { return std::move(this->data); }
    305     constexpr const T&& operator*() const && { return std::move(this->data); }
    306 
    307     constexpr explicit operator bool() const noexcept { return this->valid; }
    308     constexpr bool has_value() const noexcept { return this->valid; }
    309 
    310     constexpr T& value() &
    311     { if (this->valid) return this->data; else throw bad_optional_access{}; }
    312     constexpr const T& value() const &
    313     { if (this->valid) return this->data; else throw bad_optional_access{}; }
    314 
    315     constexpr T&& value() &&
    316     {
    317       if (this->valid) return std::move(this->data);
    318       else throw bad_optional_access{};
    319     }
    320     constexpr const T&& value() const &&
    321     {
    322       if (this->valid) return std::move(this->data);
    323       else throw bad_optional_access{};
    324     }
    325 
    326     template <typename U> constexpr T value_or(U&& def) const&
    327     {
    328       if (this->valid) return this->data;
    329       else return static_cast<T>(std::forward<U>(def));
    330     }
    331 
    332     template <typename U> constexpr T value_or(U&& def) &&
    333     {
    334       if (this->valid) return std::move(this->data);
    335       else return static_cast<T>(std::forward<U>(def));
    336     }
    337 
    338     // ---
    339 
    340     void swap(optional& o) noexcept(std::is_nothrow_move_constructible_v<T> &&
    341                                     std::is_nothrow_swappable_v<T>)
    342     {
    343       if (this->valid && !o.valid)
    344       {
    345         o.template GenCtor<T>(std::move(this->data));
    346         this->Reset();
    347       }
    348       if (!this->valid && o.valid)
    349       {
    350         this->template GenCtor<T>(std::move(o.data));
    351         o.Reset();
    352       }
    353       if (this->valid && o.valid)
    354       {
    355         using std::swap;
    356         swap(this->data, o.data);
    357       }
    358     }
    359 
    360     void reset() noexcept
    361     {
    362       // both gcc and clang can't optimize if (value) Reset() into value = false
    363       // with trivially destructible types, so be a bit more explicit
    364       if constexpr (std::is_trivially_destructible_v<T>) this->valid = false;
    365       else if (this->valid) this->Reset();
    366     }
    367 
    368     // ---
    369 
    370     template <typename... Args>
    371     T& emplace(Args&&... args)
    372     {
    373       reset();
    374       new (&this->data) T(std::forward<Args>(args)...);
    375       this->valid = true;
    376       return this->data;
    377     }
    378 
    379     template <typename U, typename... Args, std::enable_if_t<
    380       std::is_constructible_v<T, std::initializer_list<U>&, Args&&...>>* = nullptr>
    381     T& emplace(std::initializer_list<U> list, Args&&... args)
    382     {
    383       reset();
    384       new (&this->data) T(list, std::forward<Args>(args)...);
    385       this->valid = true;
    386       return this->data;
    387     }
    388 
    389   };
    390 
    391   template <typename T> optional(T) -> optional<T>;
    392 
    393   // ---
    394 
    395   template <typename T>
    396   constexpr optional<std::decay_t<T>> make_optional(T&& value)
    397   { return optional<std::decay_t<T>>(std::forward<T>(value)); }
    398 
    399   template <typename T, typename... Args>
    400   constexpr optional<T> make_optional(Args&&... args)
    401   { return optional<T>(std::in_place, std::forward<Args>(args)...); }
    402 
    403   template <typename T, typename U, typename... Args>
    404   constexpr optional<T> make_optional(std::initializer_list<U> list, Args&&... args)
    405   { return optional<T>(std::in_place, list, std::forward<Args>(args)...); }
    406 
    407   // ---
    408 
    409   template <typename T, std::enable_if_t<
    410     std::is_move_constructible_v<T> && std::is_swappable_v<T>>* = nullptr>
    411   void swap(optional<T>& lhs, optional<T>& rhs)
    412     noexcept(noexcept(lhs.swap(rhs)))
    413   { lhs.swap(rhs); }
    414 
    415   // todo: enabled/disabled not supported
    416   template <typename T>
    417   struct hash<optional<T>>
    418   {
    419     size_t operator()(const optional<T>& o) const
    420     { return o ? hash<std::remove_const_t<T>>()(*o) : 0xb19b00b2; }
    421   };
    422 
    423   // ---
    424 
    425 #define LIBSHIT_GEN(op)                                                  \
    426   template <typename T>                                                  \
    427   constexpr bool operator op(const optional<T>& a, const optional<T>& b) \
    428   {                                                                      \
    429     if (!a || !b) return bool(a) op bool(b);                             \
    430     return *a op *b;                                                     \
    431   }                                                                      \
    432   template <typename T>                                                  \
    433   constexpr bool operator op(const optional<T>& o, nullopt_t)            \
    434   { return bool(o) op false; }                                           \
    435   template <typename T>                                                  \
    436   constexpr bool operator op(nullopt_t, const optional<T>& o)            \
    437   { return false op bool(o); }                                           \
    438   template <typename T, typename U>                                      \
    439   constexpr bool operator op(const optional<T>& a, const U& b)           \
    440   {                                                                      \
    441     if (!a) return bool(a) op true;                                      \
    442     return *a op b;                                                      \
    443   }                                                                      \
    444   template <typename T, typename U>                                      \
    445   constexpr bool operator op(const T& a, const optional<U>& b)           \
    446   {                                                                      \
    447     if (!b) return true op bool(b);                                      \
    448     return a op *b;                                                      \
    449   }
    450   LIBSHIT_GEN(==) LIBSHIT_GEN(!=) LIBSHIT_GEN(<) LIBSHIT_GEN(<=)
    451   LIBSHIT_GEN(>) LIBSHIT_GEN(>=)
    452 #undef LIBSHIT_GEN
    453 }