LINUX.ORG.RU

Плагинная архитектура

 


0

3

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

Задача: Есть некое устройство, оно на COM порт передает данные, нужно написать программу которая эти данные будет обрабатываться и взаимодействовать с остальной частью системы по сложному алгоритму.

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

Предполагаемое решение: Написать отдельную апликуху (так сказать core), к которой должен будет подключатся плагин для взаимодействия с конкретным девайсом, в данном случае который умеет читать с COM порта данные и знает как их интерпретировать.

Очень хочется, что бы написанную апликуху не нужно было пересобирать для каждого нового плагина/устройства. А что бы можно было собрать отдельно плагин, оттестировать и кинуть в прод.

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

Но я вот чет не знаю как такое сотворить на С++. Нашел два вариант. Надо делать dll, dlopen и делать Си интерфейс. И весьма необычное решение в виде делать плагин отдельным приложением, которое например через заданные пайп передает данные в основное приложение.

Мне оба не нравятся. Подкиньте каких нибудь идей пожалуйста.

★

оторое например через заданные пайп передает данные в основное приложение

Проще - лучше. Зачем велосипеды, тем более, что если пойдут какие-то левые данные их можно будет через фильтр пустить сперва.

rikardoac ★
()

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

Deleted
()

Можно общаться с плагинами через dbus, тогда и

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

само решается.

Но вариант с библиотекой каноничней для таких решений (тогда можно использовать callback-функции или сигналы для уведомления основного приложения о новых данных).

unfo ★★★★★
()

Делаешь интерфейс в core для управления загружаемыми плагинами и автопоиск. Тот же dbus в помощь. Можно поступить более аскетично - только через конфиги.

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

callback или лучше сигналы.

Chaser_Andrey ★★★★★
()

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

определи сначала, что общего будет у этих устройств, и сделай интерфейс к этому общему. только у меня подозрение, что общее будет только то, что они будут работать по rs232 (именно так это правильно называется) и тогда, если это так, тебе нужен какой-нибудь класс который скрывает в себе страшные вещи из man termios, только и всего. более того, я тебе скажу, что такое уже есть в boost::asio например. да ещё и кроссплатформенно.

nanoolinux ★★★★
()

В Qt можно реализовывать плагины (сам не пробовал, только читал у Шлее). Соответственно, для оповещение о новых данных - сигналы/слоты.

solovey ★★
()

В GLib для этих целей есть GModule. Привязка к С++ называется GLibmm. Ещё есть более высокоуровневая штуковина - libpeas.

anonymous
()
Ответ на: комментарий от nanoolinux

boost::asio::serial_port

Спасибо, не знал.

Есть не нулевая вероятность что другие устройства и вовсе не через RS232 будут работать. Так же равновероятно что других устройств и вовсе не будет. Поэтому хочется баланс между простой реализации и возможность сопровождения. Так что кроссплатформенность очень кстати.

А что на счет сигналов. Если вы про те которые в QT, то QT не будет использоваться. Если вы о системных, то я не понял как их применить сюда. Это же вообще из другой оперы.

Спасибо и за libpeas, гляну на сколько просто в освоении.

Cupper ★
() автор топика
Ответ на: комментарий от Cupper

Есть не нулевая вероятность что другие устройства и вовсе не через RS232 будут работать.

что ты тогда собираешься унифицировать? файловый дескриптор из которого будешь read(2)/write(2) делать что-ли?

Если вы про те которые в QuickTime, то QuickTime не будет использоваться

и это хорошо. не понятно как эту технологию можно применить в данном конкретном случае.

nanoolinux ★★★★
()
Ответ на: комментарий от nanoolinux

что ты тогда собираешься унифицировать? файловый дескриптор из которого будешь read(2)/write(2) делать что-ли?

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

Cupper ★
() автор топика
Ответ на: комментарий от Cupper

которые будут уже совсем другие, да и данные другим образом передавать.

я подумал, что у тебя и данные разные будут.

nanoolinux ★★★★
()

Идея:
1. Забыть про плагины и связывание кода на этапе выполнения, использовать связывание на этапе сборки. Только opensource, только хардкор!
2. Дальше классическим способом. Абстрактный класс коннектора и его конкретные реализации.
3. Для красоты и распространяемости собирать через autotools.

dmitryalexeeff ★
()
Ответ на: комментарий от dmitryalexeeff

1. Ну у меня далеко не опенсорс проект. 2. Да, на мою задачу прекрасно ложиться паттер фабричный метод, если не одно но. Ну не хочу я потом пересобирать проект. 3. Сборку делаю через CMake а пишу под Eclipse'ом

Cupper ★
() автор топика

Подкиньте каких нибудь идей пожалуйста.

Плагин в виде so с фабричной Си-функцией, которая создает C++-объект и возвращает указатель на него. Основная программа решает какой из плагинов ей нужен, делает dlopen на этот плагин, находит и вызывает фабричную функцию, и пользуется полученным объектом. Перекомпиляция не нужна, хардкорный pure C не нужен.

Manhunt ★★★★★
()
Последнее исправление: Manhunt (всего исправлений: 2)
Ответ на: комментарий от Manhunt

как выглядит фабричная Си функция? Вернее как это должно выглядеть в целом?

