| /* | |
| * Copyright © 2025 Google, Inc. | |
| * | |
| * This is part of HarfBuzz, a text shaping library. | |
| * | |
| * Permission is hereby granted, without written agreement and without | |
| * license or royalty fees, to use, copy, modify, and distribute this | |
| * software and its documentation for any purpose, provided that the | |
| * above copyright notice and the following two paragraphs appear in | |
| * all copies of this software. | |
| * | |
| * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR | |
| * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES | |
| * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN | |
| * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH | |
| * DAMAGE. | |
| * | |
| * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, | |
| * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND | |
| * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS | |
| * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO | |
| * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. | |
| * | |
| * Google Author(s): Garret Rieger | |
| */ | |
| #ifndef HB_RESULT_HH | |
| #define HB_RESULT_HH | |
| #include "hb.hh" | |
| // Helper structs for Ok(...) and Err(...), not used directly | |
| // by users of hb_result_t. | |
| template <typename T> | |
| struct hb_result_ok_t | |
| { | |
| T value; | |
| }; | |
| template <> | |
| struct hb_result_ok_t<void> {}; | |
| template <typename E> | |
| struct hb_result_err_t | |
| { | |
| E error; | |
| template <typename OtherE> | |
| constexpr bool operator == (const hb_result_err_t<OtherE>& other) const { return error == other.error; } | |
| template <typename OtherE> | |
| constexpr bool operator != (const hb_result_err_t<OtherE>& other) const { return error != other.error; } | |
| }; | |
| template <typename X> struct _hb_is_result_tag : hb_false_type {}; | |
| template <typename X> struct _hb_is_result_tag<hb_result_ok_t<X>> : hb_true_type {}; | |
| template <typename X> struct _hb_is_result_tag<hb_result_err_t<X>> : hb_true_type {}; | |
| template <typename T> | |
| static inline constexpr hb_result_ok_t<T&&> Ok (T&& v) | |
| { | |
| return hb_result_ok_t<T&&>{std::forward<T> (v)}; | |
| } | |
| static inline constexpr hb_result_ok_t<void> Ok () | |
| { | |
| return hb_result_ok_t<void>{}; | |
| } | |
| template <typename E> | |
| static inline constexpr hb_result_err_t<E&&> Err (E&& e) | |
| { | |
| return hb_result_err_t<E&&>{std::forward<E> (e)}; | |
| } | |
| template <typename T, typename E> | |
| struct HB_NODISCARD_STRUCT hb_result_t | |
| { | |
| private: | |
| union | |
| { | |
| T val_; | |
| E err_; | |
| }; | |
| bool is_ok_; | |
| void destroy () | |
| { | |
| if (is_ok_) | |
| val_.~T (); | |
| else | |
| err_.~E (); | |
| } | |
| void init(const hb_result_t& o) { | |
| is_ok_ = o.is_ok_; | |
| if (is_ok_) | |
| new (std::addressof (val_)) T (o.val_); | |
| else | |
| new (std::addressof (err_)) E (o.err_); | |
| } | |
| void init(hb_result_t&& o) { | |
| is_ok_ = o.is_ok_; | |
| if (is_ok_) | |
| new (std::addressof (val_)) T (std::move (o.val_)); | |
| else | |
| new (std::addressof (err_)) E (std::move (o.err_)); | |
| } | |
| public: | |
| ~hb_result_t () | |
| { | |
| destroy (); | |
| } | |
| hb_result_t () = delete; | |
| hb_result_t (const hb_result_t& o) : is_ok_ (false) | |
| { | |
| init (o); | |
| } | |
| hb_result_t (hb_result_t&& o) noexcept (std::is_nothrow_move_constructible<T>::value && | |
| std::is_nothrow_move_constructible<E>::value) | |
| : is_ok_ (false) | |
| { | |
| init (std::move (o)); | |
| } | |
| hb_result_t& operator = (const hb_result_t& o) | |
| { | |
| if (this == &o) return *this; | |
| if (is_ok_ && o.is_ok_) | |
| { | |
| val_ = o.val_; | |
| } | |
| else if (!is_ok_ && !o.is_ok_) | |
| { | |
| err_ = o.err_; | |
| } | |
| else | |
| { | |
| destroy (); | |
| init (o); | |
| } | |
| return *this; | |
| } | |
| hb_result_t& operator = (hb_result_t&& o) noexcept (std::is_nothrow_move_assignable<T>::value && | |
| std::is_nothrow_move_assignable<E>::value) | |
| { | |
| if (this == &o) return *this; | |
| if (is_ok_ && o.is_ok_) | |
| { | |
| val_ = std::move (o.val_); | |
| } | |
| else if (!is_ok_ && !o.is_ok_) | |
| { | |
| err_ = std::move (o.err_); | |
| } | |
| else | |
| { | |
| destroy (); | |
| init (std::move (o)); | |
| } | |
| return *this; | |
| } | |
| // Construct from Ok(...) tag | |
| template <typename U, | |
| hb_enable_if ((std::is_constructible<T, U>::value))> | |
| hb_result_t (hb_result_ok_t<U>&& o) : is_ok_ (true) | |
| { | |
| new (std::addressof (val_)) T (std::move (o.value)); | |
| } | |
| template <typename U, | |
| hb_enable_if ((std::is_constructible<T, const U&>::value))> | |
| hb_result_t (const hb_result_ok_t<U>& o) : is_ok_ (true) | |
| { | |
| new (std::addressof (val_)) T (o.value); | |
| } | |
| // Construct from Err(...) tag | |
| template <typename F, | |
| hb_enable_if ((std::is_constructible<E, F>::value))> | |
| hb_result_t (hb_result_err_t<F>&& e) : is_ok_ (false) | |
| { | |
| new (std::addressof (err_)) E (std::move (e.error)); | |
| } | |
| template <typename F, | |
| hb_enable_if ((std::is_constructible<E, const F&>::value))> | |
| hb_result_t (const hb_result_err_t<F>& e) : is_ok_ (false) | |
| { | |
| new (std::addressof (err_)) E (e.error); | |
| } | |
| // Construct directly from error E (when neither T or E are convertible into each other) | |
| template <typename F = E, | |
| hb_enable_if ((hb_is_same (F, E) && | |
| !(hb_is_convertible (T, E) || hb_is_convertible (E, T))))> | |
| hb_result_t (F e) : is_ok_ (false) | |
| { | |
| new (std::addressof (err_)) E (std::move (e)); | |
| } | |
| // Construct directly from value T (when neither T or E are convertible into each other) | |
| template <typename U = T, | |
| hb_enable_if ((std::is_constructible<T, U>::value && | |
| !hb_is_same (hb_decay<U>, E) && | |
| !hb_is_same (hb_decay<U>, hb_result_t) && | |
| !_hb_is_result_tag<hb_decay<U>>::value && | |
| !(hb_is_convertible (T, E) || hb_is_convertible (E, T))))> | |
| hb_result_t (U&& v) : is_ok_ (true) | |
| { | |
| new (std::addressof (val_)) T (std::forward<U> (v)); | |
| } | |
| // Move fallible into a result if it's in_error() method returns false. | |
| static hb_result_t from (T&& fallible, E error_value) { | |
| if (fallible.in_error()) { | |
| return Err(error_value); | |
| } | |
| return Ok(fallible); | |
| } | |
| bool is_ok () const { return is_ok_; } | |
| bool is_err () const { return !is_ok_; } | |
| explicit operator bool () const { return is_ok (); } | |
| T& value () & { assert (is_ok_); return val_; } | |
| const T& value () const & { assert (is_ok_); return val_; } | |
| T value () && { assert (is_ok_); return std::move (val_); } | |
| T& operator * () & { return value (); } | |
| const T& operator * () const & { return value (); } | |
| T operator * () && { return std::move (*this).value (); } | |
| T* operator -> () { return std::addressof (value ()); } | |
| const T* operator -> () const { return std::addressof (value ()); } | |
| template <typename U> | |
| T value_or (U&& default_value) const & | |
| { | |
| if (is_ok_) return val_; | |
| return std::forward<U> (default_value); | |
| } | |
| template <typename U> | |
| T value_or (U&& default_value) && | |
| { | |
| if (is_ok_) return std::move (val_); | |
| return std::forward<U> (default_value); | |
| } | |
| T value_or_default () const & | |
| { | |
| if (is_ok_) return val_; | |
| return T (); | |
| } | |
| T value_or_default () && | |
| { | |
| if (is_ok_) return std::move (val_); | |
| return T (); | |
| } | |
| const E& error () const & { assert (!is_ok_); return err_; } | |
| E& error () & { assert (!is_ok_); return err_; } | |
| E error () && { assert (!is_ok_); return std::move (err_); } | |
| bool operator == (const hb_result_t& o) const | |
| { | |
| if (is_ok_ != o.is_ok_) return false; | |
| if (is_ok_) return val_ == o.val_; | |
| return err_ == o.err_; | |
| } | |
| bool operator != (const hb_result_t& o) const | |
| { | |
| return !(*this == o); | |
| } | |
| template <typename U> | |
| bool operator == (const hb_result_ok_t<U>& o) const | |
| { | |
| return is_ok_ && val_ == o.value; | |
| } | |
| template <typename U> | |
| bool operator != (const hb_result_ok_t<U>& o) const | |
| { | |
| return !(*this == o); | |
| } | |
| template <typename F> | |
| bool operator == (const hb_result_err_t<F>& e) const | |
| { | |
| return !is_ok_ && err_ == e.error; | |
| } | |
| template <typename F> | |
| bool operator != (const hb_result_err_t<F>& e) const | |
| { | |
| return !(*this == e); | |
| } | |
| template <typename U> | |
| bool operator == (const U&) const = delete; | |
| template <typename U> | |
| bool operator != (const U&) const = delete; | |
| }; | |
| // Partial specialization for void | |
| template <typename E> | |
| struct HB_NODISCARD_STRUCT hb_result_t<void, E> | |
| { | |
| private: | |
| union | |
| { | |
| E err_; | |
| }; | |
| bool is_ok_; | |
| template <typename, typename> friend struct hb_result_t; | |
| public: | |
| ~hb_result_t () | |
| { | |
| if (!is_ok_) | |
| err_.~E (); | |
| } | |
| hb_result_t () : is_ok_ (true) {} | |
| hb_result_t (hb_result_ok_t<void>) : is_ok_ (true) {} | |
| hb_result_t (const hb_result_t& o) : is_ok_ (o.is_ok_) | |
| { | |
| if (!is_ok_) | |
| new (std::addressof (err_)) E (o.err_); | |
| } | |
| hb_result_t (hb_result_t&& o) noexcept (std::is_nothrow_move_constructible<E>::value) | |
| : is_ok_ (o.is_ok_) | |
| { | |
| if (!is_ok_) | |
| new (std::addressof (err_)) E (std::move (o.err_)); | |
| } | |
| hb_result_t& operator = (const hb_result_t& o) | |
| { | |
| if (this == &o) return *this; | |
| if (!is_ok_ && !o.is_ok_) | |
| { | |
| err_ = o.err_; | |
| } | |
| else if (!is_ok_ && o.is_ok_) | |
| { | |
| err_.~E (); | |
| is_ok_ = true; | |
| } | |
| else if (is_ok_ && !o.is_ok_) | |
| { | |
| new (std::addressof (err_)) E (o.err_); | |
| is_ok_ = false; | |
| } | |
| return *this; | |
| } | |
| hb_result_t& operator = (hb_result_t&& o) noexcept (std::is_nothrow_move_assignable<E>::value) | |
| { | |
| if (this == &o) return *this; | |
| if (!is_ok_ && !o.is_ok_) | |
| { | |
| err_ = std::move (o.err_); | |
| } | |
| else if (!is_ok_ && o.is_ok_) | |
| { | |
| err_.~E (); | |
| is_ok_ = true; | |
| } | |
| else if (is_ok_ && !o.is_ok_) | |
| { | |
| new (std::addressof (err_)) E (std::move (o.err_)); | |
| is_ok_ = false; | |
| } | |
| return *this; | |
| } | |
| template <typename F, | |
| hb_enable_if ((std::is_constructible<E, F>::value))> | |
| hb_result_t (hb_result_err_t<F>&& e) : is_ok_ (false) | |
| { | |
| new (std::addressof (err_)) E (std::move (e.error)); | |
| } | |
| template <typename F, | |
| hb_enable_if ((std::is_constructible<E, const F&>::value))> | |
| hb_result_t (const hb_result_err_t<F>& e) : is_ok_ (false) | |
| { | |
| new (std::addressof (err_)) E (e.error); | |
| } | |
| // Construct directly from error E | |
| hb_result_t (E e) : is_ok_ (false) | |
| { | |
| new (std::addressof (err_)) E (std::move(e)); | |
| } | |
| // V is any class which has an in_error() method. This checks the result | |
| // of in_error() and returns either Ok() or Err(error_value). | |
| template<typename V> | |
| static hb_result_t<void, E> from (const V& fallible, E error_value) { | |
| if (fallible.in_error()) { | |
| return Err(error_value); | |
| } | |
| return Ok(); | |
| } | |
| bool is_ok () const { return is_ok_; } | |
| bool is_err () const { return !is_ok_; } | |
| explicit operator bool () const { return is_ok (); } | |
| const E& error () const & { assert (!is_ok_); return err_; } | |
| E& error () & { assert (!is_ok_); return err_; } | |
| E error () && { assert (!is_ok_); return std::move (err_); } | |
| void operator * () & { return; } | |
| void operator * () const & { return; } | |
| void operator * () && { return; } | |
| bool operator == (const hb_result_t<void, E>& o) const | |
| { | |
| return is_ok_ == o.is_ok_ && (is_ok_ || err_ == o.err_); | |
| } | |
| bool operator != (const hb_result_t<void, E>& o) const | |
| { | |
| return !(*this == o); | |
| } | |
| bool operator == (hb_result_ok_t<void>) const | |
| { | |
| return is_ok_; | |
| } | |
| bool operator != (hb_result_ok_t<void>) const | |
| { | |
| return !is_ok_; | |
| } | |
| template <typename F> | |
| bool operator == (const hb_result_err_t<F>& e) const | |
| { | |
| return !is_ok_ && err_ == e.error; | |
| } | |
| template <typename F> | |
| bool operator != (const hb_result_err_t<F>& e) const | |
| { | |
| return !(*this == e); | |
| } | |
| template <typename U> | |
| bool operator == (const U&) const = delete; | |
| template <typename U> | |
| bool operator != (const U&) const = delete; | |
| }; | |
| template <typename U, typename T, typename E> | |
| static inline bool operator == (const hb_result_ok_t<U>& o, const hb_result_t<T, E>& r) | |
| { | |
| return r == o; | |
| } | |
| template <typename U, typename T, typename E> | |
| static inline bool operator != (const hb_result_ok_t<U>& o, const hb_result_t<T, E>& r) | |
| { | |
| return r != o; | |
| } | |
| template <typename F, typename T, typename E> | |
| static inline bool operator == (const hb_result_err_t<F>& e, const hb_result_t<T, E>& r) | |
| { | |
| return r == e; | |
| } | |
| template <typename F, typename T, typename E> | |
| static inline bool operator != (const hb_result_err_t<F>& e, const hb_result_t<T, E>& r) | |
| { | |
| return r != e; | |
| } | |
| #ifndef TRY | |
| #define TRY(expr) \ | |
| do { \ | |
| auto _res = (expr); \ | |
| if (unlikely (!_res.is_ok())) return Err(std::move(_res).error()); \ | |
| } while (0) | |
| #endif | |
| #ifndef TRY_ASSIGN | |
| #define TRY_ASSIGN(var, ...) \ | |
| auto HB_PASTE(_res_, __LINE__) = (__VA_ARGS__); \ | |
| if (unlikely (!HB_PASTE(_res_, __LINE__).is_ok())) return Err(std::move(HB_PASTE(_res_, __LINE__)).error()); \ | |
| var = *std::move(HB_PASTE(_res_, __LINE__)) | |
| #endif | |
| #endif /* HB_RESULT_HH */ |