Registration error in C ++

I have implemented a very simple (buggy) logger class. It looks like this:

#pragma once

#include <string>

extern const char* LOG_DEFAULT_TEXT =  "<LOG>\n\n";

class Log
{
public:
    Log() : _logs(0), _log(LOG_DEFAULT_TEXT) {};

    void add(const char *str, bool nl=true);
    void clear();

    const char* get() const { return _log.c_str(); }
    int getNumLogs() const { return _logs; }


private:
    std::string _log;
    int _logs;
};

      

Now my question is, let's say I have this Main class that contains all the other objects that my program can contain as well as this log class. Obviously, I would like these "other objects" in my main class to be able to use the log object in Main, so a simple solution would be to pass pointers to each class constructor so that it can use the log object.

I'm talking about something like this:

//Main.h
...

#include "Log.h"

class Main() {
public:
  ...

private:
  Log _log;
  ImportantObject1(&_log); //pass a pointer to _log
  ImportantObject2(&_log); //..same
};

      

This solution seems too clumsy, so I ask if there are different approaches to what I want to accomplish, this is the error log.

+3


source to share


3 answers


In one of the rare cases, a singleton makes sense:

class Log
{
public:

    void add(const char *str, bool nl=true);
    void clear();

    const char* get() const { return _log.c_str(); }
    int getNumLogs() const { return _logs; }

    static Log& instance() {
         static Log theInstance;
         return theInstance;
    }
private:
    Log() : _logs(0), _log(LOG_DEFAULT_TEXT) {};

    std::string _log;
    int _logs;
};

      



This way you can use it elsewhere simply by referring to

 Log::instance().add("Blah Blah",true);

      

+5


source


Another approach to meet the requirement ...

Use functions as appropriate namespace

instead of singleton.

Log.h:

namespace Log
{
   void add(const char *str, bool nl=true);
   void clear();

   const char* get();
   int getNumLogs();
}

      

Log.cpp:

namespace detail
{
   // This is almost same as the class from OP post.
   struct LogImpl
   {
      LogImpl(std::string const& log) : _logs(0), _log(log) {};

      void add(const char *str, bool nl=true);
      void clear();

      const char* get() const { return _log.c_str(); }
      int getNumLogs() const { return _logs; }


      std::string _log;
      int _logs;
   };
}

namespace Log
{
   // This mimics the singleton implementation...
   // Create only one instance of LogImpl.

   static detail::LogImpl impl("<LOG>\n\n");

   void add(const char *str, bool nl)
   {
      impl.add(str, nl);
   }
   void clear()
   {
      impl.clear();
   }

   const char* get()
   {
      return impl.get();
   }
   int getNumLogs()
   {
      return impl.getNumLogs();
   }
}

namespace Log
{

   void add(const char *str, bool nl=true);
   void clear();

   const char* get();
   int getNumLogs();
}

      

Using functions in namespace

vs using singleton



It takes a bit more code to support the use of functions in the namespace.

However, it simplifies the calling code.

The calling code can use

Log::add(...);

      

instead

Log::instance()->add(...);

      

+2


source


Another common pattern inherited from C would be the global object. Globals tend to be frowned upon because they are a symptom of poor design. But the logical log makes sense to be global.

This means that the header for your class should contain:

extern Log log;

      

and cpp

#include <log.h>
...
Log log;    // unique definition for the global Log

      

Then, in any module using the log:

#include <log.h>
...
log.add(...);

      

Here's a caveat that there will be static initialization Log log

. And C ++ is not always very good with static initialization order: ... Otherwise, variable initialization is undefined sequenced relative to initialization of a variable defined in a different translation unit. The only sure way is to ensure that:

  • The log construct is independent of any other statically initialized variable needing a constructor phase in another translation unit - it includes std::string

    , but hopefully notconst char *

  • the global variable is used once before starting multithreading

When in doubt, use the singleton pattern suggested by πάντα ῥεῖ

+1


source







All Articles