public_device.h
--------------------------------
class Device
{
 virtual SomeData getData() = 0;
 virtual ~Device();
};
plugin.cpp
--------------------------------
#include "public_device.h"

class MyDevice : public Device
{
...
};

Device* CretaveDevice()
{
 return MyDevice();
}
Cupper ★
() автор топика
Ответ на: комментарий от Cupper

plugin.h

class IPlugin
{
public:
	virtual ~IPlugin() {}
	virtual void DoSomething() = 0;
};

typedef IPlugin *(CreatePlugin_t)();
typedef void (DestroyPlugin_t)(IPlugin *);
main.cpp
#include <dlfcn.h>
#include <cassert>
#include "plugin.h"

int main(int argc, char **argv)
{
	assert(argc == 2);

	void *handle = dlopen(argv[1], RTLD_NOW);
	assert(handle);

	CreatePlugin_t *create = (CreatePlugin_t *) dlsym(handle, "CreatePlugin");
	assert(create);

	DestroyPlugin_t *destroy = (DestroyPlugin_t *) dlsym(handle, "DestroyPlugin");
	assert(destroy);

	IPlugin *plugin = create();
	plugin->DoSomething();
	destroy(plugin);

	dlclose(handle);

	return 0;
}
plugin_a.cpp
#include <iostream>

#include "plugin.h"

class PluginA : public IPlugin
{
public:
	PluginA() { std::cout << "PluginA construct" << std::endl; }
	~PluginA() { std::cout << "PluginA destruct" << std::endl; }
	void DoSomething() { std::cout << "PluginA::DoSomething" << std::endl; }
};

extern "C" IPlugin *CreatePlugin()
{
	return new PluginA;
}

extern "C" void DestroyPlugin(IPlugin *plugin)
{
	delete plugin;
}
plugin_b.cpp
#include <iostream>

#include "plugin.h"

class PluginB : public IPlugin
{
public:
	PluginB() { std::cout << "PluginB construct" << std::endl; }
	~PluginB() { std::cout << "PluginB destruct" << std::endl; }
	void DoSomething() { std::cout << "PluginB::DoSomething" << std::endl; }
};

extern "C" IPlugin *CreatePlugin()
{
	return new PluginB;
}

extern "C" void DestroyPlugin(IPlugin *plugin)
{
	delete plugin;
}
Сборка и запуск, для so здесь не хватает линкер скрипта
$ g++ main.cpp -ldl

$ g++ -fPIC plugin_a.cpp  -shared  -o plugin_a.so

$ g++ -fPIC plugin_b.cpp  -shared  -o plugin_b.so

$ ./a.out ./plugin_a.so
PluginA construct
PluginA::DoSomething
PluginA destruct

$ ./a.out ./plugin_b.so
PluginB construct
PluginB::DoSomething
PluginB destruct

Manhunt ★★★★★
()
Ответ на: комментарий от dmitryalexeeff

Весь NIX-мир пересобирает и не жу-жу.

4.2 Грепни слово dlopen по исходникам своего дистрибутива.

Manhunt ★★★★★
()
Ответ на: комментарий от Manhunt

вот это зачет. Спасибо, выглядит весьма впечатляюще. И без гемороя.

Cupper ★
() автор топика

Сам задавался идеей по переписыванию своего проекта под плагинную архитектуру. Попровал «с наскоку» решить это средствами QPlugin. Конечно данной теме я не уделил должного внимания, однако определенный опыт получил. Плагины гораздо труднее дебажить, честно говоря до момента «ставим бряк в коде плагина и выходим на него из дебага программы» я не дошел. Но сразу мой пыл охладил следующий момент: если в плагине имеются неудовлетворенные зависимости линковщика - он соберется, но при попытке его подключить, cast к хотя-бы QObject вернет NULL. Проблему связывания решай как хочешь. Пытался делать с помощью макросов(в т.ч. в .pro файлах), которые определяли собирать библиотеку плагином или статической библиотекой. В конце концов плюнул на это дело и вернулся к старой архитектуре. Хотя до конца эту идею я не оставил. Такой вот я неосилятор и такова моя фэйл-стори. Это я к тому, что переход к плагинной архитерктуре должен быть хорошо продуман, изанчально должны быть убраны все косяки, которые могли бы тебе помешать. В моем случае это были синглтоны не предназначенные для плагинной архитектуры и классы от которых наследовались классы в плагинах.

У меня тоже была похожая задача, получать данные из разных «устройств» и "... взаимодействовать с остальной частью системы по сложному алгоритму". Решил так: создается миниатюрная программа которая на выходе имеет протокол известный основной программе, а на входе конкретное устройство. Такой себе gateway. Такая программа может быть написана и для всех устройств вместе и для каждого по-отдельности. В силу своей миниатюрности проблем с добавлением нового устройства нет, плюс при необходимости ее код не жалко передать третьим лицам для добавления их устройства.

Извини за простыню.

anonymous
()

Блин, ну почему ЛОР заполонили двоечники?

Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от Manhunt

А ещё для Qt можно сделать переменную окружения QT_DEBUG_PLUGINS=1 и тогда будет выхлоп в консоль в чём проблема.

Merkel ★
()
Вы не можете добавлять комментарии в эту тему: топик перемещен в архив.