/** * @file libimp/log.h * @author mutouyun (orz@orzz.org) * @brief Simple log output component * @date 2022-05-22 */ #pragma once #include #include #include #include "fmt/format.h" #include "fmt/chrono.h" #include "libimp/def.h" #include "libimp/detect_plat.h" #include "libimp/export.h" LIBIMP_NAMESPACE_BEG_ namespace detail_log { template class has_fn_info { static std::false_type check(...); template static auto check(U *u) -> decltype(u->info(std::declval()), std::true_type{}); public: using type = decltype(check(static_cast(nullptr))); }; template constexpr bool has_fn_info_v = has_fn_info::type::value; template class has_fn_error { static std::false_type check(...); template static auto check(U *u) -> decltype(u->error(std::declval()), std::true_type{}); public: using type = decltype(check(static_cast(nullptr))); }; template constexpr bool has_fn_error_v = has_fn_error::type::value; struct vtable_t { void (*info )(void *, std::string &&); void (*error)(void *, std::string &&); }; template class traits { template static auto make_fn_info() noexcept -> std::enable_if_t, void (*)(void *, std::string &&)> { return [](void *p, std::string &&s) { static_cast(p)->info(std::move(s)); }; } template static auto make_fn_info() noexcept -> std::enable_if_t, void (*)(void *, std::string &&)> { return [](void *, std::string &&) {}; } template static auto make_fn_error() noexcept -> std::enable_if_t, void (*)(void *, std::string &&)> { return [](void *p, std::string &&s) { static_cast(p)->error(std::move(s)); }; } template static auto make_fn_error() noexcept -> std::enable_if_t, void (*)(void *, std::string &&)> { return [](void *, std::string &&) {}; } public: static auto make_vtable() noexcept { static vtable_t vt { make_fn_info (), make_fn_error(), }; return &vt; } }; } // namespace detail_log class LIBIMP_EXPORT log_printer { void *objp_ {nullptr}; detail_log::vtable_t *vtable_ {nullptr}; public: log_printer() noexcept = default; template log_printer(T &p) noexcept : objp_ (static_cast(&p)) , vtable_(detail_log::traits::make_vtable()) {} explicit operator bool() const noexcept; void info (std::string &&); void error(std::string &&); }; class LIBIMP_EXPORT log_std_t { public: void info (std::string &&) const; void error(std::string &&) const; }; LIBIMP_EXPORT extern log_std_t log_std; namespace log { template std::string fmt(Fmt &&ft, A &&... args) { return ::fmt::format(std::forward(ft), std::forward(args)...); } class LIBIMP_EXPORT gripper { log_printer printer_; char const *func_; gripper &output(void (log_printer::*out_fn)(std::string &&), char type, std::string &&log_str) { auto tm = std::chrono::system_clock::now(); auto ms = std::chrono::time_point_cast(tm).time_since_epoch().count() % 1000; auto px = fmt("[{}][{:%Y-%m-%d %H:%M:%S}.{:03}][{}]\n", type, tm, ms, func_); (printer_.*out_fn)(std::move(px += std::move(log_str))); return *this; } public: gripper(log_printer printer, char const *func) noexcept : printer_(printer) , func_ (func) {} template gripper &info(Fmt &&ft, A &&... args) { return output(&log_printer::info, 'I', fmt(std::forward(ft), std::forward(args)...)); } template gripper &error(Fmt &&ft, A &&... args) { return output(&log_printer::error, 'E', fmt(std::forward(ft), std::forward(args)...)); } }; } // namespace log LIBIMP_NAMESPACE_END_