c ++ автоматическая фабричная регистрация производных типов - PullRequest
16 голосов
/ 02 апреля 2012

Как и многие до меня, я пытаюсь, чтобы мои производные типы автоматически регистрировались на моей фабрике. Я прочитал много вопросов и попытался сосредоточиться на том, чего там не нашел.

У меня все отлично работает, кроме автоматической регистрации.

Мои цели:

  1. автоматически регистрирует любой производный класс моего базового класса Base
    1. только классы, которые я отмечаю как регистрируемые
    2. не только прямые подклассы Base
      • ex: База -> Устройство -> Камера -> Веб-камера
      • это будет сделано с использованием CRTP , как описано в , этот вопрос затруднен
  2. минимальные изменения в классах, которые я хочу зарегистрировать - доказательство манекенов
  3. предпочел бы использовать класс регистратора , чем макросы

Что у меня есть:

template <class T>
class abstract_factory
{
    public:
        template < typename Tsub > static void register_class();
        static T* create( const std::string& name );
    private:
        // allocator<T> is a helper class to create a pointer of correct type
        static std::map<std::string, boost::shared_ptr<allocator<T> > > s_map;
};
  • шаблонная абстрактная фабрика, с std :: string как тип ключа
  • абстрактная фабрика имеет все члены и методы статические
  • имя класса восстанавливается автоматически с помощью typeid (не нужно вводить текстовое имя при регистрации)
  • регистрация по телефону: abstract_factory<Base>::register_class<MyDerived>();

Что я пытался (или хотел бы, но не знаю, как правильно):

  • registrator<Derived> class: шаблонный класс, который статически создается в Derived.cpp и должен вызывать abstract_factory::register_class<Derived>() в своем конструкторе
    • никогда не вызывается или не создается
    • если я сделаю экземпляр Derived в main(), это сработает -> хотя и побеждает цель
  • объявляйте простую статическую переменную в каждом Derived.hpp и устанавливайте ее с помощью метода статической регистрации в Derived.cpp -> снова, никогда не вызывается.
  • сделать abstract_factory настоящим синглтоном вместо того, чтобы все было статичным?

Могу воспользоваться любым советом, большим или маленьким, спасибо.

Ответы [ 2 ]

9 голосов
/ 02 апреля 2012

Я использую синглтон с членом для регистрации, в основном:

template< typename KeyType, typename ProductCreatorType >
class Factory
{
    typedef boost::unordered_map< KeyType, ProductCreatorType > CreatorMap;
    ...
};

Используя Loki, я получаю что-то вроде этого:

 typedef Loki::SingletonHolder< Factory< StringHash, boost::function< boost::shared_ptr< SomeBase >( const SomeSource& ) > >, Loki::CreateStatic > SomeFactory;

Регистрация обычно выполняется с помощью макросатакие как:

#define REGISTER_SOME_FACTORY( type ) static bool BOOST_PP_CAT( type, __regged ) = SomeFactory::Instance().RegisterCreator( BOOST_PP_STRINGIZE( type ), boost::bind( &boost::make_shared< type >, _1 ) );

Эта установка имеет ряд преимуществ:

  • Работает, например, с boost :: shared_ptr <>.
  • Не требует обслуживанияОгромный файл для всех регистрационных нужд.
  • Очень гибок в работе с создателем, все идет очень хорошо.
  • Макрос охватывает наиболее распространенный вариант использования, оставляя дверь открытой для альтернатив.

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

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

4 голосов
/ 02 апреля 2012

--- registration.h ---

#include <iostream>
#include <typeinfo>
#include <set>
#include <string>
using namespace std;

template<class T>
struct registrar {
        struct proxy { inline proxy();};
        static proxy p;
};

template<class T> typename registrar<T>::proxy registrar<T>::p;



struct factory {
        template <typename T> static T* create() {
               registrar<T>::p;
               return new T();
        }
};

set<string> & types();

template<typename T>
registrar<T>::proxy::proxy() { types().insert(typeid(T).name());}

--- registration.cpp ---

#include "registration.h"
#include <boost/foreach.hpp>

set<string> & types() {static set<string> types; return types;} 

int main() {
    BOOST_FOREACH(const string & s, types()) { cout<<s<<"\n";}
        factory::create<int>();
    factory::create<double>();
    factory::create<bool>();
        return 0;
}

--- registration_ext.cpp ---

#include "registration.h"

class myclass {};

void phony() {
    factory::create<double>();
    factory::create<myclass>();
}

Затем компилируется с:

$ g++ registration.cpp registration_ext.cpp -o registration

При запуске:

$ ./registration 
7myclass
b
d
i

Итак, похоже, что классы зарегистрированы до вызова main.

Редактировать: я заметил, что это решение зависит от реализации.В соответствии с 3.6.2 стандарт C ++ гласит:

Определяется реализацией, выполняется ли динамическая инициализация нелокальной переменной со статической продолжительностью хранения перед первым оператором main.Если инициализация откладывается до некоторого момента времени после первого утверждения main, она должна произойти до первого использования odr (3.2) любой функции или переменной, определенной в той же единице перевода, что и для инициализируемой переменной.

...