From 5ad143d5b69912658edbb4aa8201938055851a3b Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Tue, 12 Sep 2023 12:24:33 -0400 Subject: [PATCH] nestlog: control indent level + args with scope entry --- example/ex2/ex2.cpp | 17 ++++--- include/nestlog/log_state.hpp | 22 +++++++--- include/nestlog/log_streambuf.hpp | 3 +- include/nestlog/pad.hpp | 27 ++++++++---- include/nestlog/scope.hpp | 73 +++++++++++++++++++------------ include/nestlog/tostr.hpp | 6 +++ 6 files changed, 98 insertions(+), 50 deletions(-) diff --git a/example/ex2/ex2.cpp b/example/ex2/ex2.cpp index dfc2a7bd..85066b73 100644 --- a/example/ex2/ex2.cpp +++ b/example/ex2/ex2.cpp @@ -2,28 +2,33 @@ #include "nestlog/scope.hpp" +using namespace xo; + int fib(int n) { - XO_SCOPE(log); + scope log(XO_SSETUP0(), ":n ", n); int retval = 1; if (n >= 2) { - log(":n ", n); retval = fib(n - 1) + fib(n - 2); + log(":n ", n); } - - log(":n ", n, " -> :retval ", retval); + log("<- :retval ", retval); return retval; } int main(int argc, char ** argv) { - XO_SCOPE(log); + log_config::indent_width = 4; int n = 4; + + scope log(XO_SSETUP0(), ":n", 4); + int fn = fib(n); - log(":n ", n, " :fib(n) ", fn); + log(":n ", n); + log("<- :fib(n) ", fn); } diff --git a/include/nestlog/log_state.hpp b/include/nestlog/log_state.hpp index 41556b8a..8d096950 100644 --- a/include/nestlog/log_state.hpp +++ b/include/nestlog/log_state.hpp @@ -2,7 +2,9 @@ #pragma once +#include "log_config.hpp" #include "log_streambuf.hpp" +#include "pad.hpp" #include #include // for std::unique_ptr @@ -44,11 +46,12 @@ namespace xo { /* common implementation for .preamble(), .postamble() */ void entryexit_aux(std::string_view name1, std::string_view name2, - char label_char); + char label_char, + bool newline_flag); private: /* current nesting level for this thread */ - uint32_t nesting_level_ = 0; + std::uint32_t nesting_level_ = 0; /* buffer space for logging * (before pretty-printing for scope::log() calls that span multiple lines) @@ -95,16 +98,20 @@ namespace xo { #endif /* indent to nesting level */ + this->ss_ << pad(this->nesting_level_ * log_config::indent_width, pad_char); +#ifdef OBSOLETE for(uint32_t i = 0, n = this->nesting_level_; iss_ << pad_char; } +#endif } /*indent*/ template void state_impl::entryexit_aux(std::string_view name1, std::string_view name2, - char label_char) + char label_char, + bool newline_flag) { log_streambuf_type * sbuf = this->p_sbuf_phase1_.get(); @@ -115,7 +122,10 @@ namespace xo { this->ss_ << label_char; /* scope name */ - this->ss_ << name1 << name2 << "\n"; + this->ss_ << name1 << name2; + + if (newline_flag) + this->ss_ << "\n"; } /*entryexit_aux*/ template @@ -123,7 +133,7 @@ namespace xo { state_impl::preamble(std::string_view name1, std::string_view name2) { - this->entryexit_aux(name1, name2, '+' /*label_char*/); + this->entryexit_aux(name1, name2, '+' /*label_char*/, false /*!newline_flag*/); } /*preamble*/ template @@ -131,7 +141,7 @@ namespace xo { state_impl::postamble(std::string_view name1, std::string_view name2) { - this->entryexit_aux(name1, name2, '-' /*label_char*/); + this->entryexit_aux(name1, name2, '-' /*label_char*/, true /*newline_flag*/); } /*postamble*/ template diff --git a/include/nestlog/log_streambuf.hpp b/include/nestlog/log_streambuf.hpp index 5d06dab7..02b4b183 100644 --- a/include/nestlog/log_streambuf.hpp +++ b/include/nestlog/log_streambuf.hpp @@ -5,7 +5,8 @@ #include #include #include // e.g. for std::memcpy() -#include // e.g. for std::memcpy() +#include +#include namespace xo { /* recycling buffer for logging. diff --git a/include/nestlog/pad.hpp b/include/nestlog/pad.hpp index 123c8d5c..e85d9c25 100644 --- a/include/nestlog/pad.hpp +++ b/include/nestlog/pad.hpp @@ -3,36 +3,47 @@ #pragma once #include +#include namespace xo { /* use: * ostream os = ...; + * + * 1. * os << ":" << pad(8) << ":" * * writes * : : * + * 2. + * os << pad(16, '-') + * + * writes + * ---------------- + * * on os */ class pad_impl { public: - pad_impl(int32_t n) : n_pad_(n) {} + pad_impl(std::uint32_t n, char pad_char) : n_pad_{n}, pad_char_{pad_char} {} - uint32_t n_pad() const { return n_pad_; } + std::uint32_t n_pad() const { return n_pad_; } + char pad_char() const { return pad_char_; } private: - uint32_t n_pad_ = 0; + std::uint32_t n_pad_ = 0; + char pad_char_ = '\0'; }; /*pad_impl*/ inline pad_impl - pad(uint32_t n) { return pad_impl(n); } + pad(std::uint32_t n, char pad_char = ' ') { return pad_impl(n, pad_char); } inline std::ostream & - operator<<(std::ostream &s, - pad_impl const &pad) + operator<<(std::ostream & s, + pad_impl const & pad) { - for(uint32_t i=0; i class state_impl; +//# define XO_SSETUP0() xo::scope_setup(__FUNCTION__) +# define XO_SSETUP0() xo::scope_setup(__PRETTY_FUNCTION__) + /* throw exception if condition not met*/ # define XO_EXPECT(f,msg) if(!(f)) { throw std::runtime_error(msg); } /* establish scope using current function name */ -# define XO_SCOPE(name) xo::scope name(__FUNCTION__) +# define XO_SCOPE(name) xo::scope name(xo::scope_setup(__FUNCTION__)) /* like XO_SCOPE(name), but also set enabled flag */ -# define XO_SCOPE2(name, debug_flag) xo::scope name(__FUNCTION__, debug_flag) -# define XO_SCOPE_DISABLED(name) xo::scope name(__FUNCTION__, false) +# define XO_SCOPE2(name, debug_flag) xo::scope name(xo::scope_setup(__FUNCTION__, debug_flag)) +# define XO_SCOPE_DISABLED(name) xo::scope name(xo::scope_setup(__FUNCTION__, false)) # define XO_STUB() { XO_SCOPE(logr); logr.log("STUB"); } + /* convenience class for basic_scope<..> construction (see below). + * use to disambiguate setup from other arguments + */ + struct scope_setup { + scope_setup(std::string_view name1, std::string_view name2, bool enabled_flag) + : name1_{name1}, name2_{name2}, enabled_flag_{enabled_flag} {} + scope_setup(std::string_view name1, bool enabled_flag) + : scope_setup(name1, "", enabled_flag) {} + scope_setup(std::string_view name1) + : scope_setup(name1, true /*enabled_flag*/) {} + + std::string_view name1_ = "<.name1>"; + std::string_view name2_ = "<.name2>"; + bool enabled_flag_ = false; + }; /*scope_setup*/ + /* nesting logger * * Use: @@ -64,9 +83,9 @@ namespace xo { using state_impl_type = state_impl; public: - basic_scope(std::string_view name1); - basic_scope(std::string_view name1, bool enabled_flag); - basic_scope(std::string_view name1, std::string_view name2, bool enabled_flag); + //basic_scope(std::string_view name1, bool enabled_flag); + template + basic_scope(scope_setup setup, Tn&&... rest); ~basic_scope(); bool enabled() const { return !finalized_; } @@ -76,6 +95,8 @@ namespace xo { /* report current nesting level */ std::uint32_t nesting_level() const; + void set_dest_sbuf(std::streambuf * x) { this->dest_sbuf_ = x; } + template bool log(Tn&&... rest) { if(this->finalized_) { @@ -86,7 +107,7 @@ namespace xo { /* log to per-thread stream to prevent data races */ tosn(logstate2stream(logstate), rest...); - this->flush2clog(logstate); + this->flush2sbuf(logstate); } return true; @@ -112,12 +133,14 @@ namespace xo { static std::ostream & logstate2stream(state_impl_type * logstate); /* write collected output to std::clog, or chosen streambuf */ - void flush2clog(state_impl_type * logstate, std::streambuf * p_sbuf = std::clog.rdbuf()); + void flush2sbuf(state_impl_type * logstate); private: /* keep logging state separately for each thread */ static thread_local std::unique_ptr s_threadlocal_state; + /* send indented output to this streambuf (e.g. std::clog.rdbuf()) */ + std::streambuf * dest_sbuf_ = std::clog.rdbuf(); /* name of this scope (part 1) */ std::string_view name1_ = ""; /* name of this scope (part 2) */ @@ -127,17 +150,20 @@ namespace xo { }; /*basic_scope*/ template - basic_scope::basic_scope(std::string_view fn1, - std::string_view fn2, - bool enabled_flag) - : name1_(fn1), - name2_(fn2), - finalized_(!enabled_flag) + template + basic_scope::basic_scope(scope_setup setup, Tn&&... args) + + : name1_{std::move(setup.name1_)}, + name2_{std::move(setup.name2_)}, + finalized_{!setup.enabled_flag_} { - if(enabled_flag) { + if(setup.enabled_flag_) { state_impl_type * logstate = basic_scope::require_thread_local_state(); logstate->preamble(this->name1_, this->name2_); + + tosn(logstate2stream(logstate), " ", args...); + logstate->flush2sbuf(std::clog.rdbuf()); ///* next call to scope::log() can reset to beginning of buffer space */ @@ -147,16 +173,6 @@ namespace xo { } } /*ctor*/ - template - basic_scope::basic_scope(std::string_view fn1, bool enabled_flag) - : basic_scope(fn1, "", enabled_flag) - {} - - template - basic_scope::basic_scope(std::string_view fn) - : basic_scope(fn, true /*enabled_flag*/) - {} - template basic_scope::~basic_scope() { if(!this->finalized_) @@ -205,11 +221,10 @@ namespace xo { template void - basic_scope::flush2clog(state_impl_type * logstate, - std::streambuf * p_sbuf) + basic_scope::flush2sbuf(state_impl_type * logstate) { - logstate->flush2sbuf(p_sbuf); - } /*flush2clog*/ + logstate->flush2sbuf(this->dest_sbuf_); + } /*flush2sbuf*/ template void diff --git a/include/nestlog/tostr.hpp b/include/nestlog/tostr.hpp index b2bc927b..e395cfe3 100644 --- a/include/nestlog/tostr.hpp +++ b/include/nestlog/tostr.hpp @@ -59,6 +59,12 @@ namespace xo { * desired ctor */ + /* no-op terminal case */ + template + Stream & tos(Stream & s) { + return s; + } + // Use: // tos(s,a,b,c) // is the same as