Фабричный шаблон в C++: генерация явного createInstance()- Метод автоматически
У меня есть проблема в написании C++ Framework, что пользователи должны иметь меньше накладных расходов, чем это возможно использовать. Пользователи могут публиковать свои работы в каркасах, создав общую библиотеку, которая содержит класс, производный от BaseClass платформ, и реализовав внешний метод "C" createInstance() для возврата экземпляру его производного класса. Таким образом, фреймворк может получить доступ к пользовательскому классу, вызвав метод createInstance через общую библиотеку с помощью dlsym().
class BaseClass{}
class UserClass : public BaseClass{}
extern "C"{
BaseClass* UserXcreateInstance(){
return new UserClass();
}
}
В рамках:
typedef BaseClass* (*CreateInstance) ();
void* handle;
CreateInstance createInstance;
handle = dlopen( "libUserLibrary.so", RTLD_LAZY | RTLD_GLOBAL );
createInstance = reinterpret_cast <CreateInstance*> dlsym( handle, "UserXcreateInstance" );
BaseClass* userX = createInstance();
Мой вопрос: возможно ли сгенерировать метод UserXcreateInstance(), который является избыточным в каждой пользовательской библиотеке, чтобы пользователю не приходилось об этом думать?
Я думал, что это будет возможно с шаблонами + макросами, но я еще не нашел способ сделать это...
Я подумал, что другой подход заключается в том, чтобы напрямую вызывать конструктор любого пользовательского класса через dlsym и подбирать названия. (Я знаю любое пространство имен + имя класса из файла конфигурации) Но я не думаю, что это правильное решение, особенно вызов конструктора - это не то же самое, что обычный вызов функции... но очень интересно...
2 ответа
Я не могу представить способ создать это автоматически без какого-либо кодирования для каждой части пользователя. Я могу представить способы упростить это, возможно, используя макрос:
#define OBJECT_CREATOR(X) \
extern "C" { \
BaseClass *UserXCreateInstance() {\
return new X(); \
}\
}
А пользователю просто нужно поставить на свой файл cpp:
OBJECT_CREATOR(UserClass);
Я буду предполагать вызов пользовательской функции через dlsym
не является абсолютным требованием.
То, что вы хотите, легко достичь с помощью CRTP. Затем конструктор статического вспомогательного объекта регистрирует соответствующие данные в центральном хранилище. Это должно идти так:
template <typename UserClass>
class BaseClass
{
private:
class UserObjectFactory
{
UserObjectFactory()
{
std::string myname = typeid(UserClass).name();
CentralObjectFactory::instance()->register(this, myname);
}
BaseClass* XUserCreateInstance()
{
return new UserClass;
}
};
static UserObjectFactory factory;
};
Пользовательский код прост:
class MyClass : public BaseClass<MyClass>
{
// whatever
};
Предположительно, CentralObjectFactory
Экземпляр содержит какую-то (мульти) карту из std::string
в UserObjectFactory
,
myname
может быть инициализирован к некоторой уникально сгенерированной строке вместо typeid(UserClass).name()
, чтобы избежать столкновений.
Если все, что вам нужно, это один объект класса каждого пользователя, вы можете сделать UserObjectFactory
создать экземпляр UserClass
и зарегистрируйте это вместо этого.
std::string myname = typeid(UserClass).name();
CentralObjectRepository::instance()->register(XUserCreateInstance(), myname);
Соответствует ли этот дизайн вашим потребностям?