crossplatform.ru

Здравствуйте, гость ( Вход | Регистрация )

2 страниц V   1 2 >  
Ответить в данную темуНачать новую тему
> Наследование от динамически импортируемого класса, Возможно ли такое? Интересует реализация под MinGW
Iron Bug
  опции профиля:
сообщение 14.2.2011, 9:57
Сообщение #1


Профессионал
*****

Группа: Модератор
Сообщений: 1611
Регистрация: 6.2.2009
Из: Yekaterinburg
Пользователь №: 533

Спасибо сказали: 219 раз(а)




Репутация:   12  


Можно ли такое реализовать?
Допустим, есть примерно такой код (собираю под под MinGW):

В основном модуле:
#ifdef __DLL
    #define __DLL_BASE __attribute__((dllimport))
#else
    #define __DLL_BASE __attribute__((dllexport))
#endif

class __DLL_BASE Base
{
  public:
     Base() {};
     ~Base() {};
...
};


В загружаемой динамической библиотеке:
#ifdef __DLL
    #define __DLL_DERV __attribute__((dllexport))
#else
    #define __DLL_DERV __attribute__((dllimport))
#endif

class __DLL_DERV Derived : public Base
{
   public:
      Derived() {};
      ~Derived() {};
...
};


Идея в том, чтобы в основном модуле был базовый класс Base со всеми причиндалами (туда же компилится и его код). А в подгружаемых через LoadLibrary (и заранее неизвестных) библиотеках - дополнительные классы-наследники Derived , которые бы вызывали конструкторы/деструкторы базового класса, а сами были бы экспортируемыми, чтобы основной модуль мог их загружать и использовать.
Копаю-копаю, и вот ну никак! При такой реализации вроде бы должно работать, но линкер при сборке dll (__DLL при этом определён) вопит, что декорированное имя импорта конструктора базового класса не определено. Вообще, теоретически, все базовые классы экспортируемого класса тоже должны быть экспортируемыми. Ну, я пыталась добавить в Base ещё и dllexport, но не проканало. То ли неправильно добавляла, то ли это вообще компилятор не жрёт. Он не ругается на такой двойной import-export, но и ошибка не исчезает.

Сообщение отредактировал Iron Bug - 14.2.2011, 9:57
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
Iron Bug
  опции профиля:
сообщение 15.2.2011, 20:37
Сообщение #2


Профессионал
*****

Группа: Модератор
Сообщений: 1611
Регистрация: 6.2.2009
Из: Yekaterinburg
Пользователь №: 533

Спасибо сказали: 219 раз(а)




Репутация:   12  


в общем, ковыряла-ковыряла и решила-таки эту задачу. правда, чуть окольным путём:
теперь у меня две библиотеки и основной модуль. в библиотеках - базовый класс и класс-наследник. библиотека наследника линкуется динамической связью с библиотекой базового класса. а основной модуль грузит класс-наследник из библиотеки через dlopen (ну или LoadLibrary под вендой). если потомок не реализует метод, то вызывается метод предка, из базовой библиотеки. всё работает и под вендой, и под линём. правда, пришлось кое-где idfef'ы налепить и с флагами линковки всего этого безобразия повозиться, чтобы работало как надо. и пока создание представителя класса сделано через специальные методы, а не подменой указателей в виртуальных таблицах, хотя можно и напрямую класс грузить из библиотеки, если маленько ассемблера добавить.

Сообщение отредактировал Iron Bug - 15.2.2011, 20:37
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
ViGOur
  опции профиля:
сообщение 19.2.2011, 23:23
Сообщение #3


Мастер
******

Группа: Модератор
Сообщений: 3296
Регистрация: 9.10.2007
Из: Москва
Пользователь №: 4

Спасибо сказали: 231 раз(а)




Репутация:   40  


