Reputation: 816
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
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:
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.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
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