Sharing a singleton in an executable and a runtime DLL
-
Hi all, I have recently run into a problem I was not expecting that stands to jeopardize quite a bit of work. Within my application I have a singleton system logging object that reads from and writes to an XML file. This application also loads several DLLs which in turn also need to be able to contribute to the system log. The problem is when the DLLs try to get a pointer to the singleton they are actually creating a new instance of the object rather than referencing the one that has already been created. This creates an obvious problem with concurrent file I/O. My question is, is there any way around this? The design of this object is such that the only interface anyone has to this object is through global macros(i.e. LOG_ERROR("error happened")), which handle accessing the singleton, meaning simply passing a pointer into the DLL at creation time is not feasible. Any help is greatly appreciated. Thanks, Dustin Henry
-
Hi all, I have recently run into a problem I was not expecting that stands to jeopardize quite a bit of work. Within my application I have a singleton system logging object that reads from and writes to an XML file. This application also loads several DLLs which in turn also need to be able to contribute to the system log. The problem is when the DLLs try to get a pointer to the singleton they are actually creating a new instance of the object rather than referencing the one that has already been created. This creates an obvious problem with concurrent file I/O. My question is, is there any way around this? The design of this object is such that the only interface anyone has to this object is through global macros(i.e. LOG_ERROR("error happened")), which handle accessing the singleton, meaning simply passing a pointer into the DLL at creation time is not feasible. Any help is greatly appreciated. Thanks, Dustin Henry
Dustin Henry wrote:
My question is, is there any way around this?
There must be - I've done it in apps (strangely enough, it was a logging class in my app as well!). I also had the same issue you had (getting multiple instances) - I seem to remember the following making a difference:
- Making sure that all code implementing the singleton is implemented in .cpp files, not headers
- Using the DLL version of the C run-time
I also implemented the thing as a monostate object - all methods were static, something like:
class Logger
{
public:
static void Log(std::string const& msg);
private:
Logger();
static Logger* instance_;
std::ostream& output;
};You could then access the logger using
Logger::Log("Hello World");
Java, Basic, who cares - it's all a bunch of tree-hugging hippy cr*p
-
Dustin Henry wrote:
My question is, is there any way around this?
There must be - I've done it in apps (strangely enough, it was a logging class in my app as well!). I also had the same issue you had (getting multiple instances) - I seem to remember the following making a difference:
- Making sure that all code implementing the singleton is implemented in .cpp files, not headers
- Using the DLL version of the C run-time
I also implemented the thing as a monostate object - all methods were static, something like:
class Logger
{
public:
static void Log(std::string const& msg);
private:
Logger();
static Logger* instance_;
std::ostream& output;
};You could then access the logger using
Logger::Log("Hello World");
Java, Basic, who cares - it's all a bunch of tree-hugging hippy cr*p
Hey Stuart, thanks for your help. I still couldn't get it to work so I just ended up passing in a pointer to the already created object and setting it via a macro. Not as clean as I would like but functional. Dustin
-
Hey Stuart, thanks for your help. I still couldn't get it to work so I just ended up passing in a pointer to the already created object and setting it via a macro. Not as clean as I would like but functional. Dustin
I went back and had a look at the reporting implementation I wrote - I think the main difference is that I put my logger in a DLL, not the EXE. That way it's a lot easier for everything to link against it and be able to call it at run-time. However, the implementation boiled down to what I described - there's a class with a single static function allowing reporting, and purely static (i.e. single instance for the whole class) data members: Header file:
class Reporter
{
public:
Reporter();/// Output a message at a specified level.
static void Report(TMessageLevel level, const std::string& message);/// Determine if a message level is enabled and has attached output sink(s).
static bool Reporting(TMessageLevel level);private:
static TMessageLevel level_; // Store message levels to be output - a bitmask
static implementation-defined message_; // Output sink mechanism
enum { ReportLevelCount = sizeof(Reporter::level_) * 8 };
};C++ file:
Reporter::Reporter()
{
}void Reporter::Report(TMessageLevel level, const std::string& message)
{
if (Reporting(level)) {
message_(message);
}
}// Reporter::Reporting
//
// Is the specified reporting level active? OnError is treated separately to other
// levels, as there's a separate reporting channel for errors
bool Reporter::Reporting(TMessageLevel level)
{
int levelMask = (level < ReportLevelCount)?(1<message_
is effectively a function object that allows me to hook up the output mechanism at runtime.Java, Basic, who cares - it's all a bunch of tree-hugging hippy cr*p