upd: [imp] optimize result interface and fix some bugs

This commit is contained in:
mutouyun 2022-12-25 13:25:29 +08:00
parent 7d8ff13db7
commit 649a5ee02a
10 changed files with 156 additions and 192 deletions

View File

@ -54,6 +54,7 @@ class LIBIMP_EXPORT error_code {
public:
/// \brief constructors
error_code() noexcept;
error_code(error_code_t const &r) noexcept;
error_code(error_code_t const &r, error_category const &ec) noexcept;
/// \brief observers

View File

@ -8,6 +8,7 @@
#include <string>
#include <cstddef>
#include <array>
#include "libimp/def.h"
#include "libimp/generic.h"
@ -18,9 +19,12 @@
LIBIMP_NAMESPACE_BEG_
/**
* \class class LIBIMP_EXPORT fmt_context
* \brief The context of fmt.
*/
class LIBIMP_EXPORT fmt_context {
std::array<char, 2048U> sbuf_; ///< stack buffer
std::string &joined_;
std::size_t offset_;

View File

@ -16,39 +16,9 @@
#include "libimp/export.h"
#include "libimp/fmt.h"
#include "libimp/generic.h"
#include "libimp/error.h"
LIBIMP_NAMESPACE_BEG_
using result_code_t = std::uint64_t;
using result_type = std::tuple<result_code_t, bool>;
/**
* \class class LIBIMP_EXPORT result_code
* \brief Uses std::uint64_t as the default underlying type of error code.
*/
class LIBIMP_EXPORT result_code {
result_type status_;
public:
result_code() noexcept;
result_code(result_code_t value) noexcept;
result_code(result_type const &value) noexcept;
result_code(bool ok, result_code_t value) noexcept;
result_code_t value() const noexcept;
bool ok() const noexcept;
result_code_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 <typename T, typename = void>
@ -60,31 +30,56 @@ template <typename T,
typename TypeTraits = detail_result::default_traits<T>>
class result;
/// \typedef Uses std::uint64_t as the default underlying type of result code.
using result_code = result<std::uint64_t>;
namespace detail_result {
template <typename T>
struct default_traits<T, std::enable_if_t<std::is_integral<T>::value>> {
struct generic_traits {
/// \typedef Combine data and valid identifiers with a tuple.
using storage_t = std::tuple<T, error_code>;
/// \brief Custom initialization.
constexpr static void init_code(result_code &code) noexcept {
code = {};
constexpr static void init_code(storage_t &code) noexcept {
code = {0, -1/*make a default error code*/};
}
constexpr static void init_code(result_code &code, bool ok, T value) noexcept {
code = {ok, static_cast<result_code_t>(value)};
constexpr static void init_code(storage_t &code, T value, error_code const &ec) noexcept {
code = {value, ec};
}
constexpr static void init_code(result_code &code, T value) noexcept {
init_code(code, true, value);
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 <typename T>
struct default_traits<T, std::enable_if_t<std::is_integral<T>::value>> : generic_traits<T> {
/// \brief Custom initialization.
constexpr static void init_code(storage_t &code, T value, bool ok) noexcept {
code = {value, static_cast<error_code_t>(ok ? 0 :
((value == default_value()) ? -1 : value))};
}
using generic_traits<T>::init_code;
/// \brief Custom default value.
constexpr static T default_value() noexcept {
return 0;
}
/// \brief Custom type conversions.
constexpr static T cast_from_code(result_code code) noexcept {
return static_cast<T>(code.value());
}
/// \brief Custom formatted output.
static std::string format(result<T> const &r) noexcept {
return fmt(*r);
@ -92,34 +87,27 @@ struct default_traits<T, std::enable_if_t<std::is_integral<T>::value>> {
};
template <typename T>
struct default_traits<T, std::enable_if_t<std::is_pointer<T>::value>> {
struct default_traits<T, std::enable_if_t<std::is_pointer<T>::value>> : generic_traits<T> {
/// \brief Custom initialization.
constexpr static void init_code(result_code &code, T value = default_value()) noexcept {
code = {default_value() != value, reinterpret_cast<result_code_t>(value)};
constexpr static void init_code(storage_t &code, std::nullptr_t, error_code const &ec) noexcept {
code = {nullptr, ec};
}
constexpr static void init_code(result_code &code, std::nullptr_t) noexcept {
code = {false, {}};
}
constexpr static void init_code(result_code &code, std::nullptr_t, result_code_t r) noexcept {
code = {false, r};
constexpr static void init_code(storage_t &code, std::nullptr_t) noexcept {
code = {nullptr, -1};
}
using generic_traits<T>::init_code;
/// \brief Custom default value.
constexpr static T default_value() noexcept {
return nullptr;
}
/// \brief Custom type conversions.
constexpr static T cast_from_code(result_code code) noexcept {
return code.ok() ? reinterpret_cast<T>(code.value()) : default_value();
}
/// \brief Custom formatted output.
static std::string format(result<T> const &r) noexcept {
if LIBIMP_LIKELY(r) {
return fmt(static_cast<void *>(*r));
}
return fmt(static_cast<void *>(*r), ", code = ", r.code_value());
return fmt(static_cast<void *>(*r), ", error = ", r.error());
}
};
@ -127,51 +115,43 @@ struct default_traits<T, std::enable_if_t<std::is_pointer<T>::value>> {
/**
* \class class result
* \brief The generic wrapper for the result_code.
* \brief The generic wrapper for the result type.
*/
template <typename T, typename TypeTraits>
class result : public TypeTraits {
/// \brief Internal data is stored using result_code.
result_code code_;
public:
using type_traits_t = TypeTraits;
using storage_t = typename type_traits_t::storage_t;
private:
storage_t code_; ///< internal data
public:
template <typename... A,
typename = is_not_match<result, A...>,
typename = decltype(type_traits_t::init_code(std::declval<result_code &>()
typename = decltype(type_traits_t::init_code(std::declval<storage_t &>()
, std::declval<A>()...))>
result(A &&... args) noexcept {
type_traits_t::init_code(code_, std::forward<A>(args)...);
}
bool ok() const noexcept { return code_.ok(); }
T value() const noexcept { return type_traits_t::cast_from_code(code_); }
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_); }
result_code_t code_value() const noexcept { return code_.value(); }
T operator*() const noexcept {
return value();
}
explicit operator bool() const noexcept {
return 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_; }
friend bool operator!=(result const &lhs, result const &rhs) noexcept { return !(lhs == rhs); }
};
/// \brief Custom defined fmt_to method for imp::fmt
namespace detail {
inline bool tag_invoke(decltype(::LIBIMP::fmt_to), fmt_context &ctx, result_code r) {
return fmt_to(ctx, "[", (r ? "succ" : "fail"), ", value = ", *r, "]");
}
template <typename T, typename D>
bool tag_invoke(decltype(::LIBIMP::fmt_to), fmt_context &ctx, result<T, D> r) {
return fmt_to(ctx, "[", (r ? "succ" : "fail"), ", value = ", result<T, D>::type_traits_t::format(r), "]");
return fmt_to(ctx, (r ? "succ" : "fail"), ", value = ", result<T, D>::type_traits_t::format(r));
}
} // namespace detail

View File

@ -17,7 +17,13 @@ public:
return "generic";
}
std::string message(error_code_t const &r) const {
return fmt(r, ((r == 0) ? ", \"success\"" : ", \"failure\""));
if (r == error_code_t(-1)) {
return "0, \"failure\"";
}
if (r == error_code_t(0)) {
return "0, \"success\"";
}
return fmt(r, ", \"failure\"");
}
};
@ -31,6 +37,9 @@ error_category const &generic_category() noexcept {
error_code::error_code() noexcept
: error_code{0, generic_category()} {}
error_code::error_code(error_code_t const &r) noexcept
: error_code{r, generic_category()} {}
error_code::error_code(error_code_t const &r, error_category const &ec) noexcept
: e_code_{r}, ec_{&ec} {}

View File

@ -24,10 +24,6 @@ struct sfmt_policy {
constexpr static std::size_t aligned_size = 32U;
};
struct sbuf_policy {
constexpr static std::size_t aligned_size = 2048U;
};
template <typename Policy = sfmt_policy>
span<char> local_fmt_sbuf() noexcept {
thread_local std::array<char, Policy::aligned_size> sbuf;
@ -143,10 +139,6 @@ bool sprintf(fmt_context &ctx, F fop, span<char const> const &fstr, span<char co
}
}
span<char> fmt_context_sbuf() noexcept {
return local_fmt_sbuf<sbuf_policy>();
}
} // namespace
/// \brief The context of fmt.
@ -156,9 +148,7 @@ fmt_context::fmt_context(std::string &j) noexcept
, offset_(0) {}
std::size_t fmt_context::capacity() noexcept {
return (offset_ < fmt_context_sbuf().size())
? fmt_context_sbuf().size()
: joined_.size();
return (offset_ < sbuf_.size()) ? sbuf_.size() : joined_.size();
}
void fmt_context::reset() noexcept {
@ -167,8 +157,8 @@ void fmt_context::reset() noexcept {
bool fmt_context::finish() noexcept {
LIBIMP_TRY {
if (offset_ < fmt_context_sbuf().size()) {
joined_.assign(fmt_context_sbuf().data(), offset_);
if (offset_ < sbuf_.size()) {
joined_.assign(sbuf_.data(), offset_);
} else {
joined_.resize(offset_);
}
@ -183,7 +173,7 @@ span<char> fmt_context::buffer(std::size_t sz) noexcept {
constexpr std::size_t fmt_context_aligned_size = 512U;
return (sz & ~(fmt_context_aligned_size - 1)) + fmt_context_aligned_size;
};
auto sbuf = fmt_context_sbuf();
auto sbuf = make_span(sbuf_);
LIBIMP_TRY {
if (offset_ < sbuf.size()) {
if ((offset_ + sz) < sbuf.size()) {

View File

@ -1,36 +0,0 @@
#include "libimp/result.h"
#include "libimp/horrible_cast.h"
LIBIMP_NAMESPACE_BEG_
result_code::result_code() noexcept
: result_code(false, {}) {}
result_code::result_code(result_code_t value) noexcept
: result_code(true, value) {}
result_code::result_code(result_type const &value) noexcept
: result_code(std::get<bool>(value),
std::get<result_code_t>(value)) {}
result_code::result_code(bool ok, std::uint64_t code) noexcept
: status_(code, ok) {}
result_code_t result_code::value() const noexcept {
return std::get<result_code_t>(status_);
}
bool result_code::ok() const noexcept {
return std::get<bool>(status_);
}
bool operator==(result_code const &lhs, result_code const &rhs) noexcept {
return lhs.status_ == rhs.status_;
}
bool operator!=(result_code const &lhs, result_code const &rhs) noexcept {
return lhs.status_ != rhs.status_;
}
LIBIMP_NAMESPACE_END_

View File

@ -74,9 +74,17 @@ result<int> shm_open_fd(std::string const &name, mode::type type) noexcept {
}
/// \brief Create/Open POSIX shared memory bject
return ::shm_open(name.c_str(), flag, S_IRUSR | S_IWUSR |
S_IRGRP | S_IWGRP |
S_IROTH | S_IWOTH);
int fd = ::shm_open(name.c_str(), flag, S_IRUSR | S_IWUSR |
S_IRGRP | S_IWGRP |
S_IROTH | S_IWOTH);
if (fd == posix::failed) {
auto err = sys::error();
log.error("failed: shm_open(name = ", name,
", type = ", type,
"). error = ", err);
return err;
}
return fd;
}
result_code ftruncate_fd(int fd, std::size_t size) noexcept {
@ -85,9 +93,9 @@ result_code ftruncate_fd(int fd, std::size_t size) noexcept {
if (::ftruncate(fd, size) != posix::succ) {
auto err = sys::error();
log.error("failed: ftruncate(", fd, ", ", size, "). error = ", err);
return err.code();
return err;
}
return {posix::succ};
return posix::succ;
}
} // namespace
@ -100,12 +108,8 @@ result_code ftruncate_fd(int fd, std::size_t size) noexcept {
result<shm_t> shm_open(std::string name, std::size_t size, mode::type type) noexcept {
LIBIMP_LOG_();
auto fd = shm_open_fd(name, type);
if (!fd) return {};
if (*fd == posix::failed) {
log.error("failed: shm_open(name = ", name,
", type = ", type,
"). error = ", sys::error());
return {};
if (!fd) {
return fd.error();
}
LIBIMP_UNUSED auto guard = std::unique_ptr<decltype(fd), void (*)(decltype(fd) *)> {
&fd, [](decltype(fd) *pfd) {
@ -115,26 +119,30 @@ result<shm_t> shm_open(std::string name, std::size_t size, mode::type type) noex
/// \brief Try to get the size of this fd
struct stat st;
if (::fstat(*fd, &st) == posix::failed) {
log.error("failed: fstat(fd = ", *fd, "). error = ", sys::error());
return {};
auto err = sys::error();
log.error("failed: fstat(fd = ", *fd, "). error = ", err);
return err;
}
/// \brief Truncate this fd to a specified length
if (size == 0) {
size = static_cast<std::size_t>(st.st_size);
if (!ftruncate_fd(*fd, size)) return {};
auto ret = ftruncate_fd(*fd, size);
if (!ret) return ret.error();
} else if (st.st_size > 0) {
/// \remark Based on the actual size.
size = static_cast<std::size_t>(st.st_size);
} else { // st.st_size <= 0
if (!ftruncate_fd(*fd, size)) return {};
auto ret = ftruncate_fd(*fd, size);
if (!ret) return ret.error();
}
/// \brief Creates a new mapping in the virtual address space of the calling process.
void *mem = ::mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, *fd, 0);
if (mem == MAP_FAILED) {
log.error("failed: mmap(size = ", size, ", fd = ", *fd, "). error = ", sys::error());
return {};
auto err = sys::error();
log.error("failed: mmap(size = ", size, ", fd = ", *fd, "). error = ", err);
return err;
}
return new shm_handle{std::move(name), size, mem};
}
@ -149,11 +157,11 @@ result_code shm_close(shm_t h) noexcept {
if (::munmap(shm->memp, shm->f_sz) == posix::failed) {
auto err = sys::error();
log.error("failed: munmap(", shm->memp, ", ", shm->f_sz, "). error = ", err);
return err.code();
return err;
}
/// \brief no unlink the file.
delete shm;
return {posix::succ};
return posix::succ;
}
LIBIPC_NAMESPACE_END_

View File

@ -42,9 +42,9 @@ result_code mmap_close(HANDLE h) {
if (!::CloseHandle(h)) {
auto err = sys::error();
log.error("failed: CloseHandle(", h, "). error = ", err);
return err.code();
return err;
}
return {ERROR_SUCCESS};
return ERROR_SUCCESS;
}
/**
@ -78,7 +78,7 @@ result<HANDLE> mmap_open(std::string const &file, std::size_t size, mode::type t
if (h == NULL) {
auto err = sys::error();
log.error("failed: OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, ", file, "). error = ", err);
return {nullptr, err.code()};
return {nullptr, err};
}
return h;
};
@ -91,7 +91,7 @@ result<HANDLE> mmap_open(std::string const &file, std::size_t size, mode::type t
if (h == NULL) {
auto err = sys::error();
log.error("failed: CreateFileMapping(PAGE_READWRITE | SEC_COMMIT, ", size, ", ", file, "). error = ", err);
return {nullptr, err.code()};
return {nullptr, err};
}
return h;
};
@ -133,7 +133,7 @@ result<LPVOID> mmap_memof(HANDLE h) {
if (mem == NULL) {
auto err = sys::error();
log.error("failed: MapViewOfFile(", h, ", FILE_MAP_ALL_ACCESS). error = ", err);
return {nullptr, err.code()};
return {nullptr, err};
}
return mem;
}
@ -152,7 +152,7 @@ result<SIZE_T> mmap_sizeof(LPCVOID mem) {
if (::VirtualQuery(mem, &mem_info, sizeof(mem_info)) == 0) {
auto err = sys::error();
log.error("failed: VirtualQuery(", mem, "). error = ", err);
return {false, (SIZE_T)err.code()};
return {false, err};
}
return mem_info.RegionSize;
}

View File

@ -22,19 +22,19 @@ result<shm_t> shm_open(std::string name, std::size_t size, mode::type type) noex
auto h = mmap_open(name, size, type);
if (*h == NULL) {
log.error("failed: mmap_open(name = ", name, ", size = ", size, ", type = ", type, ").");
return {nullptr, h.code_value()};
return {nullptr, h.error()};
}
auto mem = mmap_memof(*h);
if (*mem == NULL) {
log.error("failed: mmap_memof(", *h, ").");
mmap_close(*h);
return {nullptr, mem.code_value()};
return {nullptr, mem.error()};
}
auto sz = mmap_sizeof(*mem);
if (!sz) {
log.error("failed: mmap_sizeof(", *mem, ").");
mmap_close(*h);
return {nullptr, static_cast<result_code_t>(sz.value())};
return {nullptr, sz.error()};
}
return new shm_handle{std::move(name), *sz, *mem, *h};
}

View File

@ -13,50 +13,58 @@ TEST(result, ok) {
EXPECT_FALSE(ret.ok());
EXPECT_EQ(ret.value(), 0);
ret = {true, 0};
ret = {0, true};
EXPECT_TRUE(ret);
EXPECT_TRUE(ret.ok());
EXPECT_EQ(ret.value(), 0);
ret = {false, 1234};
ret = {0, false};
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 = {1234, false};
EXPECT_FALSE(ret);
EXPECT_FALSE(ret.ok());
EXPECT_EQ(*ret, 1234);
ret = imp::result_code(1234, true);
EXPECT_TRUE(ret);
EXPECT_TRUE(ret.ok());
EXPECT_EQ(ret.value(), 1234);
ret = {0, false};
EXPECT_FALSE(ret);
EXPECT_FALSE(ret.ok());
EXPECT_EQ(ret.value(), 0);
ret = {4321, true};
EXPECT_TRUE(ret);
EXPECT_TRUE(ret.ok());
EXPECT_EQ(ret.value(), 4321);
imp::result<int *> r2 {nullptr, 4321};
EXPECT_NE(r2, nullptr); // imp::result<int *>{nullptr}
EXPECT_EQ(*r2, nullptr);
EXPECT_FALSE(r2);
}
TEST(result, code) {
imp::result_code ret(true, 1234);
EXPECT_TRUE(ret);
EXPECT_TRUE(ret.ok());
EXPECT_EQ(ret.value(), 1234);
ret = {false, 0};
EXPECT_FALSE(ret);
EXPECT_FALSE(ret.ok());
EXPECT_EQ(ret.value(), 0);
ret = {true, 4321};
EXPECT_TRUE(ret);
EXPECT_TRUE(ret.ok());
EXPECT_EQ(ret.value(), 4321);
}
TEST(result, compare) {
imp::result_code r1, r2;
EXPECT_EQ(r1, r2);
imp::result_code r3(true);
imp::result_code r3(0, true);
EXPECT_NE(r1, r3);
imp::result_code r4(true, 222222);
imp::result_code r4(222222, true);
EXPECT_NE(r3, r4);
imp::result_code r5(false, 222222);
imp::result_code r5(222222, false);
EXPECT_NE(r4, r5);
r3 = r5;
EXPECT_EQ(r3, r5);
@ -65,33 +73,33 @@ TEST(result, compare) {
TEST(result, fmt) {
{
imp::result_code r1;
EXPECT_EQ(imp::fmt(r1), "[fail, value = 0]");
imp::result_code r2(true, 65537);
EXPECT_EQ(imp::fmt(r2), "[succ, value = 65537]");
EXPECT_EQ(imp::fmt(r1), "fail, value = 0");
imp::result_code r2(65537, true);
EXPECT_EQ(imp::fmt(r2), "succ, value = 65537");
imp::result_code r3(0);
EXPECT_EQ(imp::fmt(r3), "[succ, value = 0]");
EXPECT_EQ(imp::fmt(r3), "succ, value = 0");
}
{
imp::result<int> r0;
EXPECT_EQ(imp::fmt(r0), imp::fmt(imp::result_code()));
imp::result<int> r1 {false, -123};
EXPECT_EQ(imp::fmt(r1), imp::fmt("[fail, value = ", -123, "]"));
imp::result<int> r1 {-123, false};
EXPECT_EQ(imp::fmt(r1), imp::fmt("fail, value = ", -123));
imp::result<void *> r2 {&r1};
EXPECT_EQ(imp::fmt(r2), imp::fmt("[succ, value = ", (void *)&r1, "]"));
EXPECT_EQ(imp::fmt(r2), imp::fmt("succ, value = ", (void *)&r1));
int aaa {};
imp::result<int *> r3 {&aaa};
EXPECT_EQ(imp::fmt(r3), imp::fmt("[succ, value = ", (void *)&aaa, "]"));
EXPECT_EQ(imp::fmt(r3), imp::fmt("succ, value = ", (void *)&aaa));
imp::result<int *> r4 {nullptr};
EXPECT_EQ(imp::fmt(r4), imp::fmt("[fail, value = ", nullptr, ", code = 0]"));
EXPECT_EQ(imp::fmt(r4), imp::fmt("fail, value = ", nullptr, ", error = [generic: 0, \"failure\"]"));
r4 = {nullptr, 1234};
EXPECT_EQ(imp::fmt(r4), imp::fmt("[fail, value = ", nullptr, ", code = 1234]"));
EXPECT_EQ(imp::fmt(r4), imp::fmt("fail, value = ", nullptr, ", error = [generic: 1234, \"failure\"]"));
imp::result<int *> r5;
EXPECT_EQ(imp::fmt(r5), "[fail, value = null, code = 0]");
EXPECT_EQ(imp::fmt(r5), "fail, value = null, error = [generic: 0, \"failure\"]");
}
{
imp::result<std::int64_t> r1 {-123};
EXPECT_EQ(imp::fmt(r1), imp::fmt("[succ, value = ", -123, "]"));
EXPECT_EQ(imp::fmt(r1), imp::fmt("succ, value = ", -123));
}
}