C++: Как создать коллекцию классов при запуске
Я использую C++ 11. У меня есть N исходных файлов, каждый из которых содержит класс, который наследуется от общего базового класса. При запуске я хочу, чтобы каждый класс регистрировал себя в коллекции. Регистрация должна включать 1) данные, необходимые для определения цели класса, 2) фабрику классов для создания экземпляров класса. Количество исходных файлов неизвестно. Какой шаблон для этого? Решение должно быть кроссплатформенным, совместимым с Visual Studio 2013, gcc и другими.
3 ответа
Используя предложение @Pawel, я создал пример. Цель состоит в том, чтобы создать процесс регистрации, который собирает список классов, каждая регистрация имеет фабрику классов и произвольные данные. В частности, этот пример регистрирует классы Mime в std::map. Запись регистрации содержит вектор строк типа содержимого и фабрику классов для создания класса Mime.
В каждом исходном файле я использую один вкладыш для регистрации класса. Вектор типов содержимого передается для указания поддерживаемых типов класса. Каждый класс наследуется от базового класса с именем Mime (не показан).
RegisterTypes<Swagger> swagger(vector<const char *>({ "application/swagger+json" }));
В заголовочном файле определите структуру, которая будет содержать регистрационную запись. Необходимы два инициализатора, вектор типов контента и фабрика классов (реализованная как лямда).
struct Registry
{
public:
Registry() {} // initializer below forces this initializer to be needed
Registry(vector<const char *> ct, function<Mime*(void)> cf) : contentTypes(ct), classFactory(cf) {}
vector<const char *> contentTypes;
function<Mime*(void)> classFactory;
};
В заголовочном файле extern a std::map, который будет содержать регистрации. Каждая запись карты состоит из ключа и структуры регистрации. Фактическое определение находится в файле.cpp (не показан). Реестр использует структуру, поэтому он может содержать несколько значений.
extern std::map<const string, struct Registry> Mimes;
В заголовочном файле определите класс регистрации. Каждый экземпляр класса регистрации создает запись регистрации в std::map. В этом примере класс регистрации создает запись регистрации, состоящую из ключа (имени класса), вектора поддерживаемых типов контента и фабрики классов. Фабрика классов создает экземпляры класса и реализуется как лямда. Каждый зарегистрированный класс имеет общий базовый класс Mime.
template<class T> class RegisterTypes
{
public:
RegisterTypes(vector<const char *> contentTypes)
{
Mimes[typeid(T).name()] = { contentTypes, [](void) -> Mime * { return (Mime *)(new T()); } }; // requires a matching explicit initializer
}
};
Перво-наперво: имейте в виду, что это возможно, только если классы являются производными от одного базового класса, потому что вы можете хранить только один тип объекта на vector
пример.
И когда дело доходит до решения... вы можете объявить объект в соответствующем файле *.cpp (пометьте его как extern в файле *.h):
// SomeClass.h
// <-- class declaration goes here
extern SomeClass someObj;
// SomeClass.cpp
SomeClass someObj;
И добавьте его в вектор внутри конструктора:
SomeClass(){
myVector.push_back(*this);
}
Обратите внимание, что myVector
должен быть виден в этой области.
Ваш myVector
должны быть заполнены после включения SomeClass.h
файл с одним экземпляром SomeClass
, Обратите внимание, что если заполнение выполняется в базовом классе, вам не нужно делать это в каждом последующем дочернем классе, потому что базовый конструктор вызывается в любом случае.
Походит на Образец Наблюдателя. Может быть лучше шаблон в зависимости от того, что вы хотите сделать с этим списком классов.
Перекрестная совместимость будет полностью зависеть от вашей реализации, но не должна создавать проблем.