// license:GPL-2.0+ // copyright-holders:Couriersud /// /// \file pfmtlog.h /// #ifndef PFMTLOG_H_ #define PFMTLOG_H_ #include "penum.h" #include "pgsl.h" #include "ppmf.h" #include "pstring.h" #include "ptypes.h" #include #include #include #define PERRMSGV(name, narg, str) \ struct name : public plib::perrmsg \ { \ template explicit name(Args&&... args) \ : plib::perrmsg(str, std::forward(args)...) \ { static_assert((narg) == sizeof...(args), "Argument count mismatch"); } \ }; namespace plib { PENUM(plog_level, DEBUG, VERBOSE, INFO, WARNING, ERROR, FATAL) template struct ptype_traits_base { static constexpr const bool is_signed = std::numeric_limits::is_signed; static char32_t fmt_spec() { return 'u'; } static void streamify(std::ostream &s, const T &v) { s << v; } }; #if (PUSE_FLOAT128) template <> struct ptype_traits_base { // FIXME: need native support at some time static constexpr const bool is_signed = true; static char32_t fmt_spec() { return 'f'; } static void streamify(std::ostream &s, const FLOAT128 &v) { s << narrow_cast(v); } }; #endif template struct ptype_traits; template<> struct ptype_traits { // FIXME: need native support at some time static constexpr const bool is_signed = true; static char32_t fmt_spec() { return 'd'; } template ::value>> static void streamify(std::ostream &s, const T &v) { s << narrow_cast(v); } }; template<> struct ptype_traits : ptype_traits_base { }; template<> struct ptype_traits : ptype_traits_base { static char32_t fmt_spec() { return is_signed ? 'd' : 'u'; } }; template<> struct ptype_traits : ptype_traits_base { static char32_t fmt_spec() { return 'd'; } }; template<> struct ptype_traits : ptype_traits_base { static char32_t fmt_spec() { return 'd'; } }; template<> struct ptype_traits : ptype_traits_base { static char32_t fmt_spec() { return 'd'; } }; template<> struct ptype_traits : ptype_traits_base { static char32_t fmt_spec() { return 'd'; } }; template<> struct ptype_traits : ptype_traits_base { static char32_t fmt_spec() { return 'd'; } }; template<> struct ptype_traits : ptype_traits_base { static char32_t fmt_spec() { return 'u'; } }; template<> struct ptype_traits : ptype_traits_base { static char32_t fmt_spec() { return 'u'; } }; template<> struct ptype_traits : ptype_traits_base { static char32_t fmt_spec() { return 'u'; } }; template<> struct ptype_traits : ptype_traits_base { static char32_t fmt_spec() { return 'u'; } }; template<> struct ptype_traits : ptype_traits_base { static char32_t fmt_spec() { return 'u'; } }; template<> struct ptype_traits : ptype_traits_base { static char32_t fmt_spec() { return 'f'; } }; template<> struct ptype_traits : ptype_traits_base { static char32_t fmt_spec() { return 'f'; } }; template<> struct ptype_traits : ptype_traits_base { static char32_t fmt_spec() { return 'f'; } }; #if (PUSE_FLOAT128) template<> struct ptype_traits : ptype_traits_base { static char32_t fmt_spec() { return 'f'; } }; #endif template<> struct ptype_traits : ptype_traits_base { static char32_t fmt_spec() { return 's'; } }; template<> struct ptype_traits : ptype_traits_base { static char32_t fmt_spec() { return 's'; } }; template<> struct ptype_traits : ptype_traits_base { static char32_t fmt_spec() { return 's'; } static void streamify(std::ostream &s, const char16_t *v) { const putf16string su16(v); s << putf8string(su16).c_str(); } }; template<> struct ptype_traits : ptype_traits_base { static char32_t fmt_spec() { return 's'; } static void streamify(std::ostream &s, const char32_t *v) { const putf32string su32(v); s << putf8string(su32).c_str(); } }; template<> struct ptype_traits : ptype_traits_base { static char32_t fmt_spec() { return 's'; } }; template<> struct ptype_traits : ptype_traits_base { static char32_t fmt_spec() { return 's'; } }; template<> struct ptype_traits : ptype_traits_base { static char32_t fmt_spec() { return 's'; } static void streamify(std::ostream &s, const putf16string &v) { s << putf8string(v).c_str(); } }; template<> struct ptype_traits : ptype_traits_base { static char32_t fmt_spec() { return 'p'; } }; class pfmt { public: explicit pfmt(const pstring &fmt) : m_str(fmt), m_locale(std::locale::classic()), m_arg(0) { } explicit pfmt(const std::locale &loc, const pstring &fmt) : m_str(fmt), m_locale(loc), m_arg(0) { } PCOPYASSIGNMOVE(pfmt, default) ~pfmt() noexcept = default; operator pstring() const { return m_str; } template std::enable_if_t::value, pfmt &> f(const T &x) {return format_element('f', x); } template std::enable_if_t::value, pfmt &> e(const T &x) {return format_element('e', x); } template std::enable_if_t::value, pfmt &> g(const T &x) {return format_element('g', x); } pfmt &operator ()(const void *x) {return format_element('p', x); } pfmt &operator ()(const pstring &x) {return format_element('s', x.c_str() ); } template pfmt &operator ()(const T &x) { return format_element(x); } template pfmt &operator ()(const T *x) { return format_element(x); } pfmt &operator ()() { return *this; } template pfmt &operator()(X&& x, Y && y, Args&&... args) { return ((*this)(std::forward(x)))(std::forward(y), std::forward(args)...); } template std::enable_if_t::value, pfmt &> x(const T &x) { return format_element('x', x); } template std::enable_if_t::value, pfmt &> o(const T &x) { return format_element('o', x); } friend std::ostream& operator<<(std::ostream &ostrm, const pfmt &fmt) { ostrm << putf8string(fmt.m_str); return ostrm; } protected: struct rtype { rtype() : ret(0), p(0), sl(0) {} int ret; pstring::size_type p; pstring::size_type sl; }; rtype setfmt(std::stringstream &strm, char32_t cfmt_spec); template pfmt &format_element(T &&v) { return format_element(ptype_traits::type>::fmt_spec(), std::forward(v)); } template pfmt &format_element(const char32_t cfmt_spec, T &&v) { rtype ret; m_arg++; do { std::stringstream strm; strm.imbue(m_locale); ret = setfmt(strm, cfmt_spec); if (ret.ret>=0) { ptype_traits::type>::streamify(strm, std::forward(v)); const pstring ps(putf8string(strm.str())); m_str = m_str.substr(0, ret.p) + ps + m_str.substr(ret.p + ret.sl); } } while (ret.ret == 1); return *this; } private: pstring m_str; std::locale m_locale; unsigned m_arg; }; template class pfmt_writer_t { public: explicit pfmt_writer_t() : m_enabled(true) { } PCOPYASSIGNMOVE(pfmt_writer_t, delete) // runtime enable template void log(const pstring & fmt, Args&&... args) const noexcept { if (build_enabled && enabled && m_enabled) { pfmt pf(fmt); dynamic_cast(*this).vdowrite(xlog(pf, std::forward(args)...)); } } template void operator ()(const pstring &fmt, Args&&... args) const noexcept { if (build_enabled && m_enabled) { pfmt pf(fmt); static_cast(*this).vdowrite(xlog(pf, std::forward(args)...)); } } void set_enabled(const bool v) { m_enabled = v; } bool is_enabled() const { return m_enabled; } protected: ~pfmt_writer_t() noexcept = default; private: pfmt &xlog(pfmt &fmt) const { return fmt; } template pfmt &xlog(pfmt &fmt, X&& x, Args&&... args) const { return xlog(fmt(std::forward(x)), std::forward(args)...); } bool m_enabled; }; using plog_delegate = plib::pmfp; template class plog_channel : public pfmt_writer_t, build_enabled> { friend class pfmt_writer_t, build_enabled>; public: explicit plog_channel(plog_delegate logger) : pfmt_writer_t() , m_logger(logger) { } PCOPYASSIGNMOVE(plog_channel, delete) ~plog_channel() noexcept = default; protected: void vdowrite(const pstring &ls) const noexcept { m_logger(L, ls); } private: plog_delegate m_logger; }; template class plog_base { public: explicit plog_base(plog_delegate logger) : debug(logger), info(logger), verbose(logger), warning(logger), error(logger), fatal(logger) {} PCOPYASSIGNMOVE(plog_base, delete) virtual ~plog_base() noexcept = default; plog_channel debug; plog_channel info; plog_channel verbose; plog_channel warning; plog_channel error; plog_channel fatal; }; struct perrmsg { template explicit perrmsg(const char (&fmt)[N], Args&&... args) // NOLINT(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays) : m_msg(plib::pfmt(fmt)(std::forward(args)...)) { } operator pstring const & () const noexcept { return m_msg; } const pstring & operator ()() const noexcept { return m_msg; } private: pstring m_msg; }; } // namespace plib template plib::pfmt& operator<<(plib::pfmt &p, T&& val) { return p(std::forward(val)); } #endif // PFMT_LOG_H_