Reputation: 677
I am trying to initialize a static object without success. The purpose is to automatically register a factory class in a repository (which is a singleton).
I've already had a look at: How to force a static member to be initialized?
One of the comments says that (there is also an example that I've followed):
I read it up in the C++ standard (14.7.1): Unless a member of a class template or a member template has been explicitly instantiated or explicitly specialized, the specialization of the member is implicitly instantiated when the specialization is referenced in a context that requires the member definition to exist; in particular, the initialization (and any associated side-effects) of a static data member does not occur unless the static data member is itself used in a way that requires the definition of the static data member to exist.
So I'm trying to do something similar but I haven't manage to force the object initialization. Here is the code. I don't know what I'm missing. This is the template I'm using.
namespace my_lib
{
template <typename T>
struct FactoryHelper
{
FactoryHelper ();
static FactoryHelper<T> _helper;
};
}
And this is the macro that the user of the library would use to define the factory class and, at the same time, register an object in the repository:
#define CREATE_FACTORY(ClassName)\
namespace my_lib\
{\
class ClassName##Factory;\
template<> FactoryHelper<ClassName##Factory>::FactoryHelper () { std::cout << "object initialized!" << std::endl; }\
template<> FactoryHelper<ClassName##Factory> FactoryHelper<ClassName##Factory>::_helper;\
struct ClassName##Factory : public FactoryBase<ClassName> {\
...\
};\
}
The previous code is defined in a header file (Factory.h).
In a .cpp file (Example.cpp), I have:
CREATE_FACTORY(UnitTestExample)
...
When I execute the program, I cannot see the message that the constructor prints when it is invoked. Any help is more than welcome.
Thanks in advance.
Upvotes: 8
Views: 5620
Reputation: 64308
This is a tricky area of C++. What you've done is to try to define the static member here:
template<> FactoryHelper<ClassName##Factory> FactoryHelper<ClassName##Factory>::_helper;\
but this is actually a declaration and not a definition. For C++ to treat it as a definition you have to pass something to the constructor. Typically, this is the value you want to initialize it to:
template<> FactoryHelper<ClassName##Factory> FactoryHelper<ClassName##Factory>::_helper = FactoryHelper<ClassName##Factory>();\
But in your case, you want this to be a singleton, so you probably don't want it to be copyable. In that case, you need some dummy parameter:
template<> FactoryHelper<ClassName##Factory> FactoryHelper<ClassName##Factory>::_helper(0);\
and you have to modify your constructor appropriately:
template<> FactoryHelper<ClassName##Factory>::FactoryHelper (int) { std::cout << "object initialized!" << std::endl; }\
Here is the complete working example:
#include <iostream>
namespace my_lib
{
template<typename> struct FactoryBase { };
template <typename T>
struct FactoryHelper
{
FactoryHelper (int);
static FactoryHelper<T> _helper;
};
}
#define CREATE_FACTORY(ClassName)\
namespace my_lib\
{\
class ClassName##Factory;\
template<> FactoryHelper<ClassName##Factory>::FactoryHelper (int) { std::cout << "object initialized!" << std::endl; }\
template<> FactoryHelper<ClassName##Factory> FactoryHelper<ClassName##Factory>::_helper(0);\
struct ClassName##Factory : public FactoryBase<ClassName> {\
};\
}
struct UnitTestExample {
};
CREATE_FACTORY(UnitTestExample);
int main(int argc,char **argv)
{
return 0;
}
That said, using some of the suggestions in the other answers may be a better design decision.
More information on the explicit specialization declaration vs. definition can be found here: static member initialization for specialized template class
Upvotes: 6
Reputation: 677
Well, first of all thanks a lot for both the suggestions and the explanations. I added the solutions you gave me to the code and didn't work. Then I tried your solutions as stand-alone programs and worked.
The difference is that the classes I'm implementing are compiled and then linked to the executable as a static libraries. If I compile the code all together (without using static libraries) then it works.
I found the response here: Static initialization and destruction of a static library's globals not happening with g++
The .o files are not linked unless they are referenced from the main application. I have used the ld
option -Wl,--whole-archive
and now it works.
-Wl,--whole-archive -lmy_static_library ... -Wl,--no-whole-archive
Related to the second question, I still don't understand why I have to specify a dummy parameter in the constructor.
template<> FactoryHelper<ClassName##Factory> FactoryHelper<ClassName##Factory>::_helper(0);\
Rather than doing this:
emplate<> FactoryHelper<ClassName##Factory> FactoryHelper<ClassName##Factory>::_helper = FactoryHelper<ClassName##Factory>();\
Thanks!
Upvotes: 1
Reputation: 20730
Instead of a static member (you have to create somewhere), consider the possibility of a static variable into a static function, like
namespace my_lib
{
template <typename T>
struct FactoryHelper
{
FactoryHelper () { ... };
static FactoryHelper<T>& helper()
{ static FactoryHelper<T> h; return h; }
};
}
Not the same as you are asking, but no need of out-of-band initializations.
Upvotes: 0
Reputation: 153792
What your macro does is to declare a specializations of some members of a class. This won't create any object and probably not what you really want anyway. What you'd need is a definition of FactoryHelper<SomeClass>::_helper
somewhere. A definition of the static member would look something like this:
FactoryHelper<foo> FactoryHelper<foo>::_helper;
That said, I don't think the is the the way to go at all: all you really need is to instantiate something which registers a factory function and this can be done much simpler and, especially, without macros.
Here is how I would do this:
template <typename T>
struct factory_helper
{
std::auto_ptr<base> create_fuction() { return std::auto_ptr<base>(new T()); }
factory_helper(std::string const& name) {
factory.register_class(name, create_function);
}
};
This assumes that you want to create objects derived from type base
and that your factory uses a mapping to function object returning std::auto_ptr<base>
as constructor functions and that it has a register_class()
function which takes the name and the constructor function as parameters. Neither of these assumptions is inherent to the apprach, though: this is just to fill in some of the blanks you didn't mention. You would register a factory function for a class foo
something like this:
static factor_helper<foo> foo_helper("foo");
Upvotes: 1