Сам не писал подобного, но читал статью как подобное реализовать на рсдн, вот только найти не могу, так как давно уже читал. :(
Ты лучше описание кодом как реализовала выложи... :)
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
Iron Bug
  опции профиля:
сообщение 21.2.2011, 14:45
Сообщение #4


Профессионал
*****

Группа: Модератор
Сообщений: 1611
Регистрация: 6.2.2009
Из: Yekaterinburg
Пользователь №: 533

Спасибо сказали: 219 раз(а)




Репутация:   12  


у меня тут кода слишком дофига. проект жирный, много-много библиотек, полная кроссплатформа, буст и надо специально сидеть и выкусывать куски настроек, относящиеся к данной подзадаче.
но если кому очень приспичит, могу пошариться и выписать. только не срочно, а то у меня тут большой проект с парсерами и работой с кучей железа. я по уши в работе, днём и ночью. сейчас вот добила интерфейс для ещё одной шины и вылезла в сеть - мозг разгрузить маленько :)
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
Iron Bug
  опции профиля:
сообщение 12.3.2011, 17:28
Сообщение #5


Профессионал
*****

Группа: Модератор
Сообщений: 1611
Регистрация: 6.2.2009
Из: Yekaterinburg
Пользователь №: 533

Спасибо сказали: 219 раз(а)




Репутация:   12  


Я тут обнаружила потребность народа в некотором примере насчёт использования динамических библиотек.
Вот, вытащила из своего проекта куски, относящиеся к загрузке и вызовам.
Смысл примера такой: есть приложение, которое грузит (динамически) библиотеки с некоторым интерфейсом (IInterface).
Все загружаемые библиотеки наследуют некоторое дефолтное поведение от базового класса (Base). Если библиотека не реализует какой-то метод интерфейса, то будет вызван базовый метод. Это чтобы не переписывать в каждой библиотеке одни и те же действия.
Пример загружаемой библиотеки - Derived.
Я привела флаги компиляции и сборки для разных систем (я собираю под линюксом с gcc и icc и под вендой с mingw).
Вроде ничего не забыла, но возможно, что некоторые флаги не нужны для данного примера (я их поместила в квадратные скобки [...]). Просто у меня проект большой и там много чего ещё кроме этого, поэтому там могли оказаться не относящиеся к данной подзадаче параметры. Да, стандартные флаги для библиотек типа -ldl я сюда не выписывала. Выписаны только специфические параметры.
Какие плюсы? Собственно, независимость библиотек от основного кода. Для добавления функционала достаточно перекомпилять загружаемые модули. Базовый функционал класса Base можно менять, не пересобирая библиотеки, которые его используют.

Надеюсь, что в принципе понятно.

Общий файл интерфейса Interface.h
Раскрывающийся текст
#ifndef __INTERFACE_H__
#define __INTERFACE_H__

// interface
class IInterface
{
public:
    IInterface() {};
    virtual ~IInterface() {};
    virtual void init() = 0;
};

// types for class instance creators/destroyers
typedef IInterface *(*funcInterfaceCreate_type)();
typedef void (*funcInterfaceDelete_type)(IInterface *);

#endif // __INTERFACE_H__


Инструкции по сборке базовой библиотеки Base:
gcc,icc:
CXXFLAGS: -fPIC
LDFLAGS: -nodefaultlibs -shared

mingw:
для венды установить дефайн__DLL (для экспорта)
CXXFLAGS:
LDFLAGS: -enable-auto-import [-enable-runtime-pseudo-reloc] -Wl,--kill-at

Заголовок базового класса Base.h
Раскрывающийся текст
#ifndef __BASE_H__
#define __BASE_H__

#include "Interface.h"

#ifndef __linux__
#ifdef __DLL
#define __BASE_DLL __declspec(dllexport)
#else
#define __BASE_DLL __declspec(dllimport)
#endif
#else
#define __BASE_DLL
#endif

class __BASE_DLL Base : public IInterface
{
protected:
    int _param;
public:
    Base();
    virtual ~Base();
    virtual void init();
};

