Logo    
Деловая газета CitCity.ru CITKIT.ru - все об Open Source Форумы Все публикации Учебный центр Курилка
CitForum    CITForum на CD    Подписка на новости портала Море(!) аналитической информации! :: CITFORUM.RU
IT-консалтинг Software Engineering Программирование СУБД Безопасность Internet Сети Операционные системы Hardware

22.02.2017

Google
WWW CITForum.ru
С Новым годом!
2007 г.

Знакомство с PDL (Portable Dynamic Loader)

Igor Semenov aka Garik, ProgZ.ru

Что такое PDL

PDL (Portable Dynamic Loader) - это легкая, простая и портабельная библиотека, предназначенная для создания и использования динамически загружаемых объектов классов.

Для чего же нужна динамическая загрузка классов

Основное применение этой технологии - это разработка динамически подключаемых плагинов, расширяющих функциональность основной программы. Главная проблема состоит в том, что динамически загружаемые библиотеки на многих платформах качественно поддерживают только процедурный стиль программирования, а с загрузкой и использованием экземпляров классов возникают разнообразные проблемы. Большинство этих проблем (но, к сожалению, пока не все) и решает библиотека PDL.

На платформе Win32 PDL является сильно упрощённой альтернативой использованию технологии COM, без счётчика ссылок, без глобальной регистрации классов и множества других возможностей. Для Unix/Linux платформ существуют аналогичные библиотеки, например, С++ Dynamic Class Loader. Поддержка динамической загрузки классов присутствует и в крупной кросс-платформенной библиотеке WxWidgets.

Целью разработки PDL было создание именно кроссплатформенной библиотеки, которая позволила бы использовать один и тот же код загрузки классов для Win32 и Unix/Linux платформ (в отличие от COM и C++ Dynamic Class Loader). В то же время, библиотека должна была быть максимально независимой и легковесной (поэтому была забракована WxWidgets).

Создание динамически загружаемого класса

Итак, рассмотрим подробно процесс создания динамически загружаемого класса с использованием библиотеки PDL. Прежде всего, следует определить интерфейс класса, через который мы будем работать с экземпляром загруженного класса. Обязательное условие: этот интерфейс должен наследоваться от класса PDL::DynamicClass. Давайте повнимательнее присмотримся к определению DynamicClass:

Код

сlass DynamicClass
{
public:
    /**
     * @brief Get class name
     * return class name
     */

    virtual const char * GetClassName() const throw() = 0;
    
    /**
     * @brief Destroy class instance
     */

    void Destroy() throw() { delete this; }

protected:
    /**
     * @brief Destructor
     */

    virtual ~DynamicClass() throw() {;; }
};

Чисто виртуальная функция GetClassName() возвращает имя класса. Вам не нужно беспокоиться о её определении, чуть ниже я объясню почему.

Невиртуальная функция Destroy() служит для уничтожения экземпляра класса. При этом сам деструктор сделан защищённым, чтобы предотвратить его прямой вызов. Зачем именно это сделано, будет также объяснено ниже.

Итак, наш интерфейс мы должны унаследовать от PDL::DynamicClass и определить в нём виртуальный деструктор, желательно в секции protected (чтобы не нарушать идеологию PDL).

Кроме того, в объявлении интерфейса обязательно нужно написать макрос DECLARE_DYNAMIC_CLASS, передав ему в качестве параметра имя класса. Если посмотреть на описание этого макроса, становится очевидно его предназначение: он просто определяет виртуальную функцию GetClassName():

Код

