/** * \file libimp/result.h * \author mutouyun (orz@orzz.org) * \brief Define the return value type with a status code. * \date 2022-04-17 */ #pragma once #include #include #include #include #include "libimp/def.h" #include "libimp/detect_plat.h" #include "libimp/export.h" #include "libimp/fmt.h" #include "libimp/generic.h" #include "libimp/error.h" LIBIMP_NAMESPACE_BEG_ namespace detail_result { template struct default_traits; } // namespace detail_result template > class result; /// \typedef Uses std::uint64_t as the default underlying type of result code. using result_code = result; namespace detail_result { template struct generic_traits { /// \typedef Combine data and valid identifiers with a tuple. using storage_t = std::tuple; /// \brief Custom initialization. static constexpr void init_code(storage_t &code) noexcept { code = {{}, std::error_code(-1, std::generic_category())}; } static constexpr void init_code(storage_t &code, T value, std::error_code const &ec) noexcept { code = {value, ec}; } static constexpr void init_code(storage_t &code, T value) noexcept { code = {value, {}}; } static constexpr void init_code(storage_t &code, std::error_code const &ec) noexcept { code = {{}, ec}; } /// \brief Custom type data acquisition. static constexpr T get_value(storage_t const &code) noexcept { return std::get<0>(code); } static constexpr bool get_ok(storage_t const &code) noexcept { return !std::get<1>(code); } static constexpr std::error_code get_error(storage_t const &code) noexcept { return std::get<1>(code); } }; template struct default_traits { /// \typedef Use the `error_code` as the storage type. using storage_t = std::error_code; /// \brief Custom initialization. static constexpr void init_code(storage_t &code) noexcept { code = std::error_code(-1, std::generic_category()); } static constexpr void init_code(storage_t &code, std::error_code const &ec) noexcept { code = ec; } /// \brief Custom type data acquisition. static constexpr bool get_ok(storage_t const &code) noexcept { return !code; } static constexpr std::error_code get_error(storage_t const &code) noexcept { return code; } /// \brief Custom formatted output. static std::string format(result const &r); }; template struct default_traits::value>> : generic_traits { /// \brief Custom initialization. static constexpr void init_code(typename generic_traits::storage_t &code, T value, bool ok) noexcept { code = {value, ok ? std::error_code() : std::error_code(-1, std::generic_category())}; } using generic_traits::init_code; /// \brief Custom formatted output. static std::string format(result const &r) noexcept; }; template struct default_traits::value>> : generic_traits { /// \brief Custom initialization. static constexpr void init_code(typename generic_traits::storage_t &code, std::nullptr_t, std::error_code const &ec) noexcept { code = {nullptr, ec}; } static constexpr void init_code(typename generic_traits::storage_t &code, std::nullptr_t) noexcept { code = {nullptr, std::error_code(-1, std::generic_category())}; } using generic_traits::init_code; /// \brief Custom formatted output. static std::string format(result const &r) noexcept; }; } // namespace detail_result /** * \class class result * \brief The generic wrapper for the result type. */ template class result : public TypeTraits { public: using type_traits_t = TypeTraits; using storage_t = typename type_traits_t::storage_t; private: storage_t code_; ///< internal data public: template , typename = decltype(type_traits_t::init_code(std::declval() , std::declval()...))> result(A &&...args) noexcept { type_traits_t::init_code(code_, std::forward(args)...); } T value() const noexcept { return type_traits_t::get_value(code_); } bool ok () const noexcept { return type_traits_t::get_ok (code_); } std::error_code error() const noexcept { return type_traits_t::get_error(code_); } T operator * () const noexcept { return value(); } explicit operator bool() const noexcept { return ok (); } friend bool operator==(result const &lhs, result const &rhs) noexcept { return lhs.code_ == rhs.code_; } friend bool operator!=(result const &lhs, result const &rhs) noexcept { return !(lhs == rhs); } }; template class result { public: using type_traits_t = TypeTraits; using storage_t = typename type_traits_t::storage_t; private: storage_t code_; ///< internal data public: template , typename = decltype(type_traits_t::init_code(std::declval() , std::declval()...))> result(A &&...args) noexcept { type_traits_t::init_code(code_, std::forward(args)...); } bool ok () const noexcept { return type_traits_t::get_ok (code_); } std::error_code error() const noexcept { return type_traits_t::get_error(code_); } explicit operator bool() const noexcept { return ok(); } friend bool operator==(result const &lhs, result const &rhs) noexcept { return lhs.code_ == rhs.code_; } friend bool operator!=(result const &lhs, result const &rhs) noexcept { return !(lhs == rhs); } }; /// \brief Custom defined fmt_to method for imp::fmt namespace detail_result { template std::string default_traits::format(result const &r) { return fmt("error = ", r.error()); } template std::string default_traits::value>> ::format(result const &r) noexcept { return fmt(*r); } template std::string default_traits::value>> ::format(result const &r) noexcept { if LIBIMP_LIKELY(r) { return fmt(static_cast(*r)); } return fmt(static_cast(*r), ", error = ", r.error()); } template bool tag_invoke(decltype(::LIBIMP::fmt_to), fmt_context &ctx, result r) { return fmt_to(ctx, (r ? "succ" : "fail"), ", value = ", result::type_traits_t::format(r)); } template bool tag_invoke(decltype(::LIBIMP::fmt_to), fmt_context &ctx, result r) { return fmt_to(ctx, (r ? "succ" : "fail"), ", ", result::type_traits_t::format(r)); } } // namespace detail_result LIBIMP_NAMESPACE_END_