#endif


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

Инструкции по сборке библиотеки Derived:
gcc,icc:
CXXFLAGS: -fPIC [-enable-runtime-pseudo-reloc]
LDFLAGS: -nodefaultlibs -shared -lBase

mingw:
для венды установить дефайн __DLL (для экспорта)
CXXFLAGS:
LDFLAGS: -enable-auto-import [-Wl,--allow-multiple-definition]

Заголовок файла основной динамически загружаемой библиотеки Derived.h
Раскрывающийся текст
#ifndef __DERIVED_H__
#define __DERIVED_H__

#include "Base.h"

#ifndef __linux__
#ifdef __DLL
    #define __DERIVED_DLL __declspec(dllexport)
#else
    #define __DERIVED_DLL __declspec(dllimport)
#endif
#else
#define __DERIVED_DLL
#endif

class __DERIVED_DLL Derived : public Base
{
    public:
        Derived();
        virtual ~Derived();
        virtual void init();
};
#endif


Пример реализации основной динамически загружаемой библиотеки Derived.cpp
Раскрывающийся текст
#include "Derived.h"

Derived::Derived() : Base()
{}

Derived::~Derived()
{}

void Derived::init()
{
    Base::init();
}

#ifdef __cplusplus
extern "C" {
#endif

IInterface
#ifndef __linux__
__stdcall __DERIVED_DLL
#endif
*CreateObject()
{
    return (IInterface*)new Derived();
}

void
#ifndef __linux__
__stdcall __DERIVED_DLL
#endif
DeleteObject(IInterface *pDerived)
{
    if(pDerived==0) throw;
    delete(pDerived);
}

#ifdef __cplusplus
}
#endif


Инструкции по сборке вызывающей программы:
gcc,icc:
CXXFLAGS: -fPIC
LDFLAGS:

mingw:
CXXFLAGS:
LDFLAGS: -enable-auto-import [-enable-runtime-pseudo-reloc] [-Wl,--allow-multiple-definition] -Wl,--kill-at

Пример загрузки/выгрузки библиотеки Derived и использования класса (вызов метода init):
Раскрывающийся текст
#include "IInteface.h"

#ifdef __linux__
void *_pDll; // linux handle for dynamic library
#else
HMODULE _hDll; // windows handle for dynamic library
#endif

funcInterfaceCreate_type _pFuncCreateObject; //pointer to CreateObject function in dll
funcInterfaceDelete_type _pFuncDeleteObject; //pointer to DeleteObject function in dll

IInterface *_pInterface;

void load()
{
#ifdef __linux__
    _pDll = dlopen("libDerived.so",RTLD_NOW);
    if(_pDll != 0)
    {
        union
        {
            funcInterfaceCreate_type func;
            void *ptr;
        } uniCreate;
        uniCreate.ptr = dlsym(_pDll,"CreateObject");
        _pFuncCreateObject = uniCreate.func;
        union
        {
            funcInterfaceDelete_type func;
            void *ptr;
        } uniDelete;
        uniDelete.ptr = dlsym(_pDll,"DeleteObject");
        _pFuncDeleteObject = uniDelete.func;
    }
#else
    _hDll = LoadLibrary("Derived.dll");
    if(_hDll != NULL)
    {
        _pFuncCreateObject = (funcInterfaceCreate_type)GetProcAddress(_hDll,"CreateObject");
        _pFuncDeleteObject = (funcInterfaceDelete_type)GetProcAddress(_hDll,"DeleteObject");
    }
#endif
    _pInterface = (*_pFuncCreateObject)();
}

void unload()
{
    (*_pFuncDeleteObject)(_pInterface);

#ifdef __linux__
    dlclose(_pDll);
#else
    FreeLibrary(_hDll);
#endif
}

void use()
{
    _pInterface->init();
}


Примечание: при вызове _pInterface->init() будет вызван метод init класса Derived.

