Add result

This commit is contained in:
mutouyun 2025-01-08 17:14:54 +08:00
parent 6c068f7ba4
commit 6db5845a45
7 changed files with 257 additions and 10 deletions

View File

@ -98,9 +98,10 @@ LIBIPC_EXPORT bool to_string(fmt_context &ctx, long double a, span<char const> 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 <typename T,
typename = std::enable_if_t<std::is_same<T, void>::value>>
LIBIPC_EXPORT bool to_string(fmt_context &ctx, T const volatile *a) noexcept;
typename = std::enable_if_t<!std::is_same<T, char>::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<char const> fstr = {}) noexcept;

138
include/libipc/imp/result.h Normal file
View File

@ -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 <type_traits>
#include <utility>
#include <cstdint>
#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 <typename T>
struct generic_initializer {
using storage_t = expected<T, std::error_code>;
/// \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 <typename T, typename = void>
struct result_base;
template <typename T>
struct result_base<T, std::enable_if_t<std::is_pointer<T>::value>>
: generic_initializer<T> {
using storage_t = typename generic_initializer<T>::storage_t;
using generic_initializer<T>::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 <typename T>
struct result_base<T, std::enable_if_t<std::is_integral<T>::value || std::is_enum<T>::value>>
: generic_initializer<T> {
using storage_t = typename generic_initializer<T>::storage_t;
using generic_initializer<T>::init_code;
};
} // namespace detail_result
/**
* \class class result
* \brief The generic wrapper for the result type.
*/
template <typename T>
class result : public detail_result::result_base<T> {
private:
using base_t = detail_result::result_base<T>;
using storage_t = typename base_t::storage_t;
storage_t ret_; ///< internal data
public:
template <typename... A,
typename = not_match<result, A...>,
typename = decltype(base_t::init_code(std::declval<A>()...))>
result(A &&...args) noexcept
: ret_(base_t::init_code(std::forward<A>(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<void> {
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 <typename T>
inline bool tag_invoke(decltype(ipc::fmt_to), fmt_context &ctx, result<T> const &r) {
return fmt_to(ctx, (r ? "succ" : "fail"), ", ", r.format_string());
}
} // namespace detail_tag_invoke
} // namespace ipc

View File

@ -302,8 +302,7 @@ bool to_string(fmt_context &ctx, std::nullptr_t) noexcept {
return ctx.append("null");
}
template <>
bool to_string<void, void>(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);
}

View File

@ -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);

View File

@ -0,0 +1,109 @@
#include <sstream>
#include <cstdint>
#include "test.h"
#include "libipc/imp/result.h"
#include "libipc/imp/fmt.h"
TEST(result, ok) {
ipc::result<std::uint64_t> 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<std::uint64_t>(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<void> 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<int *> r2 {nullptr, std::error_code{4321, std::generic_category()}};
EXPECT_NE(r2, nullptr); // ipc::result<int *>{nullptr}
EXPECT_EQ(*r2, nullptr);
EXPECT_FALSE(r2);
}
TEST(result, compare) {
ipc::result<std::uint64_t> r1, r2;
EXPECT_EQ(r1, r2);
ipc::result<std::uint64_t> r3(0);
EXPECT_NE(r1, r3);
ipc::result<std::uint64_t> r4(222222);
EXPECT_NE(r3, r4);
ipc::result<std::uint64_t> 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<std::uint64_t> r1;
EXPECT_EQ(ipc::fmt(r1), ipc::fmt("fail, error = ", std::error_code(-1, std::generic_category())));
ipc::result<std::uint64_t> r2(65537);
EXPECT_EQ(ipc::fmt(r2), "succ, value = 65537");
ipc::result<std::uint64_t> r3(0);
EXPECT_EQ(ipc::fmt(r3), "succ, value = 0");
}
{
ipc::result<int> r0;
EXPECT_EQ(ipc::fmt(r0), ipc::fmt(ipc::result<std::uint64_t>()));
ipc::result<int> 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<void *> r2 {&r1};
EXPECT_EQ(ipc::fmt(r2), ipc::fmt("succ, value = ", (void *)&r1));
int aaa {};
ipc::result<int *> r3 {&aaa};
EXPECT_EQ(ipc::fmt(r3), ipc::fmt("succ, value = ", (void *)&aaa));
ipc::result<int *> 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<int *> r5;
EXPECT_EQ(ipc::fmt(r5), ipc::fmt("fail, error = ", std::error_code(-1, std::generic_category())));
}
{
ipc::result<std::int64_t> r1 {-123};
EXPECT_EQ(ipc::fmt(r1), ipc::fmt("succ, value = ", -123));
}
{
ipc::result<void> 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()));
}
}