/** * @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 "fmt/format.h" #include "libimp/def.h" #include "libimp/detect_plat.h" #include "libimp/export.h" LIBIMP_NAMESPACE_BEG_ /** * @brief Use the least significant (in Little-Endian) of * a 64-bit unsigned integer to indicate success or failure, * so the data significant bit cannot exceed 63 bits. */ class LIBIMP_EXPORT result_code { std::uint64_t status_; public: result_code() noexcept; result_code(bool ok, std::uint64_t value = {}) noexcept; std::uint64_t value() const noexcept; bool ok() const noexcept; std::uint64_t operator*() const noexcept { return value(); } explicit operator bool() const noexcept { return ok(); } friend bool operator==(result_code const &lhs, result_code const &rhs) noexcept; friend bool operator!=(result_code const &lhs, result_code const &rhs) noexcept; }; namespace detail_result { template struct default_traits; } // namespace detail_result template , typename = void> class result; namespace detail_result { template struct default_traits::value>> { constexpr static T value() noexcept { return 0; } static std::uint64_t cast_to_code(T value) noexcept { return static_cast(value); } static T cast_from_code(std::uint64_t code) noexcept { return static_cast(code); } template static auto format(result const &r, Out &&out) noexcept { return format_to(out, "{}", *r); } }; template struct default_traits::value>> { constexpr static T value() noexcept { return nullptr; } static std::uint64_t cast_to_code(T value) noexcept { return reinterpret_cast(value); } static T cast_from_code(std::uint64_t code) noexcept { return reinterpret_cast(code); } template static auto format(result const &r, Out &&out) noexcept { if LIBIMP_LIKELY(r) { return format_to(out, "{}", static_cast(*r)); } return format_to(out, "{}, code = {}", static_cast(*r), r.code()); } }; } // namespace detail_result template class result::value>> { result_code code_; public: using default_traits_t = DefTraits; result() noexcept = default; result(bool ok, T value = default_traits_t::value()) noexcept : code_(ok, default_traits_t::cast_to_code(value)) {} T value() const noexcept { return default_traits_t::cast_from_code(code_.value()); } bool ok() const noexcept { return code_.ok(); } 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.code_ != rhs.code_; } }; template class result::value>> { result_code code_; public: using default_traits_t = DefTraits; result(T value = default_traits_t::value()) noexcept : code_(default_traits_t::value() != value, default_traits_t::cast_to_code(value)) {} result(std::nullptr_t, std::uint64_t code) noexcept : code_(false, code) {} T value() const noexcept { return code_.ok() ? default_traits_t::cast_from_code(code_.value()) : nullptr; } bool ok() const noexcept { return code_.ok(); } std::uint64_t code() const noexcept { return code_.value(); } 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.code_ != rhs.code_; } }; LIBIMP_NAMESPACE_END_ template struct fmt::formatter<::LIBIMP_::result> { constexpr auto parse(format_parse_context& ctx) { return ctx.end(); } template auto format(::LIBIMP_::result r, FormatContext &ctx) { return format_to(::LIBIMP_::result::default_traits_t::format(r, format_to(ctx.out(), "[{}, value = ", r ? "succ" : "fail")), "]"); } }; template <> struct fmt::formatter<::LIBIMP_::result_code> : formatter<::LIBIMP_::result> { template auto format(::LIBIMP_::result_code r, FormatContext &ctx) { return format_to(ctx.out(), "[{}, value = {}]", (r ? "succ" : "fail"), *r); } };