#define DECLARE_DYNAMIC_CLASS( className ) \
public: \
    virtual const char * GetClassName() const throw() { return #className; }

Ну и напоследок, добавим в описание интерфейса объявления чисто виртуальных методов, реализующих полезную функциональность будущих динамически загружаемых классов. Пусть в нашем случае это будет метод void DoSomething().

В итоге мы имеем следующее объявление интерфейса:

Код

#include <DynamicClass.hpp>

сlass MyTestInterface : public PDL::DynamicClass
{
public:
    /**
     * @brief Test method
     */

    virtual void DoSomething() throw() = 0;

    /**
     * @brief Declare this class dynamically loadable
     */

    DECLARE_DYNAMIC_CLASS( MyTestInterface )
};

Это объявление следует поместить в отдельный заголовочный файл, в нашем случае - MyTestInterface.hpp, потому как для обеспечения совместимости включаться он будет и при сборке динамически загружаемого класса, и при его непосредственной загрузке и использовании.

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

Код

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

class MyTestClass1 : public MyTestInterface
{
public:
    /**
     * @brief Test method
     */

    void DoSomething() throw()
    {
        fprintf( stderr, "MyTestClass1::DoSomething()\n" );
    }
};

EXPORT_DYNAMIC_CLASS( MyTestClass1 )

Взглянем на определение макроса EXPORT_DYNAMIC_CLASS (файл DynamicClass.hpp):

Код

#define EXPORT_DYNAMIC_CLASS( className ) \
extern "C" PDL_DECL_EXPORT PDL::DynamicClass * Create##className() \
{ \
    try { return new className(); } \
    catch( ... ) {;; } \
    return NULL; \
}

Этот макрос объявляет и определяет экспортируемую функцию Create<имя_класса>, которая создаёт и возвращает экземпляр указанного в параметре класса. Модификатор extern "C" нужен для того, чтобы компилятор не декорировал имя функции в библиотеке. Макрос PDL_DECL_EXPORT объявляет функцию экспортируемой. Он определён в файле platform.h и его реализация зависит от конкретной платформы.

Следует обратить внимание на две вещи. Во-первых, в экспортируемой функции ловятся все исключения конструктора. В случае, если вызов конструктора аварийно завершился выбросом исключения, функция просто вернёт NULL. Это связано со сложностями, возникающими при попытке обработать в основном коде исключение, выбрасываемое кодом плагина. Во-вторых, экспортируемая функция возвращает указатель на PDL::DynamicClass, к которому неявно преобразовывается созданный объект класса. Таким образом, если мы забудем унаследовать интерфейс создаваемого класса от PDL::DynamicClass, компилятор напомнит нам об этом.

После того, как класс создан, мы можем скомпилировать плагин. Следует отметить, что один плагин может содержать несколько различных динамически загружаемых классов, главное условие: они должны иметь уникальные в рамках этого плагина имена. Не следует забывать, что экспортировать при помощи макроса EXPORT_DYNAMIC_CLASS нужно каждый из классов.

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

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

Для начала нам потребуется инстанция динамического загрузчика классов - 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(). Это метод будет гарантирует, что код деструктора класса скомпилирован тем же компилятором, что и код его конструктора.

Ложка дёгтя

Есть один тонкий момент, связанный с именами библиотек. Если имя библиотеки меняется, то считается, что это уже совершенно другая библиотека, хотя по-разному записанные имена могут указывать на одну и ту же библиотеку. Например: C:\MyProg\libs\mylib.dll и MyLIB.DLL.

Следует отметить также, что библиотека PDL не решает проблемы с различным декорированием имён методов различными компиляторами - такая задача в настоящий момент не ставится.

В настоящий момент библиотека оттестирована на платформах:
FreeBSD 6.2
Debian 4.0 Linux 2.6.18-4
openSUSE 10.2
Windows XP
Я буду благодарен за любую информацию относительно работы PDL на других платформах.

Благодарности

Большое спасибо Владимиру Сторожевых (Влад) и Александру Леденёву (Shunix), благодаря чьей критике эта статья стала (я надеюсь) лучше.

Ресурсы

Страница на SourceForge:
http://sourceforge.net/projects/pdl-cpp/

Размещение рекламы — тел. +7 495 4119920, ICQ 232284597

Подписка на новости IT-портала CITForum.ru
(библиотека, CITKIT.ru, CitCity)

Новые публикации:

24 декабря

CITKIT.ru:

  • Новогодние поздравления
  • Сергей Кузнецов. Цикл Операционные системы: Ностальгия по будущему:

  • Алексей Федорчук. OpenSolaris 2008.11 Release

  • Сергей Голубев:

  • Евгений Чайкин aka StraNNik (Блогометки):

    17 декабря

  • С.Д.Кузнецов. Базы данных. Вводный курс

    10 декабря

    CITKIT.ru:

  • OpenSolaris 2008.11 Release

  • Альтернативные ОС: две грустные истории (С.Кузнецов)
  • Nokia N810 — доведение до ума
  • CitCity:

  • Платформа 2009: заоблачные перспективы Microsoft

    4 декабря

  • Лекция С.Д.Кузнецова Понятие модели данных. Обзор разновидностей моделей данных

    CITKIT.ru:

  • OpenSolaris 2008.11 Release. Первые впечатления

  • Linux vs FreeBSD: продолжим "Священные войны"?

  • Nokia N810 as is

  • Индульгенция для FOSS

  • Друзья СПО'2008

    26 ноября

  • Нечеткое сравнение коллекций: семантический и алгоритмический аспекты

    CitCity:

    CITKIT.ru:

  • Глава из книги А.Федорчука
    Сага о FreeBSD:
  • 19 ноября

  • Проблемы экономики производства крупных программных продуктов

  • Язык модификации данных формата XML функциональными методами

    CITKIT.ru:

  • Главы из книги А.Федорчука
    Сага о FreeBSD:

    Заметки к книге:

  • FreeBSD: монтирование сменных устройств и механизм HAL
  • Текстовый редактор ee

    12 ноября

  • Правило пяти минут двадцать лет спустя, и как флэш-память изменяет правила (Гоц Грейф, перевод: Сергей Кузнецов)

    CITKIT.ru:

  • Главы из книги А.Федорчука
    Сага о FreeBSD:
  • OSS в России: взгляд правоведа (В.Житомирский)

  • Новая статья из цикла С.Голубева "Железный марш":

    29 октября

  • О некоторых задачах обратной инженерии

  • Веб-сервисы и Ruby

  • Тестирование web-приложений с помощью Ruby

    CITKIT.ru:

  • Главы из книги А.Федорчука
    Сага о FreeBSD:

  • PuppyRus Linux - беседа с разработчиком (С.Голубев)

  • Сергей Кузнецов. Заметка не про Linux

    22 октября

  • Обзор методов описания встраиваемой аппаратуры и построения инструментария кросс-разработки

    CITKIT.ru:

  • Сергей Кузнецов. Почему я равнодушен к Linux

  • Глава из книги А.Федорчука
    Сага о FreeBSD:
  • Что надо иметь
    3. Базовые познания

    CitCity:

  • Управление IT-инфраструктурой на основе продуктов Microsoft

    15 октября

  • Методы бикластеризации для анализа интернет-данных

    CitCity:

  • Разъемы на ноутбуках: что они дают и зачем их так много?
  • AMD Puma и Intel Centrino 2: кто лучше?

    CITKIT.ru:

  • Новый цикл статей С.Голубева
    Железный марш:

  • Главы из книги А.Федорчука
    Сага о FreeBSD:

    8 октября

  • Автоматизация тестирования web-приложений, основанных на скриптовых языках
  • Опыт применения технологии Azov для тестирования библиотеки Qt3

    Обзоры журнала Computer:

  • SOA с гарантией качества
  • Пикоджоуль ватт бережет
  • ICT и всемирное развитие

    CitCity:

  • Пиррова победа корпорации Microsoft

    CITKIT.ru:

  • Главы из книги А.Федорчука
    Сага о FreeBSD:

    Статья из архива:

  • Я живу в FreeBSD (Вадим Колонцов)

    Новые Блогометки:

  • Перекройка шаблона Blogger или N шагов к настоящему
  • Blogger. Comment style
  • Screenie или глянцевый снимок экрана

    2 октября

    CITKIT.ru:

  • Сага о FreeBSD (А. Федорчук)

    Zenwalk: пакет недели

  • Банинг — интеллектуальное развлечение (С.Голубев)

    CitCity:

    25 сентября

  • Клермонтский отчет об исследованиях в области баз данных

    CITKIT.ru:

  • Пользователям просьба не беспокоиться... (В.Попов)

  • Снова про ZFS: диск хорошо, а два лучше
  • Командная оболочка tcsh (А.Федорчук)

    Zenwalk: пакет недели

    17 сентября

  • T2C: технология автоматизированной разработки тестов базовой функциональности программных интерфейсов
  • Технология Azov автоматизации массового создания тестов работоспособности

    CITKIT.ru:

  • FreeBSD: ZFS vs UFS, и обе-две — против всех (А.Федорчук)

    Zenwalk: пакет недели

  • Дачнет — практика без теории (С.Голубев)

    10 сентября

  • За чем следить и чем управлять при работе приложений с Oracle
  • Планировщик заданий в Oracle
    (В.Пржиялковский)

    CITKIT.ru:

  • Microsoft: ответный "боян" (С.Голубев)

  • Причуды симбиоза, или снова "сделай сам" (В.Попов)

  • Файловые системы современного Linux'а: последнее тестирование
  • Zsh. Введение и обзор возможностей
    (А.Федорчук)

    Описания пакетов Zenwalk: Zsh, Thunar, Thunar-bulk-rename, Xfce4-places-plugin, Xfce4-fsguard-plugin

    Блогометки:

  • Google Chrome
  • Лончер для ASUS Eee PC 701

    3 сентября

    CITKIT.ru:

  • Заметки о ядре (А.Федорчук):

    Добавлены описания пакетов Zenwalk: Galculator, Screenshot, Gnumeric, Pidgin

    В дискуссинном клубе:

  • И еще о Википедии и Google Knol

  • Лекция для начинающего линуксоида (С.Голубев)

    26 августа

  • Транзакционная память (Пересказ: С. Кузнецов)

    CITKIT.ru:

  • Открыт новый проект Zenwalk: пакет недели

  • Статья Текстовые процессоры и их быстродействие: конец еще одной легенды?

    21 августа

    CITKIT.ru:

  • Почему школам следует использовать только свободные программы (Ричард Столлман)
  • Беседа Сергея Голубева с учителем В.В.Михайловым

  • Википедия или Гуглезнание? Приглашение к обсуждению (Алексей Федорчук)
  • Народная энциклопедия от Google (StraNNik)

  • Обзор Mandriva 2009.0 Beta 1 Thornicrofti
  • Новичок в Линукс: Оптимизируем Mandriva 2008.1

  • Книга Zenwalk. Приобщение к Linux:

    13 августа

    CitCity:

  • Мирный Atom на службе человеку. Обзор платы Intel D945GCLF с интегрированным процессором
  • Обзор процессоров Intel Atom 230 на ядре Diamondville

  • iPhone - год спустя. Скоро и в России?

    CITKIT.ru:

  • Интермедия 3.4. GRUB: установка и настройка (из книги Zenwalk. Приобщение к Linux)

    6 августа

  • СУБД с хранением данных по столбцами и по строкам: насколько они отличаются в действительности? (Пересказ: С. Кузнецов)

    CITKIT.ru:

  • Интермедия 2.2. Что неплохо знать для начала (из книги Zenwalk. Приобщение к Linux)

  • И снова про шрифты в Иксах (А.Федорчук)

  • 20 самых быстрых и простых оконных менеджеров для Linux

  • Дело о трех миллиардах (С.Голубев)

    30 июля

  • OLTP в Зазеркалье (Пересказ: С. Кузнецов)

    CitCity:

  • Будущее BI в облаках?
  • Тиражные приложения и заказная разработка. Преимущества для заказчика
  • Дискуссия со сторонниками заказной разработки

    CITKIT.ru:

  • Новые главы книги Zenwalk. Приобщение к Linux:
  • Глава 8. Пакеты: средства установки, системы управления, системы построения
  • Глава 9. Zenwalk: репозитории, пакеты, методы установки

    23 июля

    CITKIT.ru:

  • Все против всех. 64 vs 32, Intel vs AMD, tmpfs vs ext3
  • Две головы от Intel

  • Zenwalk: обзор штатных приложений (глава из книги "Zenwalk. Приобщение к Linux")

  • Нормально, Григорий...

    16 июля

    Обзоры журнала Computer:

  • Перспективы и проблемы программной инженерии в XXI веке
  • Большие хлопоты с большими объемами данных
  • Перспективы наноэлектроники

    CITKIT.ru:

  • Интермедия о лицензиях (А.Федорчук. "Zenwalk. Приобщение к Linux")

  • Есть ли будущее у KDE?

  • Linux в школе: альтернативный вариант в задачах

  • Шифр (приключения агента Никодима)

    10 июля

    CITKIT.ru:

  • Новые разделы книги А. Федорчука Zenwalk. Приобщение к Linux:
  • Интермедия вступительная. Linux или GNU/Linux? Как вас теперь называть?
  • Глава 5. Среда Xfce
  • Глава 6. Xfce: приложения и плагины

  • ZUR (Zenwalk User Repository) FAQ

    2 июля

  • Персистентность данных в объектно-ориентированных приложениях (С. Кузнецов)

    CITKIT.ru:

  • Новые разделы книги А. Федорчука Zenwalk. Приобщение к Linux:
  • Интермедия 1.2. Дорога к Zenwalk'у. Период бури и натиска
  • Интермедия 3.3. Немного о Linux'е и "железе"
  • Глава 4. Настройка: инструментами и руками
  • Интермедия 4.1. Zenpanel и конфиги: поиски корреляции

  • Интервью с Жан-Филиппом Гийоменом, создателем дистрибутива Zenwalk

  • Linux в школе: первые итоги (С. Голубев)

    25 июня

    CITKIT.ru:

  • Zenwalk. Приобщение к Linux (А. Федорчук)

  • Логика и риторика (С.Голубев)

  • Технология Tru64 AdvFS

  • Ханс Райзер предлагает отвести полицейских к телу Нины

    18 июня

  • Проекты по управлению данными в Google (Пересказ: С. Кузнецов)

    CITKIT.ru:

  • ОС и поддержка "железа": мифы и реальность (А. Федорчук)

  • Linux в школе: другие дистрибутивы

  • Пинок (С. Голубев)

    4 июня

  • Ландшафт области управления данными: аналитический обзор (С. Кузнецов)

    CITKIT.ru:

  • Linux в школе: слово заинтересованным лицам

  • SlackBuild: пакеты своими руками

  • Linux от компании Novell. Установка и обзор openSUSE Linux

    Все публикации >>>




  • IT-консалтинг Software Engineering Программирование СУБД Безопасность Internet Сети Операционные системы Hardware

    Информация для рекламодателей PR-акции, размещение рекламы — тел. +7 495 4119920, ICQ 232284597 Пресс-релизы — pr@citcity.ru
    Послать комментарий
    Информация для авторов
    Rambler's Top100 TopList liveinternet.ru: показано число просмотров за 24 часа, посетителей за 24 часа и за сегодня This Web server launched on February 24, 1997
    Copyright © 1997-2000 CIT, © 2001-2007 CIT Forum
    Внимание! Любой из материалов, опубликованных на этом сервере, не может быть воспроизведен в какой бы то ни было форме и какими бы то ни было средствами без письменного разрешения владельцев авторских прав. Подробнее...