День добрый.
Есть обертка над DBUS которая получает event. Каждый евент может содержать набор параметров, т.е. у каждого события свой набор параметров.
Хочется сделать так, что бы внешний класс мог подписаться на событие и получить этот набор данных вместе с событием.
так же хочется иметь единый способ "подписки" без введения разных API зависищих от события и типов данных.
Простой вариант это иметь массив "variant"-ов (а-ля с труктура union с типов данных в ней) и передавать его в Callback.
Здравствуйте, k55, Вы писали:
k55>Какой еще есть способ что бы уйти от "вариантов" и спрятать работу с event в обертке.
У каждого подписчика одна сигнатура? Или набор сигнатур с перегрузкой по ним?
А набор событий ограничен? У каждого события только одна сигнатура? ...
Уходить от вариантов надо в том месте, где соответствующая информация ещё не стёрта.
Например в месте подписки тебе скорей всего известна сигнатура подписчика, её можно либо интроспектировать, либо же потребовать описать явно шаблонным параметром. А может с каждым событием жёстко связанна одна сигнатура, вот это и нужно описать в системе типов.
И далее, зная сигнатуру в месте подписки, можно создать wrapper который будет уметь проверять типы пришедших параметров, после этого конвертировать их в конкретные типы и передавать подписчику. wrapper может быть в виде лямбды, которая потом сохраняется в std::function.
Здравствуйте, k55, Вы писали:
k55>Какой еще есть способ что бы уйти от "вариантов" и спрятать работу с event в обертке. k55>Ограничения: C++11.
Типизировать callback типом эвента. Заодно упростится dispatch. Ну как-то так:
struct EventA {
static constexpr auto id = 0;
template <class T>
static void process(const char* raw, T&& callback) {
// get data from char array
callback(data1, data2, data3);
}
};
std::map<int, std::vector<std::function<void (const char*)>> subs;
template<class Event, class Subscriber>
void subscribe(Subscriber sub)
{
subs[Event::id].push_back([sub](const char* raw) { Event::process(raw, sub); });
}
Здравствуйте, k55, Вы писали:
k55>День добрый. k55>Есть обертка над DBUS которая получает event. Каждый евент может содержать набор параметров, т.е. у каждого события свой набор параметров.
k55>Хочется сделать так, что бы внешний класс мог подписаться на событие и получить этот набор данных вместе с событием. k55>так же хочется иметь единый способ "подписки" без введения разных API зависищих от события и типов данных.
k55>Простой вариант это иметь массив "variant"-ов (а-ля с труктура union с типов данных в ней) и передавать его в Callback. k55>Или делать "..." в сигнатуре калбэка а подписчик сам ищет что ему надо. k55>Какой еще есть способ что бы уйти от "вариантов" и спрятать работу с event в обертке. k55>Ограничения: C++11.
Про питон уже писали. Но можно использовать контекст.
Из готовых например lua
И у контекста спрашивать, что за событие и параметры и так же их вынимать.
Более того получите возможность расширять функционал скриптами без перекомпиляции.
Здравствуйте, k55, Вы писали:
k55>День добрый. k55>Есть обертка над DBUS которая получает event. Каждый евент может содержать набор параметров, т.е. у каждого события свой набор параметров.
k55>Хочется сделать так, что бы внешний класс мог подписаться на событие и получить этот набор данных вместе с событием. k55>так же хочется иметь единый способ "подписки" без введения разных API зависищих от события и типов данных.
Параметр как набор из переменного числа key-value pair? А далее уже задача подписчика разобрать этот контейнер и начать пользоваться.
Здравствуйте, k55, Вы писали:
k55>Есть обертка над DBUS которая получает event. Каждый евент может содержать набор параметров, т.е. у каждого события свой набор параметров. k55>Хочется сделать так, что бы внешний класс мог подписаться на событие и получить этот набор данных вместе с событием. k55>так же хочется иметь единый способ "подписки" без введения разных API зависищих от события и типов данных. k55>Простой вариант это иметь массив "variant"-ов (а-ля с труктура union с типов данных в ней) и передавать его в Callback.
Не, этот вариант сложный. Простой вариант — это передавать ссылку на обёртку в callback:
class TWrapper;
struct TCallBack
{
void Callback(const string& name, const TWrapper& rDataProvider);
}
class TWrapper
{
void Subscribe(const string &name, TCallBack* callback);
TCallBack *m_callback;
void loop()
{
if (event)
{
m_callback->Callback(event->name, this);
}
}
}
void TCallBack::Callback(const string& name, const TWrapper& rDataProvider)
{
std::uint32_t param1 = rDataProvider.GetParamAsUInt32<1>(); // 1 - номер параметра
// илиauto param1 = rDataProvider.GetParamAsUInt(1);
auto param2 = rDataProvider.GetParamAsString(2);
...
//
// вообще тут можно определить любой интерфейс - это дело вкуса и усердия:
std::uint32_t param1;
std::string param2;
rDataProvider.GetParams(¶m1, ¶m2);
}
Если же делать не по простому, то TWrapper надо обернуть в класс (или отнаследоваться от интерфейса), который интерфейсом выдаёт функции получения параметров.
Сложности возникнут только если параметры — это структуры и массивы. Для них, по моему опыту, удобно делать внешние (глобальные) функции конвертирования. Что-то вроде:
k55>Простой вариант это иметь массив "variant"-ов (а-ля с труктура union с типов данных в ней) и передавать его в Callback.
для начала и так сойдёт, но в перспективе всё-таки захочется более сильного контроля над типами, поэтому я бы сделал так:
struct event_handler{
virtual on_mouse_event(int x, int y) {};
virtual on_file_system_event(file_desc f, e_file_event_data data) {}
virtual on_network_event(socket s, socket_data d) {};
};
void loop() {
if (event)
dispatch_dbus_event(event);
}
}
void dispatch_dbus_event(event) {
using std::for_each;
switch (event.type) {
case e_dbus_mouse: {
const auto info = dbus_event_to_mouse_info(event);
for_each(handlers.begin(), handlers.end(), [](event_handler& handler) { handler.on_mouse_event(info.x, info.y) });
break;
}
case e_dbus_file_system: {
const auto info = dbus_event_to_filesystem_info(event);
for_each(handlers.begin(), handlers.end(), [](event_handler& handler) { handler.on_file_system_event(info.f, info.data) });
break;
}
case e_dbus_network: {
const auto info = dbus_event_to_network_info(event);
for_each(handlers.begin(), handlers.end(), [](event_handler& handler) { handler.on_network_event(info.socket, info.data) });
break;
}
}
}
Вместо виртуальных функций можно использовать std::function, будет чуть меньше связность, но меньше и сцепленность