Тестирование софта - статьи
ce076b8f

Использование динамически загружаемых классов


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

Для начала нам потребуется инстанция динамического загрузчика классов - PDL::DynamicLoader. Этот класс представляет собой синглтон, т.е. существует всегда в единственном экземпляре. Для получения ссылки на этот экземпляр используем статический метод DynamicLoader::Instance():

Код

PDL::DynamicLoader & dynamicLoader = PDL::DynamicLoader::Instance();

Далее, нам нужно загрузить инстанцию класса и получить указатель на неё:

Код

MyTestInterface * instance =
    dynamicLoader.GetClassInstance< MyTestInterface >( myLibName, "MyTestClass1" );

Здесь myLibName - это имя библиотеки-плагина, например "MyTestClass1.dll" или "MyTestClass.so". Не забываем подключить заголовочный файл с определением интерфейса класса - в нашем случае это MyTestInterface.hpp, упомянутый выше.

Наконец, вызываем метод загруженного класса:

Код

instance -> DoSomething();

Так как динамический загрузчик в случае ошибки выбрасывает исключения типа PDL::LoaderException, будет правильно обернуть его вызовы в блок try/catch. Полный код примера будет выглядеть так:

Код

#include <MyTestInterface.hpp>
#include <stdio.h>

try
{
    PDL::DynamicLoader & dynamicLoader = PDL::DynamicLoader::Instance();
    MyTestInterface * instance =
        dynamicLoader.GetClassInstance< MyTestInterface >( myLibName, "MyTestClass1" );
    instance -> DoSomething();
}
catch( PDL::LoaderException & ex )
{
    fprintf( stderr, "Loader exception: %s\n", ex.what() );
}

Следует отметить ещё один интересный момент: все динамически загруженные классы ведут себя как синглтоны. Иными словами, при повторном вызове DynamicLoader::GetInstance() с тем же именем библиотеки и с тем же именем класса будет возвращён указатель на уже загруженный экземпляр.
Это сделано для облегчения контроля удалением инстанций динамически загружаемых классов. Если потребуется создавать множество инстанций классов, рекомендуется реализовать фабрику классов и сделать её динамически загружаемой.А как же удаляются инстанции загруженных классов? Для этого и существует метод DynamicClass::Destroy(). Вызывать напрямую его не нужно - это делает сам загрузчик в своём деструкторе, либо при вызове метода DynamicLoader::Reset(). Почему же не вызывается обычный деструктор? Дело в том, что некоторые тонкости механизма выделения динамической памяти меняются от компилятора к компилятору. Представим себе, что плагин собран компилятором A, а основная программа, использующая его - компилятором B. Если мы загружаем класс из плагина, его инстанция создаётся кодом, сгенерированным компилятором A. И если мы попытаемся удалить эту инстанцию из кода основной программы просто вызвав деструктор, то удалить её попытается код, сгенерированный компиляторов B. И тут возможны казусы.Для того, чтобы избежать подобных проблем, деструктор ~DynamicClass() сделан защищённым, а вместо него следует вызывать метод DynamicClass::Destroy(). Это метод будет гарантирует, что код деструктора класса скомпилирован тем же компилятором, что и код его конструктора.

Содержание раздела