Проблемы реализации фабричного класса

Я реализовал фабричный класс на основе следующей статьи, доступной здесь.

Однако у меня есть одна проблема, и я думаю, что она связана с оптимизацией компилятора.

У меня есть иерархия классов, где класс Node (в Node.h / Node.cpp) является базовым классом и, например, BuildingLot (в BuildingLot.h / BuildingLot.cpp) является подклассом.

В обоих исходных файлах, объявленных как статические, у меня есть класс Registrar.

Node.cpp:

static Registrar<Node> _registrar(“Node”);

BuildingLot.cpp:

static Registrar<BuildingLot> _registrar(“BuildingLot”);

Если я попытаюсь использовать Factory, он отлично подойдет для класса Node. Однако мне нужно сначала создать экземпляр BuildingLot, чтобы он был зарегистрирован на фабрике.

В модульном тесте у меня есть:

NodePtr node = Factory::Instance()->Create(“Node”);
NodePtr bl = Factory::Instance()->Create(“BuildingLot”);

bl всегда имеет значение nullptr. Конструктор Registrar никогда не выполняется, и поэтому класс Factory никогда не знает о классе BuildingLot. Если я сделаю это:

BuildingLot b;
NodePtr node = Factory::Instance()->Create(“Node”);
NodePtr bl = Factory::Instance()->Create(“BuildingLot”);

Тогда все работает. Вызывается Регистратор, и Фабрика создает BuildingLot.

Я думаю, поскольку BuildingLot не используется в первом примере, компилятор оптимизировал и даже не компилировал BuildingLot.cpp.

Это возможно? Как я могу это решить?

Изменить: класс Registrar, по сути, является классом шаблона. Я просто забыл вставить параметры шаблона.

Вот мой код настолько простой, насколько я мог его сделать. Надеюсь, я ничего не упустил. Если я раскомментирую первую строку в main (), все заработает.

NodeFactory.h:

class NodeFactory{
private:
    /// Map of factory functions
    std::map<std::string, std::function<std::shared_ptr<Node>(void)>> mFactoryFunctions;
public:
     /// Get Singleton
    static NodeFactory* Instance();

    /// Register Function.
    void Register(const std::string &name, std::function<std::shared_ptr<Node>(void)> factoryFunction);

    /// Factory Function.
    std::shared_ptr<Node> Create(const std::string &name);
};

NodeFactory.cpp:

/**
 * Get Singleton
 */
NodeFactory* NodeFactory::Instance(){
    static NodeFactory factory;
    return &factory;
}

void NodeFactory::Register(const std::string &name, std::function<std::shared_ptr<Node>(void)> factoryFunction){
    mFactoryFunctions[name] = factoryFunction;
}

std::shared_ptr<Node> NodeFactory::Create(const std::string &name){
    if(mFactoryFunctions.find(name) == mFactoryFunctions.end())
        return nullptr;

    std::shared_ptr<Node> n = mFactoryFunctions[name]();

    return n;
}

Registrar.h:

#define REGISTER_NODE_TYPE(NODE_TYPE) static NodeRegistrar<NODE_TYPE> _registrar(#NODE_TYPE);

template<class T>
class NodeRegistrar{
private:

public:

    NodeRegistrar(const std::string &name){
        NodeFactory::Instance()->Register(name,
                                          [](void) -> std::shared_ptr<T> { return std::make_shared<T>(); }
                                          );
    }
};

Node.h:

class Node{
private:        
    /// The node ID.
    NodeID mID;

public:
    /* ****************************
     * Construction & Destruction *
     * ***************************/
    Node();
    Node(const NodeID &nodeID);
    virtual ~Node();
};

Node.cpp:

REGISTER_NODE_TYPE(Node);

Node::Node(){
    mID = -1;
}
Node::Node(const NodeID &nodeID){
    mID = nodeID;
}

Node::~Node(){

}

BuildingLot.h:

class BuildingLot : public Node{
public:

    BuildingLot();
    BuildingLot(const NodeID &nodeID);
    virtual ~BuildingLot();

};

BuildingLot.cpp:

REGISTER_NODE_TYPE(BuildingLot);

BuildingLot::BuildingLot(){

}

BuildingLot::BuildingLot(const NodeID &nodeID):Node(nodeID){

}

BuildingLot::~BuildingLot(){

}

main.cpp:

int main(int argc, const char * argv[]){   

// BuildingLot bl; // if I uncomment this, then it works
std::shared_ptr<Node> node = NodeFactory::Instance()->Create("Node");
std::shared_ptr<Node> buildingLot = NodeFactory::Instance()->Create("BuildingLot");

if(node == nullptr)
    std::cout << "node is nullptr" << std::endl;
if(buildingLot == nullptr)
    std::cout << "buildingLot is nullptr" << std::endl;

return 0;

}


person zync    schedule 29.08.2014    source источник
comment
Какой у вас компилятор? Если я правильно помню, нельзя позволять оптимизировать конструкторы с побочными эффектами ...   -  person Quentin    schedule 29.08.2014
comment
В этой статье класс Registrar является шаблоном. В ваших фрагментах кода это не так. Так как же узнать, какой класс регистрировать?   -  person Sebastian    schedule 29.08.2014
comment
Возможный дубликат: stackoverflow.com/q/1300836 Проблема заключается в фиаско статического порядка инициализации.   -  person dyp    schedule 29.08.2014
comment
Предоставьте stackoverflow.com/help/mcve   -  person Sebastian    schedule 29.08.2014
comment
@dyp: После прочтения статьи я так не думаю - он использует функцию для возврата карты регистратора. Таким образом, карта должна существовать на момент обращения к ней.   -  person Sebastian    schedule 29.08.2014
comment
@Sebastian Проблема в обоих случаях заключается в регистрации во время загрузки посредством статической инициализации. Детали того, как работает сам процесс регистрации, не имеют значения; только то, как это (должно быть) начато.   -  person dyp    schedule 29.08.2014
comment
@dyp: Но как только main () запускается, это должно быть сделано, не так ли? Вот почему я прошу mcve ... Является ли bl глобальная переменная, созданная в main (), или функция, вызываемая main ()?   -  person Sebastian    schedule 29.08.2014
comment
@Sebastian Это описано в ссылке, которую OP предоставляет в первой строке, и (к сожалению) довольно распространенный метод. [basic.start.init] / 4 Это определяется реализацией, выполняется ли динамическая инициализация нелокальной переменной со статической продолжительностью хранения перед первым оператором main. Если инициализация откладывается на некоторый момент времени после первого оператора main, она должна произойти до первого odr-использования любой функции или переменной, определенной в той же единице трансляции, что и инициализируемая переменная.   -  person dyp    schedule 29.08.2014
comment
Остерегайтесь этого примера. Базовый класс не имеет виртуального деструктора. Также MyFactory::CreateInstance() имеет очень странную реализацию.   -  person BЈовић    schedule 09.09.2014
comment
Статические переменные внутри методов инициализируются при первом вызове метода и остаются там навсегда. Таким образом, экземпляр создается автоматически при первой необходимости, и тот же экземпляр всегда доступен в следующих вызовах. Уже добавлен виртуальный деструктор. Спасибо, что указали на это :)   -  person zync    schedule 09.09.2014


Ответы (1)


В конце концов, я решил вручную зарегистрировать создателей лямбда в фабричном классе. Это очень просто, если макрос создается и используется, а количество строк кода точно такое же.

person zync    schedule 09.09.2014