upd: [imp] optimized log interface implementation

This commit is contained in:
mutouyun 2023-05-27 21:19:25 +08:00
parent 19cb391624
commit ca78e3511b
9 changed files with 95 additions and 202 deletions

View File

@ -6,14 +6,14 @@
namespace {
void imp_log_no_output(benchmark::State &state) {
imp::log::grip log {__func__, {}};
LIBIMP_LOG_([](auto &&) {});
for (auto _ : state) {
log.debug("hello log.");
}
}
void imp_log_gripper(benchmark::State &state) {
imp::log::grip log {__func__, {}};
void imp_log_info(benchmark::State &state) {
LIBIMP_LOG_([](auto &&) {});
for (auto _ : state) {
log.info("hello log.");
}
@ -22,4 +22,4 @@ void imp_log_gripper(benchmark::State &state) {
} // namespace
BENCHMARK(imp_log_no_output);
BENCHMARK(imp_log_gripper);
BENCHMARK(imp_log_info);

View File

@ -13,6 +13,7 @@
#include <utility>
#include <type_traits>
#include <chrono> // std::chrono::time_point
#include <tuple>
#include <cstddef>
#include <ctime> // std::tm, std::localtime
@ -163,5 +164,15 @@ bool tag_invoke(decltype(::LIBIMP::fmt_to), fmt_context &ctx, span<T> s) {
return true;
}
template <typename Tp, std::size_t... I>
bool unfold_tuple_fmt_to(fmt_context &ctx, Tp const &tp, std::index_sequence<I...>) {
return fmt_to(ctx, std::get<I>(tp)...);
}
template <typename... T>
bool tag_invoke(decltype(::LIBIMP::fmt_to), fmt_context &ctx, std::tuple<T...> const &tp) {
return unfold_tuple_fmt_to(ctx, tp, std::index_sequence_for<T...>{});
}
} // namespace detail
LIBIMP_NAMESPACE_END_

View File

@ -71,9 +71,9 @@ struct is_same_first<T, T> : std::true_type {};
} // namespace detail
template <typename T, typename... A>
using is_not_match =
using not_match =
typename std::enable_if<!detail::is_same_first<T,
typename std::decay<A>::type...>::value>::type;
typename std::decay<A>::type...>::value, bool>::type;
/**
* \brief Determines whether a type is specialized from a particular template.

View File

@ -83,115 +83,55 @@ std::string context_to_string(context<T...> const &l_ctx) noexcept {
}
}
namespace detail {
enum out_type : unsigned {
out_none = 0x0,
out_string = 0x1,
};
template <typename T>
class has_fn_output {
static std::integral_constant<out_type, out_none> check(...);
template <typename U, typename = decltype(std::declval<U &>().output(log::level::trace, std::declval<std::string>()))>
static std::integral_constant<out_type, out_string> check(U *u);
public:
using type = decltype(check(static_cast<T *>(nullptr)));
};
template <typename T>
constexpr out_type has_fn_output_v = has_fn_output<T>::type::value;
struct vtable_t {
void (*output)(void *, log::level, std::string &&);
};
template <typename T>
class traits {
template <typename U>
static auto make_fn_output() noexcept
-> std::enable_if_t<(has_fn_output_v<U> == out_none), decltype(vtable_t{}.output)> {
return [](void *, log::level, std::string &&) {};
}
template <typename U>
static auto make_fn_output() noexcept
-> std::enable_if_t<(has_fn_output_v<U> == out_string), decltype(vtable_t{}.output)> {
return [](void *p, log::level lev, std::string &&str) {
static_cast<U *>(p)->output(lev, std::move(str));
};
}
public:
static auto make_vtable() noexcept {
static vtable_t vt { make_fn_output<T>() };
return &vt;
}
};
} // namespace detail
class printer {
void *objp_ {nullptr};
detail::vtable_t *vtable_ {nullptr};
public:
printer() noexcept = default;
template <typename T,
typename = is_not_match<printer, T>>
printer(T &p) noexcept
: objp_ (static_cast<void *>(&p))
, vtable_(detail::traits<T>::make_vtable()) {}
explicit operator bool() const noexcept {
return (objp_ != nullptr) && (vtable_ != nullptr);
}
template <typename... T>
void output(context<T...> const &ctx) noexcept {
if (!*this) return;
LIBIMP_TRY {
vtable_->output(objp_, ctx.level, context_to_string(ctx));
} LIBIMP_CATCH(...) {}
}
};
/// \class class LIBIMP_EXPORT std_t
/// \brief Standard console output.
class LIBIMP_EXPORT std_t {
public:
void output(log::level, std::string &&) noexcept;
};
/// \brief Standard console output object.
LIBIMP_EXPORT extern std_t std_out;
inline auto &make_std_out() noexcept {
static auto std_out = [](auto const &ctx) {
auto s = context_to_string(ctx);
switch (ctx.level) {
case level::trace:
case level::debug:
case level::info:
std::fprintf(stdout, "%s\n", s.c_str());
break;
case level::warning:
case level::error:
case level::failed:
std::fprintf(stderr, "%s\n", s.c_str());
break;
default:
break;
}
};
return std_out;
}
/**
* \brief Log information grips.
*/
template <typename Outputer>
class grip {
printer printer_;
Outputer out_;
char const *func_;
level level_limit_;
template <typename... A>
grip &output(log::level l, A &&... args) noexcept {
if (!printer_ || (underlyof(l) < underlyof(level_limit_))) {
if (underlyof(l) < underlyof(level_limit_)) {
return *this;
}
printer_.output(context<A &&...> {
LIBIMP_TRY {
out_(context<A &&...> {
l, std::chrono::system_clock::now(), func_,
std::forward_as_tuple(std::forward<A>(args)...),
});
} LIBIMP_CATCH(...) {}
return *this;
}
public:
grip(char const *func, printer printer = std_out, level level_limit = level::info) noexcept
: printer_ (printer)
template <typename O>
grip(char const *func, O &&out, level level_limit) noexcept
: out_ (std::forward<O>(out))
, func_ (func)
, level_limit_(level_limit) {}
@ -203,7 +143,20 @@ public:
template <typename... A> grip &failed (A &&...args) noexcept { return output(log::level::failed , std::forward<A>(args)...); }
};
template <typename O>
inline auto make_grip(char const *func, O &&out, level level_limit = level::info) noexcept {
return grip<std::decay_t<O>>(func, std::forward<O>(out), level_limit);
}
inline auto make_grip(char const *func, level level_limit = level::info) noexcept {
return make_grip(func, make_std_out(), level_limit);
}
inline auto make_grip(char const */*ignore*/, char const *func, level level_limit = level::info) noexcept {
return make_grip(func, make_std_out(), level_limit);
}
} // namespace log
LIBIMP_NAMESPACE_END_
#define LIBIMP_LOG_(...) ::LIBIMP::log::grip log(__func__,##__VA_ARGS__)
#define LIBIMP_LOG_(...) auto log = ::LIBIMP::log::make_grip(__func__,##__VA_ARGS__)

View File

@ -139,7 +139,7 @@ private:
public:
template <typename... A,
typename = is_not_match<result, A...>,
typename = not_match<result, A...>,
typename = decltype(type_traits_t::init_code(std::declval<storage_t &>()
, std::declval<A>()...))>
result(A &&... args) noexcept {
@ -168,7 +168,7 @@ private:
public:
template <typename... A,
typename = is_not_match<result, A...>,
typename = not_match<result, A...>,
typename = decltype(type_traits_t::init_code(std::declval<storage_t &>()
, std::declval<A>()...))>
result(A &&... args) noexcept {

View File

@ -1,28 +0,0 @@
#include "libimp/log.h"
#include <cstdio>
LIBIMP_NAMESPACE_BEG_
namespace log {
std_t std_out;
void std_t::output(log::level l, std::string &&s) noexcept {
switch (l) {
case level::trace:
case level::debug:
case level::info:
std::fprintf(stdout, "%s\n", s.c_str());
break;
case level::warning:
case level::error:
case level::failed:
std::fprintf(stderr, "%s\n", s.c_str());
break;
default:
break;
}
}
} // namespace log
LIBIMP_NAMESPACE_END_

View File

@ -28,7 +28,7 @@ inline LPSECURITY_ATTRIBUTES get_sa() {
initiator() {
using namespace ::LIBIMP;
log::grip log {"get_sa"};
LIBIMP_LOG_("get_sa");
if (!::InitializeSecurityDescriptor(&sd_, SECURITY_DESCRIPTOR_REVISION)) {
log.error("failed: InitializeSecurityDescriptor(SECURITY_DESCRIPTOR_REVISION). "
"error = ", sys::error());

View File

@ -13,12 +13,11 @@ namespace {
/**
* \brief Check that bytes is not 0 and that the alignment is a power of two.
*/
bool verify_args(::LIBIMP::log::grip &log, std::size_t bytes, std::size_t alignment) noexcept {
bool verify_args(std::size_t bytes, std::size_t alignment) noexcept {
if (bytes == 0) {
return false;
}
if ((alignment == 0) || (alignment & (alignment - 1)) != 0) {
log.error("invalid bytes = ", bytes, ", alignment = ", alignment);
return false;
}
return true;
@ -47,7 +46,8 @@ new_delete_resource *new_delete_resource::get() noexcept {
*/
void *new_delete_resource::allocate(std::size_t bytes, std::size_t alignment) noexcept {
LIBIMP_LOG_();
if (!verify_args(log, bytes, alignment)) {
if (!verify_args(bytes, alignment)) {
log.error("invalid bytes = ", bytes, ", alignment = ", alignment);
return nullptr;
}
#if defined(LIBIMP_CPP_17)
@ -89,7 +89,8 @@ void new_delete_resource::deallocate(void *p, std::size_t bytes, std::size_t ali
if (p == nullptr) {
return;
}
if (!verify_args(log, bytes, alignment)) {
if (!verify_args(bytes, alignment)) {
log.error("invalid bytes = ", bytes, ", alignment = ", alignment);
return;
}
#if defined(LIBIMP_CPP_17)

View File

@ -6,76 +6,9 @@
#include "libimp/log.h"
TEST(log, detail) {
EXPECT_EQ(imp::log::detail::has_fn_output_v<int>, imp::log::detail::out_none);
struct foo {
int info(std::string);
};
EXPECT_EQ(imp::log::detail::has_fn_output_v<foo>, imp::log::detail::out_none);
struct bar {
int info(char const *);
void output(imp::log::level, std::string &&);
};
EXPECT_EQ(imp::log::detail::has_fn_output_v<bar>, imp::log::detail::out_string);
struct str {
str(std::string const &);
};
struct foo_bar {
void output(imp::log::level, str);
};
EXPECT_EQ(imp::log::detail::has_fn_output_v<foo_bar>, imp::log::detail::out_string);
auto vt_int = imp::log::detail::traits<int>::make_vtable();
EXPECT_NE(vt_int, nullptr);
EXPECT_NO_THROW(vt_int->output(nullptr, imp::log::level::error, ""));
struct log {
std::string what;
void output(imp::log::level l, std::string &&s) {
if (l == imp::log::level::error) what += s + '\n';
}
} ll;
auto vt_log = imp::log::detail::traits<log>::make_vtable();
EXPECT_NE(vt_log, nullptr);
vt_log->output(&ll, imp::log::level::info, "123");
vt_log->output(&ll, imp::log::level::info, "321");
vt_log->output(&ll, imp::log::level::info, "654");
vt_log->output(&ll, imp::log::level::info, "456");
std::cout << ll.what << "\n";
SUCCEED();
}
TEST(log, log_printer) {
struct log {
std::string i;
std::string e;
void output(imp::log::level l, std::string &&s) {
if (l == imp::log::level::error) e += s + "\n";
else if (l == imp::log::level::info) i += s + "\n";
}
} ll;
imp::log::printer pt = ll;
pt.output(imp::log::context<char const *>{imp::log::level::info , std::chrono::system_clock::now(), __func__, "hello "});
pt.output(imp::log::context<char const *>{imp::log::level::error, std::chrono::system_clock::now(), __func__, "failed: "});
pt.output(imp::log::context<char const *>{imp::log::level::info , std::chrono::system_clock::now(), __func__, "log-pt"});
pt.output(imp::log::context<char const *>{imp::log::level::error, std::chrono::system_clock::now(), __func__, "whatever"});
std::cout << ll.i << "\n";
std::cout << ll.e << "\n";
imp::log::printer ps = imp::log::std_out;
ps.output(imp::log::context<char const *>{imp::log::level::info, std::chrono::system_clock::now(), __func__, "hello world"});
SUCCEED();
}
TEST(log, gripper) {
{
imp::log::grip log {__func__};
LIBIMP_LOG_();
log.info("hello");
}
{
@ -84,3 +17,26 @@ TEST(log, gripper) {
}
SUCCEED();
}
TEST(log, custom) {
struct log {
std::string i;
std::string e;
} ll_data;
auto ll = [&ll_data](auto &&ctx) mutable {
auto s = imp::fmt(ctx.params);
if (ctx.level == imp::log::level::error) ll_data.e += s + " ";
else
if (ctx.level == imp::log::level::info ) ll_data.i += s + " ";
};
LIBIMP_LOG_(ll);
log.info ("hello");
log.error("failed:");
log.info ("log-pt");
log.error("whatever");
EXPECT_EQ(ll_data.i, "hello log-pt ");
EXPECT_EQ(ll_data.e, "failed: whatever ");
}