Подтвердить что ты не робот

С++ автоматическая factory регистрация производных типов

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

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

Мои цели:

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

Что у меня:

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;
};
  • templated abstract factory, с std::string в качестве ключевого типа
  • abstract factory содержит все элементы и методы static
  • имя класса восстанавливается автоматически с помощью typeid (при регистрации не требуется текстовое имя)
  • путем вызова: abstract_factory<Base>::register_class<MyDerived>();

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

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

Может использоваться любой совет, большой или малый, thanx.

4b9b3361

Ответ 1

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

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 файле достаточно, чтобы получить тип, зарегистрированный при запуске во время статической инициализации. Это работает dandy, за исключением случаев, когда регистрация типа является частью статической библиотеки, и в этом случае она не будет включена в ваш двоичный файл. Единственные решения, которые компилируют регистрацию как часть библиотеки, которую я видел, - это иметь один огромный файл, который делает регистрацию явно как часть какой-то инициализации. Вместо этого то, что я делаю сейчас, это иметь клиентскую папку с моей библиотекой, которую пользователь включает в себя как часть бинарной сборки.

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

Ответ 2

--- 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 стандарт С++ гласит:

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