Hi Glen,
It solves other issues, not just the multi-threading problem, which makes
it applicable even for c++11 compilers.
Performance wise it does not include multi-thread primitives which makes it
I would assume many times faster. Arguable it is even faster than the
pattern used in path_locale, because it does not check if the object
is initialized for every call.
It is not in GitHub yet, I will let you know when I publish it.
If I ignore some of the smarts the main concept is very simple:
The access to the global object can be raw, shared or other pointer, say
typedef boost::shared_ptr<GlobalLogger> GlobalLoggerSPtr;
extern GlobalLoggerSPtr gLogger;
then the initialization of the pointer is, say
GlobalLoggerSPtr gLogger = GlobalObjectManager::registerPointer(orderIndex,
gLogger, initFn, uninitFn);
registerPointer method does NOT call the initFn, but instead returns
something arbitrary (say null pointer), but as side effect stores the
address of the global object pointer in a container.
then you start your main (something that I hope I could find a way to
avoid) with constructing a GlobalObjectManager object.
int main(int argc, char **argv)
{
GlobalObjectManager gom(argc, argv);
...
}
The constructor of GlobalObjectManager initialize all the registered
objects pointers in the appropriate order. The destructor uninitializes in
reverse order. For the rest of the code it seems like the initialization
was part of the crt initialization - but we had control over the order. In
case of one global object depends on another all you need to do is to make
sure that their orderIndexes are correctly offseted.
My implementation have some smarts to help for defining the order
relatively easy, and offers a set of helper methods and default
initializers and uninitializes that allow to do the most of the most common
cases relatively simple - for example the code for my logger looks:
GlobalLoggerSPtr gLogger = GlobalObjectManager::sharedPointer(gLogger);
Now when I need access to the logger object all I need to do is:
gLogger->...
Note that the consumer library does not need to know how this pointer is
initilized (no dependencies to the GlobalObjectManager). Also, if my logger
is in a static library that I am linking, but I do not use it - the gLogger
pointer is not going to be initialized, this will result its initialization
to not be added to the GlobalObjectManager and as result all the code
required for the logger will not be added to the binary.
If I would like to replace the logger with a mock, all I need to do is -
static void initilizeGlobalLoggerMock()
{
gLogger = boost::make_shared<GlobalLoggerMock>();
}
GlobalObjectManager::genericRegister(orderIndex - 1,
&initilizeGlobalLoggerMock, NULL);
Note that I am subtracting one from the orderIndex - this will guarantee
that this initialization will be called before the original one. Now
because GlobalObjectManager::sharedPointer does not replace the pointer
unless it is NULL, when I run the code (in test environment) the same code
that uses the pointer will be execute against the mock object without any
change.
Please let me know what do you think?
Thanks,
George
On Sat, Jun 7, 2014 at 4:58 PM, Glen Fernandes
Hi George,
On Sat, Jun 7, 2014 at 3:28 PM, George Georgiev wrote:
I have code that deals with problems related to initialization of global objects (singletons). 1. Multi-threading - problems occurring in some singleton patterns based on the fact that local for a function or method static variables are not initialized in a thread safe manner
Did you mean specifically for C++03 / non-C++11 compilers? With C++11, initialization of function-local statics is guaranteed to be thread-safe.
For those compilers that do not implement that C++11 requirement (like VC12), how does your solution for thread-safe singleton initialization compare (performance-wise) to, say,
http://www.boost.org/doc/libs/1_55_0/doc/html/atomic/usage_examples.html#boo... or other alternatives?
In any case, I'm curious to see your solution. Is it on GitHub?
[ Side note: Instead of boost::shared_ptr<T>(new T(...)) always prefer boost::make_shared<T>(...). For better performance (one allocation instead of two) and exception-safety. Granted, this is a test snippet, but I also noticed it in Compil. :-) ]
Glen
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost