Вы одновременно описываете различные проблемы. Что касается конкретной проблемы наличия некоторой статической инициализации , простой подход заключается в создании поддельного класса, который будет выполнять регистрацию. Тогда каждый из различных классов может иметь член static const X
, член должен быть определен в единице перевода, и определение будет инициировать создание экземпляра и регистрацию класса.
Это не решает сложную проблему, которая является фиаско порядка инициализации. Язык не дает никаких гарантий относительно порядка инициализации объектов в разных единицах перевода. То есть, если вы скомпилируете три единицы перевода с такими классами, нет никакой гарантии относительно относительного порядка выполнения ложного члена. Это также относится к библиотеке: нет гарантии, что контейнер, в котором вы хотите зарегистрировать ваши классы, был инициализирован, если такой контейнер является глобальным / статическим атрибутом члена.
Если у вас есть доступ к коду, вы можете изменить код контейнера для использования static local variables
, и это будет шагом вперед для обеспечения порядка инициализации. В качестве эскиза возможного решения:
// registry lib
class registry { // basically a singleton
public:
static registry& instance() { // ensures initialization in the first call
static registry inst;
return inst;
}
// rest of the code
private:
registry(); // disable other code from constructing elements of this type
};
// register.h
struct register {
template <typename T>
register( std::string name ) {
registry::instance().register( name, T (*factory)() ); // or whatever you need to register
}
};
// a.h
class a {
public:
static a* factory();
private:
static const register r;
};
// a.cpp
const register a::r( "class a", a::factory );
// b.h/b.cpp similar to a.h/a.cpp
Теперь в этом случае нет определенного порядка регистрации классов a
и b
, но это может и не быть проблемой. С другой стороны, с помощью локальной статической переменной в функции registry::instance
гарантируется, что инициализация синглтона будет выполняться до любого вызова методов registry::register
часть первого вызова метода instance
).
Если вы не можете сделать это изменение, вам в основном не повезло, и вы не можете гарантировать, что экземпляр registry
будет создан раньше, чем другие атрибуты статического члена (или глобальные переменные) в других единицах перевода. Если это так, то вам придется отложить регистрацию класса до первого экземпляра и добавить код в конструктор каждого регистрируемого класса, который гарантирует, что класс зарегистрирован за до фактического построения объекта.
Это может быть или не быть решением, в зависимости от того, создает ли другой код объекты типа или нет. В частном случае фабричных функций (первая, которая пришла в голову), если ничто иное не разрешено создавать объекты типов a
или b
..., то регистрация с резервным копированием для вызовов конструктора также не будет решением.