![]() |
|
1 | |
Теория плагинов30.04.2010, 17:02. Показов 31381. Ответов 40
Метки нет Все метки)
(
Всем привет.
Для одной моей проги, нужно реализовать поддержку плагинов. Плагины предполагаются простенькие, написанные на Си. То, что плагин, это просто .so файл - понятно. То, что прога может дергать из .so файла функции - тоже понятно. 1. Непонятно то, как сам плагин сможет дергать функции из программы? 2. Программа написана на С++, но плагины предполагаю писать на Си, во избежания бинарной несовместимости. В этом случае, какие сложности могут возникнуть? 3. Еще непонятно, каким образом "разделять" плагины, ведь их может быть несколько? 4. И еще непонятно, каким образом программе "сообщить" какие функции дергать из конкретного плагина? 5. И еще непонятно, каким образом плагин, сможет дергать функции из другого плагина? Нужна теоретическая подкова ![]() Благодарен всем откликнувшимся.
0
|
|
30.04.2010, 17:02 | |
Ответы с готовыми решениями:
40
Написание плагинов для notepad++ Система плагинов Взаимодействие плагинов
|
22 / 22 / 2
Регистрация: 06.12.2010
Сообщений: 125
|
||||||||||||||||||||||||||
12.03.2011, 19:48 | 21 | |||||||||||||||||||||||||
эхх... чем пояснять и доказывать, вот вам пример:
Я тут обнаружила потребность народа в некотором примере насчёт использования динамических библиотек. Вот, вытащила из своего проекта куски, относящиеся к загрузке и вызовам. Смысл примера такой: есть приложение, которое грузит (динамически) библиотеки с некоторым интерфейсом (IInterface). Все загружаемые библиотеки наследуют некоторое дефолтное поведение от базового класса (Base). Если библиотека не реализует какой-то метод интерфейса, то будет вызван базовый метод. Это чтобы не переписывать в каждой библиотеке одни и те же действия. Пример загружаемой библиотеки - Derived. Я привела флаги компиляции и сборки для разных систем (я собираю под линюксом с gcc и icc и под вендой с mingw). Вроде ничего не забыла, но возможно, что некоторые флаги не нужны для данного примера (я их поместила в квадратные скобки [...]). Просто у меня проект большой и там много чего ещё кроме этого, поэтому там могли оказаться не относящиеся к данной подзадаче параметры. Да, стандартные флаги для библиотек типа -ldl я сюда не выписывала. Выписаны только специфические параметры. Какие плюсы? Собственно, независимость библиотек от основного кода. Для добавления функционала достаточно перекомпилять загружаемые модули. Базовый функционал класса Base можно менять, не пересобирая библиотеки, которые его используют. Надеюсь, что в принципе понятно. Общий файл интерфейса Interface.h
gcc,icc: CXXFLAGS: -fPIC LDFLAGS: -nodefaultlibs -shared mingw: для венды установить дефайн__DLL (для экспорта) CXXFLAGS: LDFLAGS: -enable-auto-import [-enable-runtime-pseudo-reloc] -Wl,--kill-at Заголовок базового класса Base.h
Инструкции по сборке библиотеки 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
gcc,icc: CXXFLAGS: -fPIC LDFLAGS: mingw: CXXFLAGS: LDFLAGS: -enable-auto-import [-enable-runtime-pseudo-reloc] [-Wl,--allow-multiple-definition] -Wl,--kill-at Пример загрузки/выгрузки библиотеки Derived и использования класса (вызов метода init):
меня давно просили и вот количество просьб превысило порог моей лени и я написала своеобразный "отчёт о проделанной работе". перекопав море документации и наступив на кучу граблей, я всё-таки решила свою задачу. не знаю, подойдёт-не подойдёт, но мне кажется, что эти именно то самое, о чём спрашивал niXman или, во всяком случае, очень близкая к этому задача. да, кстати: в С++ классы экспортировать можно. только есть некоторые ограничения. но их можно аккуратненько обойти ![]()
1
|
![]() |
|
13.03.2011, 02:16 [ТС] | 22 |
Evg, давайте пойдем с другой стороны.
в моей реализации, что конкретно вы считаете неправильным? и почему? Iron Bug, посмотрел вашу статью. но возник всего один вопрос: что в вашем примере, радикально отличается от моего варианта?! единственное это то, что ваш вариант менее функционален, и сильно хуже спроектирован. или я что-то пропустил? ... подскажите.
0
|
![]() ![]() |
||||||
13.03.2011, 10:24 | 23 | |||||
Да не экспортируешь их ты. В том понимании, что подразумевается под этим словом в отношении плагинов. Не надо путать понятие экспорта по отношению к библиотеке (когда требуется компиляция программы) и экспорта по отношению к плагину (когда программу перекомпилировать не надо)
Я уже несколько раз объяснил про одно и то же. По твоей теории я могу написать вот такой "плагин":
0
|
22 / 22 / 2
Регистрация: 06.12.2010
Сообщений: 125
|
|
13.03.2011, 11:49 | 24 |
это просто кроссплатформенная реализация того, что мне было нужно. с примером, как это собирать. не более. для моей задачи функциональность вполне достаточна и главное: это работает. это не теория, это практика.
Добавлено через 10 минут вот в моём примере как раз и не надо. просто у меня не плагины, а библиотеки для работы с разными железяками, т.к. я хардварный программист, но не суть. смысл в том, что вызывающей программе не требуется знать ничего, кроме интерфейса. она через него вызывает виртуальные методы и это работает. и дополнительно классы "плагинов" наследуются от базового класса, который сидит в ещё одной библиотеке. таким образом реализовано дефолтное поведение: т.е. если "плагин" не реализовал какой-то метод интерфейса, то будет взят метод из базового класса. базовый класс прицеплен к программе через линковку(но не суть, на самом деле, можно его и через dlopen тащить, просто не требуется), остальное всё грузится динамически через dlopen и оно работает. под разными системами. вызываются перегруженные вирутальные методы интерфейса и т.п. никакой перекомпиляции программы не нужно, ибо она знает только о заголовочнике интрерфейса.
0
|
![]() ![]() |
|
13.03.2011, 12:02 | 25 |
Iron Bug,
1. Твой код - это обычная реализация виртуального наследования, вынесенного в динамическую библиотеку. 2. В твоём коде нет экспорта типов, потому что на Си++ это невозможно. Ты путаешь понятие экспорта типа и экспорта данных или функций. И твой размышления по части манглирования подтверждают это. 3. Твой код работает только под виндами и линуксом, а потому назвать его кроссплатформенным - несколько оптимистично. Тем более, что даже на две платформы с идеологической точки зрения разведено неправильно. Так же мне с ходу непонятно наличие специфических флагов типа -enable-runtime-pseudo-reloc или -Wl,--allow-multiple-definition, но я под виндой практически не работаю Мне не хотелось бы заниматься подробным обсуждением твоего кода, потому что в данной теме речь идёт совсем о другом. Но твой код, применительно к данной теме - это пример правильной реализации того, как должен выглядеть плагин. Потому что код плагина не выходит за рамки базового интерфейса, предоставленного для работы с плагином. Чего нет в варианте от niXman'а
0
|
22 / 22 / 2
Регистрация: 06.12.2010
Сообщений: 125
|
|
13.03.2011, 12:12 | 26 |
P.S. но в моей реализации там есть "фабрики" - функции для создания и уничтожения представителя класса, реализованные внутри библиотеки. это требуется для совместимости библиотек, собранных разными компиляторами. ибо там выделение памяти разное и прочие различия имеются. сами "фабрики" имеют фиксированные имена и также цепляются через dlsym.
Добавлено через 4 минуты другой реализации "экспорта класса", кроме экспорта его данных, создать просто нельзя. ибо разные компиляторы создают разные физические реализации объекта. поэтому мой метод - единственно возможный для использования перегруженных методов из динамических библиотек, собственно. а по части манглирования внутри совместимых на уровне кода (фактически, скомпиленных одним компилятором) библиотек возможна подгрузка куска памяти и инициализация указателей через ассемблер. это и будет чистым "импортом класса". но этот вариант мне не подходит. что касается кроссплатформенности, то мне нужны только линь и венда. по сути, линь - это моя инициатива. а так, всё написано через буст и исключительно стандартные библиотеки. так что если потребуется реализация для мака или ещё чего-то - это будет требовать минимума изменений в коде. софтина сложная и совсем без ifdef'ов тут не обойтись, увы.
0
|
![]() ![]() |
|
13.03.2011, 12:21 | 27 |
Скажем так, чтобы уметь запустить функцию из подгруженной в run-time динамической библиотеки, нужно уметь как-то найти эту функцию. Единственный для этого способ - это по символьному имени функции. Надёжным образом это можно сделать только для тех функций (и переменных), имена которых НЕ подвергаются манглированию со стороны компилятора. Таким свойством обладают только функции (и переменные), реализованные на Си (или на Си++ внутри блока extern "C"). Поэтому такая "фабрика" - это единственный способ вызвать создание объекта извне библиотеки
Не только поэтому. В языках Си и Си++ попросту нет возможности работать с динамически созданными типами. Любой тип, с которым идёт работа, должен быть виден в момент компиляции. Именно так. А способов вызвать "новый" метод (т.е. не описанный в базовом классе), коими являются set и get в варианте niXman'а, попросту нет. В цитате слово "метод" выделил жирным - лучше в таком контексте использовать слово "способ", чтобы не путать с методами языка Си++
0
|
22 / 22 / 2
Регистрация: 06.12.2010
Сообщений: 125
|
|
13.03.2011, 12:37 | 28 |
я уже десятый раз повторяю, что не единственный. я наконец-то нашла линк на ассемблерные приколы с библиотеками:
http://www.codeproject.com/KB/... ingLL.aspx это реализация для венды, но под линём тоже можно найти аналогичное решение. ведь загрузчик в этой инфе лазит и её инициализирует. значит, и мы можем это сделать. просто это геморно, архитектурно-зависимо и для моей задачи не подходит. но это работает. если очень усираться, то можно написать такие "загрузчики" для всех систем и архитектур. только нафига оно надо... а про методы именно так и есть. мы должны знать имя. на самом деле, мы можем выкопать его из виртуальной таблицы. но тут включается семантика. ну вот мы выкопали имя, деманглировали (если и это не лень), выковыряли из мангла типы переменных и их количество... а дальше-то что? как это использовать? мы ничего не знаем о семантике метода и его назначении. вот и приплыли.
0
|
![]() |
|
13.03.2011, 17:12 [ТС] | 32 |
эм...
вот ты, для того чтоб выделить память, почему используешь функцию malloc() ? вот и мне, для того чтоб использовать функционал предоставляемый плагином реализующим type1, нужно знать интерфейс к type1.
0
|
![]() ![]() |
|
13.03.2011, 17:42 | 33 |
Так интерфейсом плагина у тебя является тип i_type. Какого ж фига ты лезешь в type1? И чем использование type1, о котором приложение, вообще говоря, не должно знать, отличается от типа type_evg? Ничем. Я бы сказал это одно и то же
Добавлено через 44 секунды Я его использую потому, что он прописан в интерфейсе. А type1 в интерфейсе не прописан
0
|
![]() |
|
13.03.2011, 18:03 [ТС] | 34 |
это называется динамический полиморфизм.
т.е. я работаю на основе информации известной из интерфейса. а из интерфейса я знаю, что у некоторого типа есть некоторые методы. так же знаю, что поведение методов определено не интерфейсом, а его реализацией в плагине. Добавлено через 2 минуты Evg, по моему, мы говорим о разном...
0
|
![]() ![]() |
|
13.03.2011, 18:04 | 35 |
В интерфейсе (класс plugin_object) нет методов set и get.
Добавлено через 45 секунд Я говорю о том, как правильно делать плагины. О чём говоришь ты, я, если честно, не очень понимаю
0
|
![]() |
|
13.03.2011, 18:08 [ТС] | 36 |
потому что он базовый. единственное что реализует plugin_object, так это:
name() description() version() и наследование от этого типа, обязывает реализатора плагина, переопределить эти методы, дабы они возвращали информацию о реализуемом плагине. Добавлено через 1 минуту к примеру, я выкину тип plugin_object, и изменю функцию instance() так, чтоб она возвращала указатель на void. это будет то, о чем говоришь ты?
0
|
![]() ![]() |
|
13.03.2011, 18:18 | 37 |
Вот всё то, что здесь описано, и является интерфейсом. Типа type1, методов set и get в интерфейсе нет, а следовательно, в основной программе их никак нельзя использовать
Тогда я не очень понимаю, что в итоге-то останется? Iron Bug приводила ссылку со своей реализацией. Вот там всё было правильно: из главной программы не было никаких обращений, выходящих за рамки интерфейса. Вот возьми, к примеру, фотошоп. У него куча всяких фильтров (которые выполняют преобразование изображения). Все они реализованы в виде плагинов и могут добавляться к программе (в том числе и от сторонних разработчиков). Интерфейс плагина, в самом примитивном случае состоит из следующего: - создать экземпляр класса "плагин". Реально создастся экземпляр пронаследованного класса, но он будет отдан программе в виде указателя на базовый класс - выполнить преобразование картинки: в некий метод, определённый в базовом классе, отдаётся картинка на вход и принимается картинка на выходе. Главная программа никоим образом не знает о том, через какой класс всё это реализовано внутри плагина. Знает лишь то, что этот класс является производным классом от базового класса плагина. И главная программа пользуется лишь тем интерфейсом, который описан в базовом классе. Никаких Set и get, который в базовом классе нет
0
|
![]() |
|
13.03.2011, 18:36 [ТС] | 38 |
так интерфейсы можно наследовать!
это все правильно. но я НЕ хочу использовать такой принцип. почему что-то должно меня ограничивать?! я хочу чтоб плагин type1.so экспортировал функционал согласно интерфейсу type1. поэтому, я получив от плагина инстанс, кастую его к типу type1*, и работаю с указателем на type1. одновременно, я хочу юзать плагин type2.so. так же, получаю инстанс, и кастую его к типу type2*, и работаю с указателем на type2. все. невижу противоречий и нелогичности.
0
|
![]() ![]() |
|
13.03.2011, 18:47 | 39 |
По определению. Есть функция qsort, одним из параметров которой является указатель на функцию с конкретно заданным прототипом. И ты обязан придерживаться этого интерфейса независимо от желаний "не хочу что-то ограничивать".
А я вижу. То, что ты хочешь - НЕ является плагином. А является частью программы, оформленной в виде динамической библиотеки. Эту часть ты можешь вообще отдельным классом реализовать без каких-либо наследований от базового класса. Я могу реализовать динамическую библиотеку, экспортировать в ней функцию trampampam, в которую передаются два int'а, а она будет возвращать их сумму. Далее могу для вычисления суммы двух чисел подключать эту библиотеку через dlopen и работать через ней. Могу назвать эту библиотеку "плагином". И всё будет работать. За исключением того, что это НЕ будет являться плагином
0
|
![]() |
|
13.03.2011, 19:00 [ТС] | 40 |
наследование тут нужно чтоб обязать _создателя_плагина_ переопределить методы базового класса.
т.е. все что ты хочешь сказать, так это то, что плагины обязаны соответствовать единому интерфейсу? с этим я не спорю. оно так и есть. но мне нужна возможность использовать несколько типов интерфейсов обязывая себя знать, какой плагин, какой интерфейс реализует. детали же реализация плагина мне не известны. это мне и требуется.
0
|
13.03.2011, 19:00 | |
13.03.2011, 19:00 | |
Помогаю со студенческими работами здесь
40
Как видео с камеры отобразить в браузере без использования сторонних плагинов? Реализовать систему плагинов (модулей), каждый из которых должен работать в отдельном потоке Взаимодействие плагинов с ядром Установка плагинов cppunit + ecut в Eclipse Искать еще темы с ответами Или воспользуйтесь поиском по форуму: |