Question

i have a factory class that i've used for quite some time, which i have tried to do a little template magic to, and it simply will not work. I don't think you'll need to see that class, as it's quite large, but if you really think it's related then i will edit it in. Anyway, i have a setup like this which i've done forever.

class Base :
    public Factory<Base>
{
};

class Derived :
    public Base
{
    static Factory<Base>::DerivedRegister<Derived> m_Reg;
};

Factory<Base>::DerivedRegister<Derived> Derived::m_Reg;

the factory gives it the static method createInstance(const std::string& name) which will return an instance of the registered object. The DerivedRegister object derives from the factory object and when declared statically, must be constructed during static initialization. This means i have access to the factories static internals within it's constructor, and so i utilize it's constructor to have all registered object available to the factory during static initialization. This works and has for awhile, but then i tried something like this so that derived classes could avoid needing to explicitly make that static derivedregister object:

class Base :
    public Factory<Base>
{
};

template<class TDerived>
class BaseReg :
    public Base
{
    static Factory<Base>::DerivedRegister<TDerived> m_Reg;
};

template<class TDerived>
Factory<Base>::DerivedRegister<TDerived> BaseReg<TDerived>::m_Reg;

class Derived :
    public BaseReg<Derived>
{
};

When i run the program and try to create an instance of the Derived class, it doesn't exist in the factory, and after a little doodling, i find that the static DerivedRegister is never being constructed in derived, as in the constructor is never called. I find this odd. How could it not be forced to statically initialize if the Derived class isn't a template? When i make a dummy static object as seen below, and use a method from m_Reg in it's static construction, suddenly the DerivedRegister constructs correctly and the factory can instantiate it, no sweat.

class Base :
    public Factory<Base>
{
};

template<class TDerived>
class BaseReg :
    public Base
{
protected:
    static Factory<Base>::DerivedRegister<TDerived> m_Reg;
};

template<class TDerived>
Factory<Base>::DerivedRegister<TDerived> BaseReg<TDerived>::m_Reg;

class Derived :
    public BaseReg<Derived>
{
    class RandomClass
    {
    public:
         RandomClass(std::string meh) {}
    };

private:
    static RandomClass m_Obj;
};

Derived::RandomClass Derived::m_Obj(m_Reg.func());

So what am i not getting about this template class's static member initialization here? doesn't it have to statically initialize the object like any other non-template object?

EDIT: mkay, to give a little insight, i'll post the Factory object. Just beware the WALLS of text. ignore the extra includes, and the superfluous GetNames, and func functions, they are just voodoo code.

#ifndef FACTORY_H
#define FACTORY_H

// library tools
#include <map>
#include <string>
#include <typeinfo>
#include <cstdlib>
#include <vector>
#include <iostream>
#include <cxxabi.h>

const std::string demangle(const char* name);

template<class base>
class Factory
{
protected:
    template<class T>
    static base * createT() { return new T;}

    typedef std::map<std::string, base*(*)()> map_type;

    static map_type& GetMap()
    {
        static map_type map;
        return map;
    }

public:
    virtual ~Factory(){}

    static base * createInstance(const std::string & s)
    {
        if(!GetMap().count(s))
            return nullptr;
        typename map_type::iterator it = GetMap().find(s);
        return it->second();
    }

    template <class TDerived>
    struct DerivedRegister :
        public Factory<base>
    {
        DerivedRegister()
        {
            std::string name = demangle(typeid(TDerived).name());
            GetMap().insert(std::pair<std::string, base*(*)()>(name, &createT<TDerived>));
        }

        DerivedRegister(const std::string& name)
        {
            GetMap().insert(std::pair<std::string, base*(*)()>(name, &createT<TDerived>));
        }

        std::string func() {return "meh";}
    };

    static void GetNames(std::vector<std::string>& names)
    {
        names.clear();

        for(auto it = GetMap().begin(); it != GetMap().end(); ++it)
            names.push_back(it->first);

    }
};


#endif
Was it helpful?

Solution

What you might be running up against here is the woes of unpredictable static initialisation order, assuming that you are constructing the factory itself statically, or that it is constructed as part of a chain of other static constructions.

You can get around this by wrapping and using the static in its own function:

template<class TDerived>
class BaseReg :
    public Base
{
protected:
    static Factory<Base>::DerivedRegister<TDerived> & Reg()
    {
        static Factory<Base>::DerivedRegister<TDerived> m_Reg;
        return m_Reg;
    }
};

Now, the only way to access m_Reg is to call Reg(), after which time you can be certain that m_Reg is constructed. That is, it's constructed at whatever time it is first used.

I have successfully fixed some pretty hairy problems with the above approach, and now I use it as a matter of course, to avoid having to debug strange crashes. I almost never have a need for a static member variable anymore.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top