Factory Pattern в C++: автоматическое создание явного метода createInstance()

У меня проблема с написанием фреймворка C++, так как у пользователей должно быть меньше накладных расходов, чем это возможно для его использования. Пользователи могут опубликовать свою работу в фреймворках, создав разделяемую библиотеку, содержащую класс, производный от базового класса фреймворка, и реализуя внешний C-метод createInstance() для возврата экземпляра его производного класса. Таким образом, фреймворк может получить доступ к пользовательскому классу, вызвав метод createInstance-Method через разделяемую библиотеку с помощью 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 и соответствующее изменение имени. (Я знаю любое пространство имен + имя класса из файла конфигурации) Но я не думаю, что это правильное решение, особенно вызов конструктора не то же самое, что вызов обычной функции... но очень интересно...


person Dudero    schedule 05.07.2011    source источник


Ответы (2)


Я не могу представить способ создать это автоматически без какого-либо кодирования для каждой части пользователя. Я могу представить способы упростить его, возможно, с помощью макроса:

#define OBJECT_CREATOR(X) \
    extern "C" {          \
         BaseClass *UserXCreateInstance() {\
             return new X(); \
         }\
    }

А пользователю достаточно поставить на свой cpp файл:

OBJECT_CREATOR(UserClass);
person bcsanches    schedule 05.07.2011
comment
Спасибо, одна строчка лучше четырех ;) - person Dudero; 07.07.2011

Я предполагаю, что вызов пользовательской функции через 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);

Соответствует ли этот дизайн вашим потребностям?

person n. 1.8e9-where's-my-share m.    schedule 05.07.2011
comment
Ваша идея звучит очень интересно, но мне она не подходит. Конструктор статического вспомогательного класса не достигается... Должно ли это происходить с помощью dlopen-вызова разделяемой библиотеки пользователей или есть дополнительные предварительные условия? - person Dudero; 07.07.2011
comment
О-о, мне очень жаль, это действительно не работает. Я дам вам знать, если я могу это исправить. - person n. 1.8e9-where's-my-share m.; 07.07.2011