hypheni
hypheni

Reputation: 816

c++ singleton class instance access all over application

I'm trying to write a log class with threadsafe practice in c++. Now the thing is, I want to make the call to each log line very simple. I can go with static class way like below:

//Calling this in some GUI initialization point of application
CLogger::InitLogger("LogFile.log");

//Calling below from any class just adding the header to the cpp
CLogger::Log("Some informational text goes here");

Now this doesn't follow the OOP so would like to go with a singleton class.

//Singleton log class
class CLogWrite
{
public:
  static CLogWrite* GetInstance();

private:
  static CLogWrite *pInstance;
  void Log();
};

CLogWrite* CLogWrite::GetInstance()
{
  if(pInstance != NULL)
  {
    pInstance = new CLogWrite;
  }
  return pInstance;
}

void CLogWrite::Log()
{
  //threadSafe lock

  //write 'logText' to file here

  //threadSafe unlock
}

Now the thing is, if I write above class and call CLogWriter::GetInstance() inside my GUI class init function like below:

//single logger instance for my app
CLogWriter *mLogInstance;
mLogInstance = CLogWriter::GetInstance()

I need to pass 'mLogInstance' variable to each and every class inside my project from where I want to write a log. But I don't wanna do that.

What would be best approach?

Upvotes: 1

Views: 1794

Answers (2)

Mateusz Grzejek
Mateusz Grzejek

Reputation: 12078

One of my favourite techniques in C++ is so-called CRTP, which can be used to implement singleton logic. Look at this code:

template <class Type>
class Singleton
{
private:
    static std::unique_ptr<Type>    _holder_ptr;
    static std::mutex               _mutex;

public:
    static Type& GetSingleton()
    {
        if(!_holder_ptr)
            _create_instance();

        return *(_holder_ptr.get());
    }

protected:
    static void _create_instance()
    {
        _mutex.lock();

        if(!_holder_ptr)
            _holder_ptr.reset(new Type());

        _mutex.unlock();
    }
};

Now, you can use this class to define "singletons":

class Log : public Singleton<Log>
{
    //content
};

and use it:

Log& global_log = Log::GetSingleton();
//do something

This is version for C++ 11. For older compilers, replace std::mutex with platform-dependent synchronizing primitive. On Windows, you may want to use CRITICAL_SECTION. Sample usage: here.

std::unique_ptr was also introduced in C++ 11, so you may either replace it with std::auto_ptr (deprecated in C++ 11), use smart pointer from Boost library or create your own solution (which is not hard, but reinventing the wheel is not a good idea).

Note, that this Singleton does not forbid to create additional instances of Log - and it should not! There are two common scenarios:

  • You allow users to create additional instances of Log, but at the same time demand, that at least one such object must exist. Then, simple deriving from Singleton is all what you need.
  • You need Log to be true "Singleton", that is: only one, shared instance can exist - rest of code must use this instance, so everything can work as expected. In this case, use = delete syntax to remove default constructor, copy constructor and assignment operator (or simply declare them as protected or private, depending on compiler support for = delete syntax).

Also, remember, that if you want to hold shared instance as pointer, you need to use some kind of smart pointers, as presented above. You cannot use plain pointers like in your original solution:

CLogWrite* CLogWrite::GetInstance()
{
    if(pInstance != NULL)
    {
        pInstance = new CLogWrite;
    }
    return pInstance;
}

Where and when pInstance would be destroyed? How?

Upvotes: 1

user4773641
user4773641

Reputation:

Try this:

class Singleton
{
public:
    static Singleton& getInstance()
    {
        static Singleton instance;
        return instance;
    }

    Singleton(Singleton const&) = delete;
    void operator=(Singleton const&) = delete;

private:
    Singleton() {};        
};

Upvotes: 2

Related Questions