Фабричный образец, выделяющий память во время компиляции, и как напечатать информацию времени компиляции

Я использую фабричный образец. Это в основном позволяет классам быть зарегистрированными во время компиляции и сохраненными в карте. Затем экземпляр можно вернуть с помощью BaseFactory :: createInstance ()

Я не уверен, как карта содержит имена классов во время компиляции !! Как может быть выделена память во время компиляции, которая действительна во время выполнения?

Весь класс в этом случае является производным от родительского класса Bump_BaseObject

//C++ STL used for adding Reflection
#include <string>
#include <map>


class Bump_BaseObject;

/**
 * Derived Base objects creation factory
 */
template<typename T>
Bump_BaseObject* createT(void)
{
#pragma message("createT instantiated")
    return new T();
}

struct BaseFactory {
    typedef std::map<std::string, Bump_BaseObject*(*)()> map_type;

    //return an instance of the class type 's'
    static Bump_BaseObject* createInstance(const std::string& s) {
        map_type::iterator it = getMap()->find(s);
        if(it == getMap()->end())
            return 0;

        //this is where we instatiate and allocate memory for the object(it must NOT have any arguments)
        //we could write a variant that accepts args, but there is no need.
        return it->second();
    }

    //check if 's' is present in the map of registered types
    static bool checkIfRegisteredType(const std::string& s) {
        map_type::iterator it = getMap()->find(s);
        if(it == getMap()->end())
            return false;

        return true;
    }

protected:
    static map_type* getMap() {
        // never delete'ed. (exist until program termination)
        // because we can't guarantee correct destruction order
        if(!objectMap) { objectMap = new map_type; }
        return objectMap;
    }

private:
    static map_type * objectMap;
};

#define VALUE_TO_STRING(x) #x

template<typename T>
struct DerivedRegister : BaseFactory {
    DerivedRegister(const std::string& s) {


#pragma message("Type registered")
        getMap()->insert(std::pair<std::string, Bump_BaseObject*(*)()>(s, &createT<T>));
    }
};

Также есть ли способ напечатать имена классов по мере их регистрации?

 Kerrek SB20 мая 2012 г., 21:38
Ничего не происходит во время компиляции. Все интересные действия происходят вdynamic initialization phaseчто происходит во время выполнения непосредственно передmain() называется.
 Kerrek SB20 мая 2012 г., 21:39
Ваша статическая карта протекает. Более чистый способ написать это было бы:static map_type & getMap() { static map_type impl; return impl; }.

Ответы на вопрос(1)

Решение Вопроса

что ваш код полностью сбит с толку, смешивая директивы препроцессора со странными шаблонами наследования. Вместо того, чтобы пытаться это исправить, я хотел бы представить общую, саморегистрационную фабричную среду (которая будет распечатывать регистрации по мере их появления).

Обратите внимание, что вся глобальная инициализация происходит во времяdynamic initialization phaseто есть во время выполнения непосредственно передmain() называется.

Base.hpp:

#include <unordered_map>
#include <string>

class Base
{
public:
    typedef Base * (*base_creator_fn)();
    typedef std::unordered_map<std::string, base_creator_fn> registry_map;

    virtual ~Base() = default;

    static registry_map & registry();
    static Base * instantiate(std::string const & name);
};

struct Registrar
{
    Registrar(std::string name, Base::base_creator_fn func);
};

Base.cpp:

#include "Base.hpp"
#include <iostream>

registry_map & Base::registry()
{
    static registry_map impl;
    return impl;
}

Base * Base::instantiate(std::string const & name)
{
    auto it = Base::registry().find(name);
    return it == Base::registry().end() ? nullptr : (it->second)();
}

Registrar::Registrar(std::string name, Base::base_creator_fn func)
{
    Base::registry()[name] = func;
    std::cout << "Registering class '" << name << "'\n";
}
Usage Example

Example.hpp:

#include "Base.hpp"

class DerivedExample : public Base
{
    static Registrar registrar;
public:
    static Base * create() { return new DerivedExample; }
    // ...
};

Example.cpp:

#include "Example.hpp"

Registrar DerivedExample::registrar("DerivedExample", DerivedExample::create);

Main.cpp

#include "Example.hpp"

int main()
{
    Base * p = Base::instantiate("DerivedExample");
    Base * q = Base::instantiate("AnotherExample");
}

Суть в том, что каждый производный класс имеет статическийRegistrar член, который инициализируется (в неопределенном порядке) на этапе динамической инициализации вашей программы, и каждый конструктор которого выполняет фактическую вставку в карту реестра, а также распечатывает сообщение журнала.


(Если у вас нет современного компилятора C ++, вам придется использовать старый синтаксис в стиле C ++ 98 :)

virtual ~Base() { }   //  no "= default"

Base::registry_map::const_iterator it = Base::registry().find(name); // no "auto"
 21 мая 2012 г., 00:30
@safe_malloc: я должен добавить, что в C ++ 11 у меня определенно была бы функция создания, возвращающаяstd::unique_ptr<Base>!
 safe_malloc20 мая 2012 г., 23:13
Спасибо за четкие объяснения Керрека. Также пример :). Да, мне никогда не нравился код, который я давал, довольно сложно понять, что происходит. Я сейчас пробую ваш пример.
 safe_malloc21 мая 2012 г., 00:28
Отлично сработало с небольшими изменениями :)! .... упс, вы сделали изменения: P
 14 мар. 2013 г., 22:21
@KerrekSB: Это мне очень помогло! Я неделями боролся с такими вещами. Теперь я могу вставлять новые производные классы повсюду и создавать их экземпляры и настраивать их из файла, заполненного именами классов и данными конфигурации. Качественный товар!
 20 мая 2012 г., 23:50
@safe_malloc: я исправил несколько мелких ошибок. Самое большое изменение заключается в том, что ковариантные указатели на функции не являются конвертируемыми, поэтомуcreate() теперь просто возвращаетBase*.

Ваш ответ на вопрос