/** * \file libimp/log.h * \author mutouyun (orz@orzz.org) * \brief Simple log output component. * \date 2022-05-22 */ #pragma once #include #include #include #include #include "libimp/def.h" #include "libimp/detect_plat.h" #include "libimp/export.h" #include "libimp/enum_cast.h" #include "libimp/fmt.h" LIBIMP_NAMESPACE_BEG_ namespace log { enum class level : std::int32_t { trace, debug, info, warning, error, failed, }; struct context { log::level level; std::chrono::system_clock::time_point tp; char const *func; std::string text; }; LIBIMP_EXPORT std::string to_string(context &&) noexcept; LIBIMP_EXPORT bool to_string(fmt_context &ctx, context &&) noexcept; /// \brief Custom defined fmt_to method for imp::fmt template bool tag_invoke(decltype(::LIBIMP::fmt_to), fmt_context &ctx, context &&arg) noexcept { return ::LIBIMP::log::to_string(ctx, std::move(arg)); } } // namespace log namespace detail_log { enum out_type : unsigned { out_none = 0x0, out_string = 0x1, out_context = 0x2, }; template class has_fn_output { static std::integral_constant check(...); template ().output(log::level::trace, std::declval()))> static std::integral_constant check(U *u); template ().output(std::declval()))> static std::integral_constant check(U *u); public: using type = decltype(check(static_cast(nullptr))); }; template constexpr out_type has_fn_output_v = has_fn_output::type::value; struct vtable_t { void (*output)(void *, log::context &&); }; template class traits { template static auto make_fn_output() noexcept -> std::enable_if_t<(has_fn_output_v == out_none), decltype(vtable_t{}.output)> { return [](void *, log::context &&) {}; } template static auto make_fn_output() noexcept -> std::enable_if_t<(has_fn_output_v == out_string), decltype(vtable_t{}.output)> { return [](void *p, log::context &&ctx) { auto lev = ctx.level; auto str = to_string(std::move(ctx)); static_cast(p)->output(lev, std::move(str)); }; } template static auto make_fn_output() noexcept -> std::enable_if_t<(has_fn_output_v == out_context), decltype(vtable_t{}.output)> { return [](void *p, log::context &&ctx) { static_cast(p)->output(std::move(ctx)); }; } public: static auto make_vtable() noexcept { static vtable_t vt { make_fn_output() }; return &vt; } }; } // namespace detail_log namespace log { class LIBIMP_EXPORT printer { void *objp_ {nullptr}; detail_log::vtable_t *vtable_ {nullptr}; public: printer() noexcept = default; template ::value>> printer(T &p) noexcept : objp_ (static_cast(&p)) , vtable_(detail_log::traits::make_vtable()) {} explicit operator bool() const noexcept; void output(context) noexcept; }; /// \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; /** * \brief Log information grips. */ class grip { printer printer_; char const *func_; level level_limit_; template grip &output(log::level l, A &&... args) noexcept { if (!printer_ || (enum_cast(l) < enum_cast(level_limit_))) { return *this; } context ctx; LIBIMP_TRY { ctx = { l, std::chrono::system_clock::now(), func_, fmt(std::forward(args)...), }; } LIBIMP_CATCH(std::exception const &e) { /// \remark [TBD] std::string constructor may throw an exception ctx = { level::failed, std::chrono::system_clock::now(), func_, e.what(), }; } printer_.output(std::move(ctx)); return *this; } public: grip(char const *func, printer printer = std_out, level level_limit = level::info) noexcept : printer_ (printer) , func_ (func) , level_limit_(level_limit) {} template grip &trace (A &&...args) noexcept { return output(log::level::trace , std::forward(args)...); } template grip &debug (A &&...args) noexcept { return output(log::level::debug , std::forward(args)...); } template grip &info (A &&...args) noexcept { return output(log::level::info , std::forward(args)...); } template grip &warning(A &&...args) noexcept { return output(log::level::warning, std::forward(args)...); } template grip &error (A &&...args) noexcept { return output(log::level::error , std::forward(args)...); } template grip &failed (A &&...args) noexcept { return output(log::level::failed , std::forward(args)...); } }; } // namespace log LIBIMP_NAMESPACE_END_ #define LIBIMP_LOG_(...) ::LIBIMP::log::grip log {__func__, __VA_ARGS__}