Сообщение отредактировал Iron Bug - 12.3.2011, 18:08
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
PAFOS
  опции профиля:
сообщение 6.6.2011, 16:12
Сообщение #6


Активный участник
***

Группа: Участник
Сообщений: 258
Регистрация: 27.12.2010
Из: Дмитров
Пользователь №: 2309

Спасибо сказали: 29 раз(а)




Репутация:   8  


напоминает технологию COM мелкософта. :rolleyes:
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
Iron Bug
  опции профиля:
сообщение 6.6.2011, 22:58
Сообщение #7


Профессионал
*****

Группа: Модератор
Сообщений: 1611
Регистрация: 6.2.2009
Из: Yekaterinburg
Пользователь №: 533

Спасибо сказали: 219 раз(а)




Репутация:   12  


Цитата(PAFOS @ 6.6.2011, 19:12) *
напоминает технологию COM мелкософта.

главная идея COM-объектов - счётчики ссылок. а это обычное наследование, просто реализованное в библиотеках.
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
PAFOS
  опции профиля:
сообщение 7.6.2011, 8:10
Сообщение #8


Активный участник
***

Группа: Участник
Сообщений: 258
Регистрация: 27.12.2010
Из: Дмитров
Пользователь №: 2309

Спасибо сказали: 29 раз(а)




Репутация:   8  


Имхо счетчик ссылок тут лишь способ правильного управления временем жизни объекта. *

Однако СОМ это ведь не только счетчик ссылок. Клиент (Приложение) знает об интерфейсе IUnknown и знает об интерфейсе, который несет полезную нагрузку. Через IUnknown клиент может узнать поддерживает ли сервер (СОМ объект) нужный ему интерфейс. Так же через IUnknown ведется подсчет ссылок. Но это только вершина айсберга...


t;line-height:100%">*Iron Bug, рано или поздно (если будешь развивать эту тему) ты придешь к вопросу о моменте удаления объекта. В данной версии момент удаления объекта определяет программист (правильно ли я понял?), использующий объект, а не программист, разрабатывающий сам объект. Это конечно по нашему, по С++вски, но накладывает на ленивого программиста дополнительные обязанности.
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
Iron Bug
  опции профиля:
сообщение 7.6.2011, 20:27
Сообщение #9


Профессионал
*****

Группа: Модератор
Сообщений: 1611
Регистрация: 6.2.2009
Из: Yekaterinburg
Пользователь №: 533

Спасибо сказали: 219 раз(а)




Репутация:   12  


ну, если прога загружает библиотеку и создаёт представитель класса, то она же его и убивает. тут проблем не возникает. единственное, за чем следит сам юзер - это очистка ео локальных переменных и правильное завершение работы после вызова деструктора.
на самом деле, для меня это не проблема, потому что я - единственный разработчик "плагинов" для своей софтины :) проблема в том, что надо уметь динамически, где-то в поле, расширять функционал, без пересборки основной софтины. и эту проблему я решила через такие расширения, которые просто прописываются в конфиге и подгружаются динамически.
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
PAFOS
  опции профиля:
сообщение 9.6.2011, 8:05
Сообщение #10


Активный участник
***

Группа: Участник
Сообщений: 258
Регистрация: 27.12.2010
Из: Дмитров
Пользователь №: 2309

Спасибо сказали: 29 раз(а)




Репутация:   8  


с этими плагинами надо очень быть предусмотрительным)

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

Мне тут однажды попалась на глаза программа SharpDevelop (опен сорс IDE для разработки на .Net). У нее мне понравилась система плагинов - там есть возможность расширять одни плагины другими. Очень интересная идея имхо)
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение

2 страниц V   1 2 >
Быстрый ответОтветить в данную темуНачать новую тему
Теги
Нет тегов для показа


1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0




RSS Текстовая версия Сейчас: 29.3.2024, 11:49