Я пытаюсь зарегистрировать группу классов на фабрике во время загрузки. Моя стратегия состоит в том, чтобы использовать статическую инициализацию, чтобы убедиться, что перед началом main () фабрика готова к работе. Эта стратегия, кажется, работает, когда я связываю свою библиотеку динамически, но не когда я связываю статически; когда я статически связываю, только некоторые из моих статических членов данных инициализируются.
Допустим, мой завод строит автомобили. У меня есть классы CarCreator, которые могут создавать несколько автомобилей, но не все. Я хочу, чтобы фабрика собирала все эти классы CarCreator, чтобы код, ищущий новый Автомобиль, мог отправляться на фабрику без необходимости знать, кто будет заниматься фактической конструкцией.
Итак, у меня есть
CarTypes.hpp
enum CarTypes
{
prius = 0,
miata,
hooptie,
n_car_types
};
MyFactory.hpp
class CarCreator
{
public:
virtual Car * create_a_car( CarType ) = 0;
virtual std::list< CarTypes > list_cars_I_create() = 0;
};
class MyFactory // makes cars
{
public:
Car * create_car( CarType type );
void factory_register( CarCreator * )
static MyFactory * get_instance(); // singleton
private:
MyFactory();
std::vector< CarCreator * > car_creator_map;
};
MyFactory.cpp
MyFactory:: MyFactory() : car_creator_map( n_car_types );
MyFactory * MyFactory::get_instance() {
static MyFactory * instance( 0 ); /// Safe singleton
if ( instance == 0 ) {
instance = new MyFactory;
}
return instance;
}
void MyFactory::factory_register( CarCreator * creator )
{
std::list< CarTypes > types = creator->list_cars_I_create();
for ( std::list< CarTypes >::const_iteator iter = types.begin();
iter != types.end(); ++iter ) {
car_creator_map[ *iter ] = creator;
}
}
Car * MyFactory::create_car( CarType type )
{
if ( car_creator_map[ type ] == 0 ) { // SERIOUS ERROR!
exit();
}
return car_creator_map[ type ]->create_a_car( type );
}
...
Тогда у меня будут конкретные машины и конкретные создатели автомобилей:
Miata.cpp
class Miata : public Car {...};
class MiataCreator : public CarCreator {
public:
virtual Car * create_a_car( CarType );
virtual std::list< CarTypes > list_cars_I_create();
private:
static bool register_with_factory();
static bool registered;
};
bool MiataCreator::register_with_factory()
{
MyFactory::get_instance()->factory_register( new MiataCreator );
return true;
}
bool MiataCreator::registered( MiataCreator::register_with_factory() );
...
Повторюсь: при динамическом связывании моих библиотек MiataCreator :: register будет инициализирован, статически связывая мои библиотеки, он не будет инициализирован.
При статической сборке, когда кто-то отправляется на завод, чтобы запросить Miata, элемент miata car_creator_map
будет указывать на NULL и программа завершит работу.
Есть ли что-то особенное в частных статических интегральных элементах данных, что их инициализация будет как-то пропущена? Статические члены-члены инициализируются только в том случае, если используется класс? Мои классы CarCreator не объявлены ни в одном заголовочном файле; они полностью живут в файле .cpp. Возможно ли, что компилятор встраивает функцию инициализации и как-то избегает вызова MyFactory :: factory_register
?
Есть ли лучшее решение этой проблемы с регистрацией?
Невозможно перечислить все CarCreators в одной функции, явно зарегистрировать каждую из них на заводе-изготовителе, а затем гарантировать, что эта функция вызывается. В частности, я хочу связать несколько библиотек вместе и определить CarCreators в этих отдельных библиотеках, но по-прежнему использую единственную фабрику для их создания.
...
Вот некоторые ответы, которые я ожидаю, но которые не решают мою проблему:
1) ваша фабрика-одиночка не является поточно-ориентированной.
а) Не имеет значения, я работаю только с одним потоком.
2) ваша Singleton Factory может быть неинициализирована, когда инициализируются ваши CarCreators (то есть вы потерпели статическую инициализацию)
а) Я использую безопасную версию класса singleton, помещая экземпляр singleton в функцию. Если бы это было проблемой, я бы увидел вывод, если бы добавил оператор print к методу MiataCreator's::register_with_factory
: нет.