0

I want to build a printer class that co-manages the indentation level automatically by an Indentation level destructor.

However, this code uses a static indentation level, it works well for a single instance. What workaround I can make to make a unique indentation level across several instances?

#include <stdio.h>
#include <string>
class Logger
{
  std::string _tag;
protected:
public:
  class Indents
  {
  public:
     Indents () { indents++; };
    ~Indents () { indents--; };
  };
  static int indents;
  Logger (const std::string & tag):_tag (tag)
  {
  }
  void I (const std::string & info)
  {
    for (int i = 0; i < indents; i++) 
        printf ("\t"); 

    printf ("%s:\t", _tag.c_str());
    printf ("%s\n", info.c_str());
  }
};

int Logger::indents = 0;
Logger x ("Service X");
int main ()
{
  {
    auto a = Logger::Indents();
    x.I ("Hello");
  }
  x.I ("World");

  return 0;
}

Prints:

    Service X:  Hello  
Service X:  World
Hamza Hajeir
  • 119
  • 1
  • 8
  • If you want `indents` to be per-instance, then make it a non-static member variable. Instead of `Logger::Indents()`, you'd use something like `auto a = x.Indent();` where `x.Indent()` is a member function that manufactures an `Indents` instance referring to `x.indents` member. – Igor Tandetnik Sep 04 '22 at 16:14
  • Any logger that is going to track the indentation level is likely going to need a thread-local variable to track that. For any but a trivial application, that is – Joe Sep 04 '22 at 16:34
  • what is the expected output? – OznOg Sep 04 '22 at 16:40
  • @IgorTandetnik, thanks for your input, I observe that `Indent()` member function manufacture is local inside the function call, thus it would be destructed just before `x.Indents()` returns. So no indentation occurs. – Hamza Hajeir Sep 04 '22 at 16:41
  • @Joe, thanks for the input, it's true, I'm trying to have it automated within an instance of a class, but couldn't get rid of the static `indents`. By the first look to @apple's answer, it might solve it. – Hamza Hajeir Sep 04 '22 at 16:44
  • @OznOg, the code above just work for its limited purpose (single instance), I'd like to have multiple instances having different set of indentation levels. – Hamza Hajeir Sep 04 '22 at 16:46
  • @IgorTandetnik, I think I've misunderstood you, your comment is correct as apple's answer below. – Hamza Hajeir Sep 04 '22 at 16:55

2 Answers2

1

you capture the Logger instance to Indents. (and change Logger::indents to non-static)

class Logger{
   int indents = 0; // no more static

   public:
   struct Indents{
       Indents (Logger& logger):logger(logger) { logger.indents++; };
       Indents (const Indents&) = delete;
      ~Indents () { logger.indents--; };
      Logger& logger;
   };
};

then you can either directly use it

void foo(){
   Logger x;
   Logger::Indents indent{x};
}

or write a member function to return it

class Logger{
   // ...
   Indents indent(){return {*this};} // c++17
};

// use
void foo(){
   Logger x;
   auto indent = x.indent(); 
}
apple apple
  • 10,292
  • 2
  • 16
  • 36
  • 1
    Just as needed, Thanks. – Hamza Hajeir Sep 04 '22 at 16:52
  • @HamzaHajeir I update the code a little, please checkout. (the class method only guaranteed to work after `c++17`, or you need to write a copy constructor which may not make much sense / move constructor which you need to keep track of moved-from state) – apple apple Sep 04 '22 at 21:30
  • I see, The target version is C++11, where embedded compilers support. What should I change to the [code](https://gist.github.com/HamzaHajeir/ace648b030f38c95c3bc1d0360535057)? – Hamza Hajeir Sep 05 '22 at 11:04
  • 1
    @HamzaHajeir you write a copy constructor. answer updated. – apple apple Sep 05 '22 at 14:58
1

If you want to use member function to construct indent and you don't have c++17

you need to implement copy/move constructor for Indents.

class Logger{
   int indents = 0; // no more static

   public:
   struct Indents{
       Indents (Logger& logger):logger(logger) { logger.indents++; };
       Indents (const Indents& x):Indents(x.logger){};
      ~Indents () { logger.indents--; };
      Logger& logger;
   };
   Indents indent(){return {*this};}
};

use:

void foo(){
   Logger x;
   Logger::Indents indent{x}; // still valid
   auto indent = x.indent(); 
}
apple apple
  • 10,292
  • 2
  • 16
  • 36