trying to force static object initialization

冷暖自知 提交于 2019-11-29 10:40:31
Vaughn Cato

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

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");

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.

user1192525

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!

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!