Как создать экземпляр статического класса в динамически загружаемой библиотеке

#c #dynamic #dll #plugins #shared

Вопрос:

Я использую систему плагинов времени компиляции на основе фабрики, которую я хотел бы перенести, чтобы она была более динамичной. На данный момент он основан на статических шаблонных классах регистраторов, где конструктор регистрирует класс на фабрике. например

 template <class T>
SingleModuleRegistrar<T>::SingleModuleRegistrar(ModuleDesc description={ T::factoryName(), T::category }) : _name(description.moduleName)
{
    ModuleFactoryamp; factory = ModuleFactory::Instance();
    factory.Register(this, description);
}
 

а затем в заголовке DefaultPlugin класса

 static SingleModuleRegistrar<DefaultPlugin> DefaultPluginRegistrar;
 

Когда это встроено в статическую библиотеку, DefaultPlugin оно регистрируется на заводе-изготовителе и доступно для использования.
Теперь я хочу переместить статическую библиотеку в библиотеку dll, чтобы я мог динамически добавлять новые плагины на фабрику.
Я могу загрузить dll без проблем, но статическая переменная, похоже, не создается. ожидали бы вы этого, а если нет, то как я могу запустить создание статического DefaultPluginRegistrar объекта ?

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

ПРАВКА: Я думаю, что мне следует добавить еще несколько деталей о структуре. В настоящее время DefaultPlugin регистрация находится в заголовке библиотеки, которая статически связана с .dll. Он также находится в пространстве имен.

 MyPlugin.dll
|_ MyStaticLibrary.lib
   |_DefaultPlugin.h
     SomeNamespace {
     class DefaultPlugin : public IPlugin
     {
     public: 
        DefaultPlugin();
        //etc. 
     };

     // registration
     static SingleModuleRegistrar<DefaultPlugin> DefaultPluginRegistrar;

     }
|_ SomeOtherStaticLibrary.lib

 

Я также попытался удалить статическое объявление и экспортировать символ, но безрезультатно

 __declspec(dllexport) SingleModuleRegistrar<DefaultPlugin> DefaultPluginRegistrar;
 

Комментарии:

1. Какой компилятор/набор инструментов вы используете? IIRC, поддержка среды выполнения в DLL должна обрабатывать инициализацию глобальных переменных (я думаю, в сообщении DLL_PROCESS_ATTACH).

2. В данный момент я использую visual studio, но мне также нужно будет сделать это в Linux/gcc

3. Я предполагаю, что DLL получает свою собственную копию ModuleFactory «синглтона» и регистрируется с ней, а не с копией, на которую смотрит EXE. Распечатайте или иным образом сбросьте amp;ModuleFactory::Instance() файл EXE и DLL — я подозреваю, что вы увидите два разных адреса.

4. К сожалению, это не так.. Я переехал ModuleFactory в «а». dll специально для того, чтобы остановить это с помощью статического связывания. Конструктор для DefaultPlugin никогда не вызывается.

5. Немного отвлекающего маневра выше, конструктор DefaultPlugin не должен вызываться во время регистрации 😀 Регистрация тоже не происходит, но я подумал, что должен это прояснить. Я вижу, что тестовый плагин зарегистрируется сам, если он находится в заголовке, который находится в самом проекте .dll, а не в одной из библиотек, на которые он ссылается.


Ответ №1:

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

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

Также может быть полезно отметить, что во время выполнения MS статика инициализируется внутри _initterm in initterm.cpp , from dll_main_crt_process_attach .