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
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.