nestlog: control indent level + args with scope entry

This commit is contained in:
Roland Conybeare 2023-09-12 12:24:33 -04:00
commit 5ad143d5b6
6 changed files with 98 additions and 50 deletions

View file

@ -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);
}

View file

@ -2,7 +2,9 @@
#pragma once
#include "log_config.hpp"
#include "log_streambuf.hpp"
#include "pad.hpp"
#include <ostream>
#include <memory> // 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_; i<n; ++i) {
this->ss_ << pad_char;
}
#endif
} /*indent*/
template <typename CharT, typename Traits>
void
state_impl<CharT, Traits>::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 <typename CharT, typename Traits>
@ -123,7 +133,7 @@ namespace xo {
state_impl<CharT, Traits>::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 <typename CharT, typename Traits>
@ -131,7 +141,7 @@ namespace xo {
state_impl<CharT, Traits>::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 <typename CharT, typename Traits>

View file

@ -5,7 +5,8 @@
#include <iostream>
#include <vector>
#include <cstring> // e.g. for std::memcpy()
#include <cassert> // e.g. for std::memcpy()
#include <cstdint>
#include <cassert>
namespace xo {
/* recycling buffer for logging.

View file

@ -3,36 +3,47 @@
#pragma once
#include <iostream>
#include <cstdint>
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<pad.n_pad(); ++i)
s << " ";
for(std::uint32_t i=0; i<pad.n_pad(); ++i)
s << pad.pad_char();
return s;
} /*operator<<*/

View file

@ -15,15 +15,34 @@ namespace xo {
template <typename ChartT, typename Traits>
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<CharT, Traits>;
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 <typename... Tn>
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<typename... Tn>
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<state_impl_type> 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_ = "<name1>";
/* name of this scope (part 2) */
@ -127,17 +150,20 @@ namespace xo {
}; /*basic_scope*/
template <typename CharT, typename Traits>
basic_scope<CharT, Traits>::basic_scope(std::string_view fn1,
std::string_view fn2,
bool enabled_flag)
: name1_(fn1),
name2_(fn2),
finalized_(!enabled_flag)
template <typename... Tn>
basic_scope<CharT, Traits>::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 <typename CharT, typename Traits>
basic_scope<CharT, Traits>::basic_scope(std::string_view fn1, bool enabled_flag)
: basic_scope(fn1, "", enabled_flag)
{}
template <typename CharT, typename Traits>
basic_scope<CharT, Traits>::basic_scope(std::string_view fn)
: basic_scope(fn, true /*enabled_flag*/)
{}
template <typename CharT, typename Traits>
basic_scope<CharT, Traits>::~basic_scope() {
if(!this->finalized_)
@ -205,11 +221,10 @@ namespace xo {
template <typename CharT, typename Traits>
void
basic_scope<CharT, Traits>::flush2clog(state_impl_type * logstate,
std::streambuf * p_sbuf)
basic_scope<CharT, Traits>::flush2sbuf(state_impl_type * logstate)
{
logstate->flush2sbuf(p_sbuf);
} /*flush2clog*/
logstate->flush2sbuf(this->dest_sbuf_);
} /*flush2sbuf*/
template <typename CharT, typename Traits>
void

View file

@ -59,6 +59,12 @@ namespace xo {
* desired ctor
*/
/* no-op terminal case */
template<class Stream>
Stream & tos(Stream & s) {
return s;
}
// Use:
// tos(s,a,b,c)
// is the same as