/** * \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. constexpr static void init_code(storage_t &code) noexcept { code = {0, -1/*make a default error code*/}; } constexpr static void init_code(storage_t &code, T value, error_code const &ec) noexcept { code = {value, ec}; } constexpr static void init_code(storage_t &code, T value) noexcept { code = {value, {}}; } constexpr static void init_code(storage_t &code, error_code const &ec) noexcept { code = {{}, ec}; } /// \brief Custom type data acquisition. constexpr static T get_value(storage_t const &code) noexcept { return std::get<0>(code); } constexpr static bool get_ok(storage_t const &code) noexcept { return !std::get<1>(code); } constexpr static error_code get_error(storage_t const &code) noexcept { return std::get<1>(code); } }; template struct default_traits::value>> : generic_traits { /// \brief Custom initialization. constexpr static void init_code(storage_t &code, T value, bool ok) noexcept { code = {value, static_cast(ok ? 0 : ((value == default_value()) ? -1 : value))}; } using generic_traits::init_code; /// \brief Custom default value. constexpr static T default_value() noexcept { return 0; } /// \brief Custom formatted output. static std::string format(result const &r) noexcept { return fmt(*r); } }; template struct default_traits::value>> : generic_traits { /// \brief Custom initialization. constexpr static void init_code(storage_t &code, std::nullptr_t, error_code const &ec) noexcept { code = {nullptr, ec}; } constexpr static void init_code(storage_t &code, std::nullptr_t) noexcept { code = {nullptr, -1}; } using generic_traits::init_code; /// \brief Custom default value. constexpr static T default_value() noexcept { return nullptr; } /// \brief Custom formatted output. static std::string format(result const &r) noexcept { if LIBIMP_LIKELY(r) { return fmt(static_cast(*r)); } return fmt(static_cast(*r), ", error = ", r.error()); } }; } // 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_); } 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); } }; /// \brief Custom defined fmt_to method for imp::fmt namespace detail { 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)); } } // namespace detail LIBIMP_NAMESPACE_END_