diff --git a/.github/workflows/exhaustive_float.yml b/.github/workflows/exhaustive_float.yml index 1b7e2c1b89..4343147ceb 100644 --- a/.github/workflows/exhaustive_float.yml +++ b/.github/workflows/exhaustive_float.yml @@ -34,6 +34,11 @@ jobs: steps: - uses: actions/checkout@v4 + - name: Install dependencies + run: | + sudo apt-get update + sudo apt-get install -y libc++-dev libc++abi-dev + - name: Configure CMake run: | cmake -B ${{github.workspace}}/build \ @@ -41,6 +46,7 @@ jobs: -DCMAKE_CXX_STANDARD=${{matrix.std}} \ -DCMAKE_C_COMPILER=${{env.CC}} \ -DCMAKE_CXX_COMPILER=${{env.CXX}} \ + -DCMAKE_CXX_FLAGS="-stdlib=libc++" \ -DBUILD_TESTING=Off - name: Build diff --git a/.github/workflows/exhaustive_int.yml b/.github/workflows/exhaustive_int.yml index 64d11731ab..048a2c2290 100644 --- a/.github/workflows/exhaustive_int.yml +++ b/.github/workflows/exhaustive_int.yml @@ -34,6 +34,11 @@ jobs: steps: - uses: actions/checkout@v4 + - name: Install dependencies + run: | + sudo apt-get update + sudo apt-get install -y libc++-dev libc++abi-dev + - name: Configure CMake run: | cmake -B ${{github.workspace}}/build \ @@ -41,6 +46,7 @@ jobs: -DCMAKE_CXX_STANDARD=${{matrix.std}} \ -DCMAKE_C_COMPILER=${{env.CC}} \ -DCMAKE_CXX_COMPILER=${{env.CXX}} \ + -DCMAKE_CXX_FLAGS="-stdlib=libc++" \ -DBUILD_TESTING=Off - name: Build diff --git a/.github/workflows/quickfuzz.yml b/.github/workflows/quickfuzz.yml index e0cd11d731..6faa902e96 100644 --- a/.github/workflows/quickfuzz.yml +++ b/.github/workflows/quickfuzz.yml @@ -36,8 +36,8 @@ jobs: - name: Install dependencies run: | - sudo echo apt-get update - sudo echo apt-get install -y libc++-dev libc++abi-dev + sudo apt-get update + sudo apt-get install -y libc++-dev libc++abi-dev - name: Configure CMake run: | @@ -46,6 +46,7 @@ jobs: -DCMAKE_CXX_STANDARD=${{matrix.std}} \ -DCMAKE_C_COMPILER=${{env.CC}} \ -DCMAKE_CXX_COMPILER=${{env.CXX}} \ + -DCMAKE_CXX_FLAGS="-stdlib=libc++" \ -DBUILD_TESTING=Off - name: Build diff --git a/include/glaze/json/json_t.hpp b/include/glaze/json/json_t.hpp index ccd19762bb..eb5a37161a 100644 --- a/include/glaze/json/json_t.hpp +++ b/include/glaze/json/json_t.hpp @@ -3,6 +3,28 @@ #pragma once +#ifndef GLZ_THROW_OR_ABORT +#if __cpp_exceptions +#define GLZ_THROW_OR_ABORT(EXC) (throw(EXC)) +#define GLZ_NOEXCEPT noexcept(false) +#else +#define GLZ_THROW_OR_ABORT(EXC) (std::abort()) +#define GLZ_NOEXCEPT noexcept(true) +#endif +#endif + +#if __cpp_exceptions +#include +#endif + +namespace glz +{ + inline void glaze_error([[maybe_unused]] const char* msg) GLZ_NOEXCEPT + { + GLZ_THROW_OR_ABORT(std::runtime_error(msg)); + } +} + #include #include #include diff --git a/include/glaze/util/expected.hpp b/include/glaze/util/expected.hpp index b262047b37..a00c6d7797 100644 --- a/include/glaze/util/expected.hpp +++ b/include/glaze/util/expected.hpp @@ -1,63 +1,13 @@ -/* - * MIT License - * - * Copyright (c) 2022 Rishabh Dwivedi - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ +// Glaze Library +// For the license information refer to glaze.hpp #pragma once +#include +#include #include #include -#ifndef GLZ_THROW_OR_ABORT -#if __cpp_exceptions -#define GLZ_THROW_OR_ABORT(EXC) (throw(EXC)) -#define GLZ_NOEXCEPT noexcept(false) -#else -#define GLZ_THROW_OR_ABORT(EXC) (std::abort()) -#define GLZ_NOEXCEPT noexcept(true) -#endif -#endif - -#if __cpp_exceptions -#include -#endif - -namespace glz -{ - inline void glaze_error([[maybe_unused]] const char* msg) GLZ_NOEXCEPT - { - GLZ_THROW_OR_ABORT(std::runtime_error(msg)); - } -} - -#ifdef __has_include -#if __has_include() && __cpp_lib_expected >= 202202L -#include -#elif __has_include() -#include -#endif -#endif - -#if defined(__cpp_lib_expected) namespace glz { template @@ -80,1447 +30,3 @@ namespace glz using unexpected = std::unexpected; #endif } -#else - -#include -#include -#include -#include -#include -#include -#include - -namespace glz -{ - - template - class unexpected - { - public: - using value_type = E; - constexpr unexpected(const unexpected&) = default; - constexpr unexpected(unexpected&&) = default; // NOLINT - constexpr auto operator=(const unexpected&) -> unexpected& = default; - constexpr auto operator=(unexpected&&) -> unexpected& = default; // NOLINT - ~unexpected() = default; - - template - requires std::constructible_from - constexpr explicit unexpected(std::in_place_t /*unused*/, Args&&... args) : val(std::forward(args)...) - {} - - template - requires std::constructible_from&, Args...> - constexpr explicit unexpected(std::in_place_t /*unused*/, std::initializer_list i_list, Args&&... args) - : val(i_list, std::forward(args)...) - {} - - template - requires(!std::same_as, unexpected>) && - (!std::same_as, std::in_place_t>) && std::constructible_from - constexpr explicit unexpected(Err&& err) // NOLINT - : val(std::forward(err)) - {} - - constexpr auto value() const& noexcept -> const E& { return val; } - constexpr auto value() & noexcept -> E& { return val; } - constexpr auto value() const&& noexcept -> const E&& { return std::move(val); } - constexpr auto value() && noexcept -> E&& { return std::move(val); } - - constexpr void swap(unexpected& other) noexcept(std::is_nothrow_swappable_v) - requires(std::is_swappable_v) - { - using std::swap; - swap(val, other.val); - } - - template - requires(requires(const E& x, E2 const& y) { - { - x == y - } -> std::convertible_to; - }) - friend constexpr auto operator==(const unexpected& x, const unexpected& y) -> bool - { - return x.value() == y.value(); - } - - friend constexpr void swap(unexpected& x, unexpected& y) noexcept(noexcept(x.swap(y))) - requires(std::is_swappable_v) - { - x.swap(y); - } - - private: - E val; - }; - - template - unexpected(E) -> unexpected; - - template - class bad_expected_access; - - template <> - class bad_expected_access : public std::exception - { - protected: - bad_expected_access() noexcept = default; - bad_expected_access(const bad_expected_access&) = default; - bad_expected_access(bad_expected_access&&) = default; - auto operator=(const bad_expected_access&) -> bad_expected_access& = default; - auto operator=(bad_expected_access&&) -> bad_expected_access& = default; - ~bad_expected_access() override = default; - - public: - auto what() const noexcept -> const char* override - { // NOLINT - return "bad expected access"; - } - }; - - template - class bad_expected_access : public bad_expected_access - { - public: - explicit bad_expected_access(E e) : val(std::move(e)) {} - auto what() const noexcept -> const char* override - { // NOLINT - return "bad expected access"; - } - - auto error() & noexcept -> E& { return val; } - auto error() const& noexcept -> const E& { return val; } - auto error() && noexcept -> E&& { return std::move(val); } - auto error() const&& noexcept -> const E&& { return std::move(val); } - - private: - E val; - }; - - struct unexpect_t - {}; - inline constexpr unexpect_t unexpect{}; - - namespace detail - { - template - concept non_void_destructible = std::same_as || std::destructible; - } - - template - class expected; - - namespace detail - { - template - concept expected_constructible_from_other = - std::constructible_from && std::constructible_from && - (!std::constructible_from&>)&&(!std::constructible_from >)&&(!std::constructible_from&>)&&( - !std::constructible_from< - T, - const expected< - U, - G> >)&&(!std:: - convertible_to< - expected&, - T>)&&(!std:: - convertible_to< - expected&&, - T>)&&(!std:: - convertible_to< - const expected&, - T>)&&(!std:: - convertible_to< - const expected&&, - T>)&&(!std:: - constructible_from< - unexpected, - expected< - U, - G>&>)&&(!std:: - constructible_from< - unexpected, - expected< - U, - G> >)&&(!std:: - constructible_from< - unexpected< - E>, - const expected< - U, - G>&>)&&(!std:: - constructible_from< - unexpected< - E>, - const expected< - U, - G> >); - - template - concept is_unexpected = std::same_as, unexpected >; - - template - concept is_expected = - std::same_as, expected >; - - // This function makes sure expected doesn't get into valueless_by_exception - // state due to any exception while assignment - template - constexpr void reinit_expected(T& newval, U& oldval, Args&&... args) - { - if constexpr (std::is_nothrow_constructible_v) { - std::destroy_at(std::addressof(oldval)); - std::construct_at(std::addressof(newval), std::forward(args)...); - } - else if constexpr (std::is_nothrow_move_constructible_v) { - T tmp(std::forward(args)...); - std::destroy_at(std::addressof(oldval)); - std::construct_at(std::addressof(newval), std::move(tmp)); - } - else { - U tmp(std::move(oldval)); - std::destroy_at(std::addressof(oldval)); -#if __cpp_exceptions - try { - std::construct_at(std::addressof(newval), std::forward(args)...); - } - catch (...) { - std::construct_at(std::addressof(oldval), std::move(tmp)); - throw; - } -#else - std::abort(); -#endif - } - } - - } // namespace detail - - template - class expected - { - public: - using value_type = T; - using error_type = E; - using unexpected_type = unexpected; - - template - using rebind = expected; - - // constructors - // postcondition: has_value() = true - constexpr expected() - requires std::default_initializable - : val{} {}; - - // postcondition: has_value() = rhs.has_value() - constexpr expected(const expected& rhs) - requires std::copy_constructible && std::copy_constructible && - std::is_trivially_copy_constructible_v && std::is_trivially_copy_constructible_v - = default; - - // postcondition: has_value() = rhs.has_value() - constexpr expected(const expected& rhs) - requires std::copy_constructible && std::copy_constructible - : has_val(rhs.has_val) - { - if (rhs.has_value()) { - std::construct_at(std::addressof(this->val), *rhs); - } - else { - std::construct_at(std::addressof(this->unex), rhs.error()); - } - } - - constexpr expected(expected&&) noexcept( - std::is_nothrow_move_constructible_v&& std::is_nothrow_move_constructible_v) - requires std::move_constructible && std::move_constructible && - std::is_trivially_move_constructible_v && std::is_trivially_move_constructible_v - = default; - - constexpr expected(expected&& rhs) noexcept( - std::is_nothrow_move_constructible_v&& std::is_nothrow_move_constructible_v) - requires std::move_constructible && std::move_constructible - : has_val(rhs.has_value()) - { - if (rhs.has_value()) { - std::construct_at(std::addressof(this->val), std::move(*rhs)); - } - else { - std::construct_at(std::addressof(this->unex), std::move(rhs.error())); - } - } - - template - requires detail::expected_constructible_from_other - constexpr explicit(!std::convertible_to || !std::convertible_to) - expected(const expected& rhs) // NOLINT - : has_val(rhs.has_value()) - { - using UF = const U&; - using GF = const G&; - if (rhs.has_value()) { - std::construct_at(std::addressof(this->val), std::forward(*rhs)); - } - else { - std::construct_at(std::addressof(this->unex), std::forward(rhs.error())); - } - } - - template - requires detail::expected_constructible_from_other - constexpr explicit(!std::convertible_to || !std::convertible_to) - expected(expected&& rhs) // NOLINT - : has_val(rhs.has_value()) - { - using UF = const U&; - using GF = const G&; - if (rhs.has_value()) { - std::construct_at(std::addressof(this->val), std::forward(*rhs)); - } - else { - std::construct_at(std::addressof(this->unex), std::forward(rhs.error())); - } - } - - template - requires(!std::same_as, std::in_place_t>) && - (!std::same_as, std::remove_cvref_t >) && - (!detail::is_unexpected) && std::constructible_from - constexpr explicit(!std::convertible_to) expected(U&& v) // NOLINT - : val(std::forward(v)) - {} - - template - requires std::constructible_from - constexpr explicit(!std::convertible_to) expected(const unexpected& e) // NOLINT - : has_val{false}, unex(std::forward(e.value())) - {} - - template - requires std::constructible_from - constexpr explicit(!std::convertible_to) expected(unexpected&& e) // NOLINT - : has_val{false}, unex(std::forward(e.value())) - {} - - template - requires std::constructible_from - constexpr explicit expected(std::in_place_t /*unused*/, Args&&... args) : val(std::forward(args)...) - {} - - template - requires std::constructible_from&, Args...> - constexpr explicit expected(std::in_place_t /*unused*/, std::initializer_list il, Args&&... args) - : val(il, std::forward(args)...) - {} - - template - requires std::constructible_from - constexpr explicit expected(unexpect_t /*unused*/, Args&&... args) - : has_val{false}, unex(std::forward(args)...) - {} - - template - requires std::constructible_from&, Args...> - constexpr explicit expected(unexpect_t /*unused*/, std::initializer_list il, Args&&... args) - : has_val(false), unex(il, std::forward(args)...) - {} - - // destructor - constexpr ~expected() - { - if constexpr (std::is_trivially_destructible_v and std::is_trivially_destructible_v) { - } - else if constexpr (std::is_trivially_destructible_v) { - if (!has_val) { - std::destroy_at(std::addressof(this->unex)); - } - } - else if constexpr (std::is_trivially_destructible_v) { - if (has_val) { - std::destroy_at(std::addressof(this->val)); - } - } - else { - if (has_val) { - std::destroy_at(std::addressof(this->val)); - } - else { - std::destroy_at(std::addressof(this->unex)); - } - } - } - - // assignment - constexpr auto operator=(const expected& rhs) // NOLINT - -> expected& - requires std::is_copy_assignable_v && std::is_copy_constructible_v && std::is_copy_assignable_v && - std::is_copy_constructible_v && - (std::is_nothrow_move_constructible_v || std::is_nothrow_move_constructible_v) - { - has_val = rhs.has_value(); - if (this->has_value() and rhs.has_value()) { - this->val = *rhs; - } - else if (this->has_value()) { - detail::reinit_expected(this->unex, this->val, rhs.error()); - } - else if (rhs.has_value()) { - detail::reinit_expected(this->val, this->unex, *rhs); - } - else { - this->unex = rhs.error(); - } - return *this; - } - - constexpr auto operator=(expected&& rhs) // - noexcept(std::is_nothrow_move_assignable_v&& std::is_nothrow_move_constructible_v&& - std::is_nothrow_move_assignable_v&& std::is_nothrow_move_constructible_v) -> expected& - requires std::is_move_constructible_v && std::is_move_assignable_v && std::is_move_constructible_v && - std::is_move_assignable_v && - (std::is_nothrow_move_constructible_v || std::is_nothrow_move_constructible_v) - { - has_val = rhs.has_value(); - if (this->has_value() and rhs.has_value()) { - this->val = std::move(*rhs); - } - else if (this->has_value()) { - detail::reinit_expected(this->unex, this->val, std::move(rhs.error())); - } - else if (rhs.has_value()) { - detail::reinit_expected(this->val, this->unex, std::move(*rhs)); - } - else { - this->unex = std::move(rhs.error()); - } - return *this; - } - - template - constexpr auto operator=(U&& rhs) -> expected& requires(!std::same_as >) && - (!detail::is_unexpected >)&&std::constructible_from&& std::is_assignable_v && - (std::is_nothrow_constructible_v || std::is_nothrow_move_constructible_v || - std::is_nothrow_move_constructible_v) - { - if (this->has_value()) { - this->val = std::forward(rhs); - return *this; - } - detail::reinit_expected(this->val, this->unex, std::forward(rhs)); - has_val = true; - return *this; - } - - template - requires std::constructible_from && std::is_assignable_v && - (std::is_nothrow_constructible_v || std::is_nothrow_move_constructible_v || - std::is_nothrow_move_constructible_v) - constexpr auto operator=(const unexpected& e) -> expected& - { - using GF = const G&; - if (has_value()) { - detail::reinit_expected(this->unex, this->val, std::forward(e.value())); - } - else { - this->unex = std::forward(e.value()); - } - has_val = false; - return *this; - } - - template - requires std::constructible_from && std::is_assignable_v && - (std::is_nothrow_constructible_v || std::is_nothrow_move_constructible_v || - std::is_nothrow_move_constructible_v) - constexpr auto operator=(unexpected&& e) -> expected& - { - using GF = G; - if (has_value()) { - detail::reinit_expected(this->unex, this->val, std::forward(e.value())); - } - else { - this->unex = std::forward(e.value()); - } - has_val = false; - return *this; - } - - // modifiers - template - requires std::is_nothrow_constructible_v - constexpr auto emplace(Args&&... args) noexcept -> T& - { - if (has_value()) { - std::destroy_at(std::addressof(this->val)); - } - else { - std::destroy_at(std::addressof(this->unex)); - has_val = true; - } - return *std::construct_at(std::addressof(this->val), std::forward(args)...); - } - - template - requires std::is_nothrow_constructible_v&, Args...> - constexpr auto emplace(std::initializer_list il, Args&&... args) noexcept -> T& - { - if (has_value()) { - std::destroy_at(std::addressof(this->val)); - } - else { - std::destroy_at(std::addressof(this->unex)); - has_val = true; - } - return *std::construct_at(std::addressof(this->val), il, std::forward(args)...); - } - - // swap - constexpr void swap(expected& rhs) noexcept( - std::is_nothrow_constructible_v&& std::is_nothrow_swappable_v&& std::is_nothrow_move_constructible_v&& - std::is_nothrow_swappable_v) - requires std::is_swappable_v && std::is_swappable_v && std::is_move_constructible_v && - std::is_move_constructible_v && - (std::is_nothrow_constructible_v || std::is_nothrow_constructible_v) - { - if (rhs.has_value()) { - if (has_value()) { - using std::swap; - swap(this->val, rhs.val); - } - else { - rhs.swap(*this); - } - } - else { - if (has_value()) { - if constexpr (std::is_nothrow_move_constructible_v) { - E tmp(std::move(rhs.unex)); - std::destroy_at(std::addressof(rhs.unex)); -#if __cpp_exceptions - try { - std::construct_at(std::addressof(rhs.val), std::move(this->val)); - std::destroy_at(std::addressof(this->val)); - std::construct_at(std::addressof(this->unex), std::move(tmp)); - } - catch (...) { - std::construct_at(std::addressof(rhs.unex), std::move(tmp)); - throw; - } -#else - std::abort(); -#endif - } - else { - T tmp(std::move(this->val)); - std::destroy_at(std::addressof(this->val)); -#if __cpp_exceptions - try { - std::construct_at(std::addressof(this->unex), std::move(rhs.unex)); - std::destroy_at(std::addressof(rhs.unex)); - std::construct_at(std::addressof(rhs.val), std::move(tmp)); - } - catch (...) { - std::construct_at(std::addressof(this->val), std::move(tmp)); - throw; - } -#else - std::abort(); -#endif - } - has_val = false; - rhs.has_val = true; - } - else { - using std::swap; - swap(this->unex, rhs.unex); - } - } - } - - // observers - - // precondition: has_value() = true - constexpr auto operator->() const noexcept -> const T* { return std::addressof(this->val); } - - // precondition: has_value() = true - constexpr auto operator->() noexcept -> T* { return std::addressof(this->val); } - - // precondition: has_value() = true - constexpr auto operator*() const& noexcept -> const T& { return this->val; } - - // precondition: has_value() = true - constexpr auto operator*() & noexcept -> T& { return this->val; } - - // precondition: has_value() = true - constexpr auto operator*() const&& noexcept -> const T&& { return std::move(this->val); } - - // precondition: has_value() = true - constexpr auto operator*() && noexcept -> T&& { return std::move(this->val); } - - constexpr explicit operator bool() const noexcept { return has_val; } - - [[nodiscard]] constexpr auto has_value() const noexcept -> bool { return has_val; } - - constexpr auto value() const& -> const T& - { - if (has_value()) { - return this->val; - } - GLZ_THROW_OR_ABORT(bad_expected_access(error())); - } - - constexpr auto value() & -> T& - { - if (has_value()) { - return this->val; - } - GLZ_THROW_OR_ABORT(bad_expected_access(error())); - } - - constexpr auto value() const&& -> const T&& - { - if (has_value()) { - return std::move(this->val); - } - GLZ_THROW_OR_ABORT(bad_expected_access(std::move(error()))); - } - - constexpr auto value() && -> T&& - { - if (has_value()) { - return std::move(this->val); - } - GLZ_THROW_OR_ABORT(bad_expected_access(std::move(error()))); - } - - // precondition: has_value() = false - constexpr auto error() const& -> const E& { return this->unex; } - - // precondition: has_value() = false - constexpr auto error() & -> E& { return this->unex; } - - // precondition: has_value() = false - constexpr auto error() const&& -> const E&& { return std::move(this->unex); } - - // precondition: has_value() = false - constexpr auto error() && -> E&& { return std::move(this->unex); } - - template - requires std::is_copy_constructible_v && std::is_convertible_v - constexpr auto value_or(U&& v) const& -> T - { - return has_value() ? **this : static_cast(std::forward(v)); - } - - template - requires std::is_move_constructible_v && std::is_convertible_v - constexpr auto value_or(U&& v) && -> T - { - return has_value() ? std::move(**this) : static_cast(std::forward(v)); - } - - template > > - requires detail::is_expected && std::is_same_v && - std::is_copy_constructible_v && std::is_copy_constructible_v - constexpr auto and_then(F&& f) & - { - if (has_value()) { - return std::invoke(std::forward(f), **this); - } - return U(unexpect, error()); - } - - template > > - requires detail::is_expected && std::is_same_v && - std::is_copy_constructible_v && std::is_copy_constructible_v - constexpr auto and_then(F&& f) const& - { - if (has_value()) { - return std::invoke(std::forward(f), **this); - } - return U(unexpect, error()); - } - - template > > - requires detail::is_expected && std::is_same_v && - std::is_move_constructible_v && std::is_move_constructible_v - constexpr auto and_then(F&& f) && - { - if (has_value()) { - return std::invoke(std::forward(f), std::move(**this)); - } - return U(unexpect, std::move(error())); - } - - template > > - requires detail::is_expected && std::is_same_v && - std::is_move_constructible_v && std::is_move_constructible_v - constexpr auto and_then(F&& f) const&& - { - if (has_value()) { - return std::invoke(std::forward(f), std::move(**this)); - } - return U(unexpect, std::move(error())); - } - - template > > - requires detail::is_expected && std::is_same_v && - std::is_copy_constructible_v && std::is_copy_constructible_v - constexpr auto or_else(F&& f) & - { - if (has_value()) { - return G(**this); - } - return std::invoke(std::forward(f), error()); - } - - template > > - requires detail::is_expected && std::is_same_v && - std::is_copy_constructible_v && std::is_copy_constructible_v - constexpr auto or_else(F&& f) const& - { - if (has_value()) { - return G(**this); - } - return std::invoke(std::forward(f), error()); - } - - template > > - requires detail::is_expected && std::is_same_v && - std::is_move_constructible_v && std::is_move_constructible_v - constexpr auto or_else(F&& f) && - { - if (has_value()) { - return G(std::move(**this)); - } - return std::invoke(std::forward(f), std::move(error())); - } - - template > > - requires detail::is_expected && std::is_same_v && - std::is_move_constructible_v && std::is_move_constructible_v - constexpr auto or_else(F&& f) const&& - { - if (has_value()) { - return G(std::move(**this)); - } - return std::invoke(std::forward(f), std::move(error())); - } - - template > > - requires std::is_void_v && std::is_copy_constructible_v && std::is_copy_constructible_v - constexpr auto or_else(F&& f) & - { - if (has_value()) { - return expected(*this); - } - std::invoke(std::forward(f), error()); - return expected(*this); - } - - template > > - requires std::is_void_v && std::is_copy_constructible_v && std::is_copy_constructible_v - constexpr auto or_else(F&& f) const& - { - if (has_value()) { - return expected(*this); - } - std::invoke(std::forward(f), error()); - return expected(*this); - } - - template > > - requires std::is_void_v && std::is_move_constructible_v && std::is_move_constructible_v && - std::is_copy_constructible_v - constexpr auto or_else(F&& f) && - { - if (has_value()) { - return expected(std::move(*this)); - } - // TODO: is this copy necessary, as f can be just read argument function - std::invoke(std::forward(f), error()); - return expected(std::move(*this)); - } - - template > > - requires std::is_void_v && std::is_move_constructible_v && std::is_move_constructible_v && - std::is_copy_constructible_v - constexpr auto or_else(F&& f) const&& - { - if (!has_value()) { - return expected(std::move(*this)); - } - // TODO: is this copy necessary, as f can be just read argument function - std::invoke(std::forward(f), error()); - return expected(std::move(*this)); - } - - template > > - requires std::is_copy_constructible_v && std::is_copy_constructible_v - constexpr auto transform(F&& f) & - { - if (has_value()) { - if constexpr (!std::same_as) { - return expected(std::invoke(std::forward(f), **this)); - } - else { - std::invoke(std::forward(f), std::move(**this)); - return expected(); - } - } - return expected(unexpect, error()); - } - - template > > - requires std::is_copy_constructible_v && std::is_copy_constructible_v - constexpr auto transform(F&& f) const& - { - if (has_value()) { - if constexpr (!std::same_as) { - return expected(std::invoke(std::forward(f), **this)); - } - else { - std::invoke(std::forward(f), std::move(**this)); - return expected(); - } - } - return expected(unexpect, error()); - } - - template > > - requires std::is_move_constructible_v && std::is_move_constructible_v - constexpr auto transform(F&& f) && - { - if (has_value()) { - if constexpr (!std::same_as) { - return expected(std::invoke(std::forward(f), std::move(**this))); - } - else { - std::invoke(std::forward(f), std::move(**this)); - return expected(); - } - } - return expected(unexpect, std::move(error())); - } - - template > > - requires std::is_move_constructible_v && std::is_move_constructible_v - constexpr auto transform(F&& f) const&& - { - if (has_value()) { - if constexpr (!std::same_as) { - return expected(std::invoke(std::forward(f), std::move(**this))); - } - else { - std::invoke(std::forward(f), std::move(**this)); - return expected(); - } - } - return expected(unexpect, std::move(error())); - } - - template > > - requires std::is_copy_constructible_v && std::is_copy_constructible_v - constexpr auto transform_error(F&& f) & - { - if (has_value()) { - return expected(**this); - } - return expected(unexpect, std::invoke(std::forward(f), error())); - } - - template > > - requires std::is_copy_constructible_v && std::is_copy_constructible_v - constexpr auto transform_error(F&& f) const& - { - if (has_value()) { - return expected(**this); - } - return expected(unexpect, std::invoke(std::forward(f), error())); - } - - template > > - requires std::is_move_constructible_v && std::is_move_constructible_v - constexpr auto transform_error(F&& f) && - { - if (has_value()) { - return expected(std::move(**this)); - } - return expected(unexpect, std::invoke(std::forward(f), std::move(error()))); - } - - template > > - requires std::is_move_constructible_v && std::is_move_constructible_v - constexpr auto transform_error(F&& f) const&& - { - if (has_value()) { - return expected(std::move(**this)); - } - return expected(unexpect, std::invoke(std::forward(f), std::move(error()))); - } - - // equality operators - template - requires(!std::is_void_v) && requires(const T& t1, T2 const& t2, const E& e1, E2 const& e2) { - { - t1 == t2 - } -> std::convertible_to; - { - e1 == e2 - } -> std::convertible_to; - } - friend constexpr auto operator==(const expected& x, const expected& y) -> bool - { - if (x.has_value() != y.has_value()) { - return false; - } - return x.has_value() ? (*x == *y) : (x.error() == y.error()); - } - - template - requires(!detail::is_expected) - friend constexpr bool operator==(const expected& x, const T2& v) - { - return x.has_value() && bool(*x == v); - } - - template - requires requires(const E& x, const unexpected& e) { - { - x == e.value() - } -> std::convertible_to; - } - friend constexpr auto operator==(const expected& x, const unexpected& e) -> bool - { - return !x.has_value() && bool(x.error() == e.value()); - } - - // specialized algorithms - friend constexpr void swap(expected& x, expected& y) noexcept(noexcept(x.swap(y))) { x.swap(y); } - - private: - bool has_val{true}; - union { - T val; - E unex; - }; - }; - - template - class expected - { - public: - using value_type = void; - using error_type = E; - using unexpected_type = unexpected; - - template - using rebind = expected; - - // constructors - - // postcondition: has_value() = true - constexpr expected() noexcept {} // NOLINT - - constexpr expected(const expected& rhs) - requires std::is_copy_constructible_v && std::is_trivially_copy_constructible_v - = default; - - constexpr expected(const expected& rhs) - requires std::is_copy_constructible_v - : has_val(rhs.has_value()) - { - if (!rhs.has_value()) { - std::construct_at(std::addressof(this->unex), rhs.error()); - } - } - - constexpr expected(expected&&) noexcept(std::is_nothrow_move_constructible_v) - requires std::is_move_constructible_v && std::is_trivially_move_constructible_v - = default; - - constexpr expected(expected&& rhs) noexcept(std::is_nothrow_move_constructible_v) - requires std::is_move_constructible_v - : has_val(rhs.has_value()) - { - if (!rhs.has_value()) { - std::construct_at(std::addressof(this->unex), std::move(rhs.error())); - } - } - - template - requires std::is_void_v && std::is_constructible_v && - (!std::is_constructible_v, expected&>) && - (!std::is_constructible_v, expected >) && - (!std::is_constructible_v, const expected&>) && - (!std::is_constructible_v, const expected&>) - constexpr explicit(!std::is_convertible_v) expected(const expected& rhs) // NOLINT - : has_val(rhs.has_value()) - { - if (!rhs.has_value()) { - std::construct_at(std::addressof(this->unex), std::forward(rhs.error())); - } - } - - template - requires std::is_void_v && std::is_constructible_v && - (!std::is_constructible_v, expected&>) && - (!std::is_constructible_v, expected >) && - (!std::is_constructible_v, const expected&>) && - (!std::is_constructible_v, const expected&>) - constexpr explicit(!std::is_convertible_v) expected(expected&& rhs) // NOLINT - : has_val(rhs.has_value()) - { - if (!rhs.has_value()) { - std::construct_at(std::addressof(this->unex), std::forward(rhs.error())); - } - } - - template - requires std::is_constructible_v - constexpr explicit(!std::is_convertible_v) expected(const unexpected& e) // NOLINT - : has_val(false), unex(std::forward(e.value())) - {} - - template - requires std::is_constructible_v - constexpr explicit(!std::is_convertible_v) expected(unexpected&& e) // NOLINT - : has_val(false), unex(std::forward(e.value())) - {} - - constexpr explicit expected(std::in_place_t /*unused*/) noexcept {} - - template - requires std::is_constructible_v - constexpr explicit expected(unexpect_t /*unused*/, Args&&... args) - : has_val(false), unex(std::forward(args)...) - {} - - template - requires std::is_constructible_v&, Args...> - constexpr explicit expected(unexpect_t /*unused*/, std::initializer_list il, Args... args) - : has_val(false), unex(il, std::forward(args)...) - {} - - // destructor - constexpr ~expected() - { - if constexpr (std::is_trivially_destructible_v) { - } - else { - if (!has_value()) std::destroy_at(std::addressof(this->unex)); - } - } - - // assignment - constexpr auto operator=(const expected& rhs) -> expected& // NOLINT - requires std::is_copy_assignable_v && std::is_copy_constructible_v - { - if (has_value() && rhs.has_value()) { - } - else if (has_value()) { - std::construct_at(std::addressof(this->unex), rhs.unex); - has_val = false; - } - else if (rhs.has_value()) { - std::destroy_at(std::addressof(this->unex)); - has_val = true; - } - else { - this->unex = rhs.error(); - } - return *this; - } - - constexpr auto operator=(expected&& rhs) noexcept( - std::is_nothrow_move_constructible_v&& std::is_nothrow_move_assignable_v) -> expected& - requires std::is_move_constructible_v && std::is_move_assignable_v - { - if (has_value() && rhs.has_value()) { - } - else if (has_value()) { - std::construct_at(std::addressof(this->unex), std::move(rhs.unex)); - has_val = false; - } - else if (rhs.has_value()) { - std::destroy_at(std::addressof(this->unex)); - has_val = true; - } - else { - this->unex = std::move(rhs.error()); - } - return *this; - } - - template - requires std::is_constructible_v and std::is_assignable_v - constexpr auto operator=(const unexpected& e) -> expected& - { - if (has_value()) { - std::construct_at(std::addressof(this->unex), std::forward(e.value())); - has_val = false; - } - else { - this->unex = std::forward(e.value()); - } - return *this; - } - - template - requires std::is_constructible_v && std::is_assignable_v - constexpr auto operator=(unexpected&& e) -> expected& - { - if (has_value()) { - std::construct_at(std::addressof(this->unex), std::forward(e.value())); - has_val = false; - } - else { - this->unex = std::forward(e.value()); - } - return *this; - } - - // modifiers - constexpr void emplace() noexcept - { - if (!has_value()) { - std::destroy_at(std::addressof(this->unex)); - has_val = true; - } - } - - // swap - constexpr void swap(expected& rhs) noexcept( - std::is_nothrow_move_constructible_v&& std::is_nothrow_swappable_v) - requires std::is_swappable_v && std::is_move_constructible_v - { - if (rhs.has_value()) { - if (has_value()) { - } - else { - rhs.swap(*this); - } - } - else { - if (has_value()) { - std::construct_at(std::addressof(this->unex), std::move(rhs.unex)); - std::destroy_at(std::addressof(rhs.unex)); - has_val = false; - rhs.has_val = true; - } - else { - using std::swap; - swap(this->unex, rhs.unex); - } - } - } - - // observers - constexpr explicit operator bool() const noexcept { return has_val; } - - [[nodiscard]] constexpr auto has_value() const noexcept -> bool { return has_val; } - - // precondition: has_value() = true - constexpr void operator*() const noexcept {} - - constexpr void value() const& - { - if (!has_value()) { - GLZ_THROW_OR_ABORT(bad_expected_access(error())); - } - } - - constexpr void value() && - { - if (!has_value()) { - GLZ_THROW_OR_ABORT(bad_expected_access(std::move(error()))); - } - } - - // precondition: has_value() = false - constexpr auto error() const& -> const E& { return this->unex; } - - // precondition: has_value() = false - constexpr auto error() & -> E& { return this->unex; } - - // precondition: has_value() = false - constexpr auto error() const&& -> const E&& { return std::move(this->unex); } - - // precondition: has_value() = false - constexpr auto error() && -> E&& { return std::move(this->unex); } - - // monadic - template > > - requires detail::is_expected && std::is_same_v && std::is_copy_constructible_v - constexpr auto and_then(F&& f) & - { - if (has_value()) { - return std::invoke(std::forward(f)); - } - return U(unexpect, error()); - } - - template > > - requires detail::is_expected && std::is_same_v && std::is_copy_constructible_v - constexpr auto and_then(F&& f) const& - { - if (has_value()) { - return std::invoke(std::forward(f)); - } - return U(unexpect, error()); - } - - template > > - requires detail::is_expected && std::is_same_v && std::is_move_constructible_v - constexpr auto and_then(F&& f) && - { - if (has_value()) { - return std::invoke(std::forward(f)); - } - return U(unexpect, std::move(error())); - } - - template > > - requires detail::is_expected && std::is_same_v && std::is_move_constructible_v - constexpr auto and_then(F&& f) const&& - { - if (has_value()) { - return std::invoke(std::forward(f)); - } - return U(unexpect, std::move(error())); - } - - template > > - requires detail::is_expected && std::is_same_v && - std::is_copy_constructible_v - constexpr auto or_else(F&& f) & - { - if (has_value()) { - return G{}; - } - return std::invoke(std::forward(f), error()); - } - - template > > - requires detail::is_expected && std::is_same_v && - std::is_copy_constructible_v - constexpr auto or_else(F&& f) const& - { - if (has_value()) { - return G{}; - } - return std::invoke(std::forward(f), error()); - } - - template > > - requires detail::is_expected && std::is_same_v && - std::is_move_constructible_v - constexpr auto or_else(F&& f) && - { - if (has_value()) { - return G{}; - } - return std::invoke(std::forward(f), std::move(error())); - } - - template > > - requires detail::is_expected && std::is_same_v && - std::is_move_constructible_v - constexpr auto or_else(F&& f) const&& - { - if (has_value()) { - return G{}; - } - return std::invoke(std::forward(f), std::move(error())); - } - - template > > - requires std::is_void_v && std::is_copy_constructible_v - constexpr auto or_else(F&& f) & - { - if (has_value()) { - return expected(*this); - } - std::invoke(std::forward(f), error()); - return expected(*this); - } - - template > > - requires std::is_void_v && std::is_copy_constructible_v - constexpr auto or_else(F&& f) const& - { - if (has_value()) { - return expected(*this); - } - std::invoke(std::forward(f), error()); - return expected(*this); - } - - template > > - requires std::is_void_v && std::is_move_constructible_v && std::is_copy_constructible_v - constexpr auto or_else(F&& f) && - { - if (has_value()) { - return expected(std::move(*this)); - } - // TODO: is this copy necessary, as f can be just read argument function - std::invoke(std::forward(f), error()); - return expected(std::move(*this)); - } - - template > > - requires std::is_void_v && std::is_move_constructible_v && std::is_copy_constructible_v - constexpr auto or_else(F&& f) const&& - { - if (!has_value()) { - return expected(std::move(*this)); - } - // TODO: is this copy necessary, as f can be just read argument function - std::invoke(std::forward(f), error()); - return expected(std::move(*this)); - } - - template > > - requires std::is_copy_constructible_v - constexpr auto transform(F&& f) & - { - if (has_value()) { - if constexpr (!std::same_as) { - return expected(std::invoke(std::forward(f))); - } - else { - std::invoke(std::forward(f)); - return expected(); - } - } - return expected(unexpect, error()); - } - - template > > - requires std::is_copy_constructible_v - constexpr auto transform(F&& f) const& - { - if (has_value()) { - if constexpr (!std::same_as) { - return expected(std::invoke(std::forward(f))); - } - else { - std::invoke(std::forward(f)); - return expected(); - } - } - return expected(unexpect, error()); - } - - template > > - requires std::is_move_constructible_v - constexpr auto transform(F&& f) && - { - if (has_value()) { - if constexpr (!std::same_as) { - return expected(std::invoke(std::forward(f))); - } - else { - std::invoke(std::forward(f)); - return expected(); - } - } - return expected(unexpect, std::move(error())); - } - - template > > - requires std::is_move_constructible_v - constexpr auto transform(F&& f) const&& - { - if (has_value()) { - if constexpr (!std::same_as) { - return expected(std::invoke(std::forward(f))); - } - else { - std::invoke(std::forward(f)); - return expected(); - } - } - return expected(unexpect, std::move(error())); - } - - template > > - requires std::is_copy_constructible_v - constexpr auto transform_error(F&& f) & - { - if (has_value()) { - return expected{}; - } - return expected(unexpect, std::invoke(std::forward(f), error())); - } - - template > > - requires std::is_copy_constructible_v - constexpr auto transform_error(F&& f) const& - { - if (has_value()) { - return expected{}; - } - return expected(unexpect, std::invoke(std::forward(f), error())); - } - - template > > - requires std::is_move_constructible_v - constexpr auto transform_error(F&& f) && - { - if (has_value()) { - return expected{}; - } - return expected(unexpect, std::invoke(std::forward(f), std::move(error()))); - } - - template > > - requires std::is_move_constructible_v - constexpr auto transform_error(F&& f) const&& - { - if (has_value()) { - return expected{}; - } - return expected(unexpect, std::invoke(std::forward(f), std::move(error()))); - } - - // expected equality operators - template - requires std::is_void_v && requires(E e, E2 e2) { - { - e == e2 - } -> std::convertible_to; - } - friend constexpr auto operator==(const expected& x, const expected& y) -> bool - { - if (x.has_value() != y.has_value()) return false; - return x.has_value() or bool(x.error() == y.error()); - } - - template - requires requires(const expected& x, const unexpected& e) { - { - x.error() == e.value() - } -> std::convertible_to; - } - friend constexpr auto operator==(const expected& x, const unexpected& e) -> bool - { - return !x.has_value() && bool(x.error() == e.value()); - } - - // specialized algorithms - friend constexpr void swap(expected& x, expected& y) noexcept(noexcept(x.swap(y))) { x.swap(y); } - - private: - bool has_val{true}; - union { - E unex; - }; - }; - -} - -#endif