From 453f964bdd74a919017a1fe70ab304e15b94fefb Mon Sep 17 00:00:00 2001 From: mutouyun Date: Wed, 8 Jan 2025 17:14:54 +0800 Subject: [PATCH] Add `result` --- include/libipc/imp/expected.h | 2 +- include/libipc/imp/fmt.h | 7 +- include/libipc/imp/generic.h | 2 +- include/libipc/imp/result.h | 138 ++++++++++++++++++++++++++++++++++ src/libipc/imp/fmt.cpp | 3 +- test/imp/test_imp_log.cpp | 6 +- test/imp/test_imp_result.cpp | 109 +++++++++++++++++++++++++++ 7 files changed, 257 insertions(+), 10 deletions(-) create mode 100644 include/libipc/imp/result.h create mode 100644 test/imp/test_imp_result.cpp diff --git a/include/libipc/imp/expected.h b/include/libipc/imp/expected.h index 1e276a0..a56aed4 100644 --- a/include/libipc/imp/expected.h +++ b/include/libipc/imp/expected.h @@ -24,7 +24,7 @@ namespace ipc { struct unexpected_t { explicit unexpected_t() = default; }; -constexpr unexpected_t unexpected {}; +constexpr unexpected_t unexpected{}; /** * \class template expected diff --git a/include/libipc/imp/fmt.h b/include/libipc/imp/fmt.h index 5ccaf48..c9d7af2 100644 --- a/include/libipc/imp/fmt.h +++ b/include/libipc/imp/fmt.h @@ -98,9 +98,10 @@ LIBIPC_EXPORT bool to_string(fmt_context &ctx, long double a, span f /// \brief Pointer. LIBIPC_EXPORT bool to_string(fmt_context &ctx, std::nullptr_t) noexcept; +LIBIPC_EXPORT bool to_string(fmt_context &ctx, void const volatile *a) noexcept; template ::value>> -LIBIPC_EXPORT bool to_string(fmt_context &ctx, T const volatile *a) noexcept; + typename = std::enable_if_t::value>> +inline bool to_string(fmt_context &ctx, T const volatile *a) noexcept { return to_string(ctx, (void *)a); } /// \brief Date and time. LIBIPC_EXPORT bool to_string(fmt_context &ctx, std::tm const &a, span fstr = {}) noexcept; @@ -114,7 +115,7 @@ namespace detail_fmt { inline bool time_to_string(fmt_context &ctx, std::time_t tt, span fstr) noexcept { #if defined(LIBIPC_CC_MSVC) /// \see https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/localtime-s-localtime32-s-localtime64-s - std::tm tm {}; + std::tm tm{}; if (::localtime_s(&tm, &tt) != 0) { return {}; } diff --git a/include/libipc/imp/generic.h b/include/libipc/imp/generic.h index 50184cb..21f8a6e 100644 --- a/include/libipc/imp/generic.h +++ b/include/libipc/imp/generic.h @@ -37,7 +37,7 @@ using std::in_place; struct in_place_t { explicit in_place_t() = default; }; -constexpr in_place_t in_place {}; +constexpr in_place_t in_place{}; #endif/*!LIBIPC_CPP_17*/ /** diff --git a/include/libipc/imp/result.h b/include/libipc/imp/result.h new file mode 100644 index 0000000..d5685e2 --- /dev/null +++ b/include/libipc/imp/result.h @@ -0,0 +1,138 @@ +/** + * \file libipc/result.h + * \author mutouyun (orz@orzz.org) + * \brief Define the return value type with an error status code. + */ +#pragma once + +#include +#include +#include + +#include "libipc/imp/expected.h" +#include "libipc/imp/error.h" +#include "libipc/imp/generic.h" +#include "libipc/imp/fmt.h" + +namespace ipc { +namespace detail_result { + +template +struct generic_initializer { + using storage_t = expected; + + /// \brief Custom initialization. + static constexpr storage_t init_code() noexcept { + return {unexpected, std::error_code(-1, std::generic_category())}; + } + + static constexpr storage_t init_code(T value) noexcept { + return {in_place, value}; + } + + static constexpr storage_t init_code(std::error_code const &ec) noexcept { + return {unexpected, ec}; + } +}; + +template +struct result_base; + +template +struct result_base::value>> + : generic_initializer { + + using storage_t = typename generic_initializer::storage_t; + using generic_initializer::init_code; + + static constexpr storage_t init_code(std::nullptr_t, std::error_code const &ec) noexcept { + return {unexpected, ec}; + } + + static constexpr storage_t init_code(std::nullptr_t) noexcept { + return {unexpected, std::error_code(-1, std::generic_category())}; + } +}; + +template +struct result_base::value || std::is_enum::value>> + : generic_initializer { + + using storage_t = typename generic_initializer::storage_t; + using generic_initializer::init_code; +}; + +} // namespace detail_result + +/** + * \class class result + * \brief The generic wrapper for the result type. + */ +template +class result : public detail_result::result_base { +private: + using base_t = detail_result::result_base; + using storage_t = typename base_t::storage_t; + + storage_t ret_; ///< internal data + +public: + template , + typename = decltype(base_t::init_code(std::declval()...))> + result(A &&...args) noexcept + : ret_(base_t::init_code(std::forward(args)...)) {} + + std::string format_string() const { + if LIBIPC_LIKELY(ret_) { + return fmt("value = ", ret_.value()); + } else { + return fmt("error = ", ret_.error()); + } + } + + T value() const noexcept { return ret_ ? ret_.value() : T{}; } + bool ok () const noexcept { return ret_.has_value(); } + std::error_code error() const noexcept { return ret_.error(); } + + 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.ret_ == rhs.ret_; } + friend bool operator!=(result const &lhs, result const &rhs) noexcept { return !(lhs == rhs); } +}; + +template <> +class result { +private: + std::error_code ret_; ///< internal data + +public: + result() noexcept + : ret_(-1, std::generic_category()) {} + + result(std::error_code const &ec) noexcept + : ret_(ec) {} + + std::string format_string() const { + return fmt("error = ", error()); + } + + bool ok () const noexcept { return !ret_; } + std::error_code error() const noexcept { return ret_; } + explicit operator bool () const noexcept { return ok(); } + + friend bool operator==(result const &lhs, result const &rhs) noexcept { return lhs.ret_ == rhs.ret_; } + friend bool operator!=(result const &lhs, result const &rhs) noexcept { return !(lhs == rhs); } +}; + +/// \brief Custom defined fmt_to method for imp::fmt +namespace detail_tag_invoke { + +template +inline bool tag_invoke(decltype(ipc::fmt_to), fmt_context &ctx, result const &r) { + return fmt_to(ctx, (r ? "succ" : "fail"), ", ", r.format_string()); +} + +} // namespace detail_tag_invoke +} // namespace ipc diff --git a/src/libipc/imp/fmt.cpp b/src/libipc/imp/fmt.cpp index deb344c..0c92f37 100644 --- a/src/libipc/imp/fmt.cpp +++ b/src/libipc/imp/fmt.cpp @@ -302,8 +302,7 @@ bool to_string(fmt_context &ctx, std::nullptr_t) noexcept { return ctx.append("null"); } -template <> -bool to_string(fmt_context &ctx, void const volatile *a) noexcept { +bool to_string(fmt_context &ctx, void const volatile *a) noexcept { if (a == nullptr) { return to_string(ctx, nullptr); } diff --git a/test/imp/test_imp_log.cpp b/test/imp/test_imp_log.cpp index 7ebea4e..307eb10 100644 --- a/test/imp/test_imp_log.cpp +++ b/test/imp/test_imp_log.cpp @@ -28,10 +28,10 @@ TEST(log, custom) { std::string e; } ll_data; auto ll = [&ll_data](auto &&ctx) { - auto s = imp::fmt(ctx.params); - if (ctx.level == imp::log::level::error) ll_data.e += s + " "; + auto s = ipc::fmt(ctx.params); + if (ctx.level == ipc::log::level::error) ll_data.e += s + " "; else - if (ctx.level == imp::log::level::info ) ll_data.i += s + " "; + if (ctx.level == ipc::log::level::info ) ll_data.i += s + " "; }; LIBIPC_LOG(ll); diff --git a/test/imp/test_imp_result.cpp b/test/imp/test_imp_result.cpp new file mode 100644 index 0000000..159ea59 --- /dev/null +++ b/test/imp/test_imp_result.cpp @@ -0,0 +1,109 @@ + +#include +#include + +#include "test.h" + +#include "libipc/imp/result.h" +#include "libipc/imp/fmt.h" + +TEST(result, ok) { + ipc::result ret; + EXPECT_FALSE(ret); + EXPECT_FALSE(ret.ok()); + EXPECT_EQ(ret.value(), 0); + + ret = {0}; + EXPECT_TRUE(ret); + EXPECT_TRUE(ret.ok()); + EXPECT_EQ(ret.value(), 0); + + ret = ipc::result(1234); + EXPECT_TRUE(ret); + EXPECT_TRUE(ret.ok()); + EXPECT_EQ(ret.value(), 1234); + + ret = std::error_code{9999, std::generic_category()}; + EXPECT_FALSE(ret); + EXPECT_FALSE(ret.ok()); + EXPECT_EQ(ret.value(), 0); + + ret = 4321; + EXPECT_TRUE(ret); + EXPECT_TRUE(ret.ok()); + EXPECT_EQ(ret.value(), 4321); + + ipc::result r1; + EXPECT_FALSE(r1); + r1 = std::error_code{}; + EXPECT_TRUE(r1); + r1 = {}; + EXPECT_FALSE(r1); + r1 = std::error_code{9999, std::generic_category()}; + EXPECT_FALSE(r1); + EXPECT_EQ(r1.error().value(), 9999); + + ipc::result r2 {nullptr, std::error_code{4321, std::generic_category()}}; + EXPECT_NE(r2, nullptr); // ipc::result{nullptr} + EXPECT_EQ(*r2, nullptr); + EXPECT_FALSE(r2); +} + +TEST(result, compare) { + ipc::result r1, r2; + EXPECT_EQ(r1, r2); + + ipc::result r3(0); + EXPECT_NE(r1, r3); + + ipc::result r4(222222); + EXPECT_NE(r3, r4); + + ipc::result r5(std::error_code{9999, std::generic_category()}); + EXPECT_NE(r4, r5); + EXPECT_NE(r3, r5); + + r3 = r5; + EXPECT_EQ(r3, r5); +} + +TEST(result, fmt) { + { + ipc::result r1; + EXPECT_EQ(ipc::fmt(r1), ipc::fmt("fail, error = ", std::error_code(-1, std::generic_category()))); + ipc::result r2(65537); + EXPECT_EQ(ipc::fmt(r2), "succ, value = 65537"); + ipc::result r3(0); + EXPECT_EQ(ipc::fmt(r3), "succ, value = 0"); + } + { + ipc::result r0; + EXPECT_EQ(ipc::fmt(r0), ipc::fmt(ipc::result())); + ipc::result r1 {std::error_code(-1, std::generic_category())}; + EXPECT_EQ(ipc::fmt(r1), ipc::fmt("fail, error = ", std::error_code(-1, std::generic_category()))); + + ipc::result r2 {&r1}; + EXPECT_EQ(ipc::fmt(r2), ipc::fmt("succ, value = ", (void *)&r1)); + + int aaa {}; + ipc::result r3 {&aaa}; + EXPECT_EQ(ipc::fmt(r3), ipc::fmt("succ, value = ", (void *)&aaa)); + ipc::result r4 {nullptr}; + EXPECT_EQ(ipc::fmt(r4), ipc::fmt("fail, error = ", std::error_code(-1, std::generic_category()))); + r4 = std::error_code(1234, std::generic_category()); + EXPECT_EQ(ipc::fmt(r4), ipc::fmt("fail, error = ", std::error_code(1234, std::generic_category()))); + ipc::result r5; + EXPECT_EQ(ipc::fmt(r5), ipc::fmt("fail, error = ", std::error_code(-1, std::generic_category()))); + } + { + ipc::result r1 {-123}; + EXPECT_EQ(ipc::fmt(r1), ipc::fmt("succ, value = ", -123)); + } + { + ipc::result r1; + EXPECT_EQ(ipc::fmt(r1), ipc::fmt("fail, error = ", std::error_code(-1, std::generic_category()))); + r1 = std::error_code{}; + EXPECT_TRUE(r1); + EXPECT_EQ(ipc::fmt(r1), ipc::fmt("succ, error = ", std::error_code())); + } +}