Russian Qt Forum

Программирование => С/C++ => Тема начата: Fregloin от Ноябрь 06, 2012, 18:27



Название: Классы-плагины
Отправлено: Fregloin от Ноябрь 06, 2012, 18:27
Хай всем. Как можно реализовать полиморфизм в следюущей ситуации:
Есть общий предок ViewItem наследованный от QGraphicsObject.
От ViewItem наследуется куча классов, задача которых отобрать на GraphicsScene различные графические объекты.
Одни объекты могут менять свой цвет, фон, другие к томуже могут менять еще что то (например светофор, у которого есть несколько ламп, каждую из которых можно подсвечивать отдельно) или програсбар, в котором можно задавать текущее значение (int) и так далее. Так вот, я смотрю пока в сторону Q_INVOCABLE и работу с метаобъектами. Все графические классы хочу оформить в виде плагинов, так как в некоторых проектах набор этих классов может меняться (сейчас через define - но это ужас и я хочу от этого уйти).
Есть еще логические объекты, которые собственно говоря и управляют графическим представлением. Хочется помаксимуму отойти к абстракции граф.объекта, но пока не знаю как сделать вызовы спецефичных для конкретных классов методов. Или в предке сделать что то типа virtual void setParam(имя параметра, сами параметры) = 0; а в наследниках уже разбирать эти параметры? Хотя как по мне это изврат... В какую сторону копать, где б что почитать в этом направлении.
Графические и логические методы создаются соотвесвующими фабричными классами, хочется что бы они лежали каждый с своем плагине и основная програ ничего лишнего об их реализации не знала. пока все варится в одном проекте.


Название: Re: Классы-плагины
Отправлено: Bepec от Ноябрь 06, 2012, 19:31
Проще и короче.

Имеются классы графические и логические.

Хочешь всё спрятать от пользователя.

Ну так сделай интерфейс для обоих типов, там сделай виртуальные методы.

Создавай свои классы, наследуясь от интерфейса. Будет стандартная основа и особенности для каждого класса.

PS а уж где они будут решать тебе - плагины или обычные фабрики в dll/проекте.


Название: Re: Классы-плагины
Отправлено: Igors от Ноябрь 07, 2012, 09:40
Есть еще логические объекты, которые собственно говоря и управляют графическим представлением. Хочется помаксимуму отойти к абстракции граф.объекта, но пока не знаю как сделать вызовы спецефичных для конкретных классов методов. Или в предке сделать что то типа virtual void setParam(имя параметра, сами параметры) = 0; а в наследниках уже разбирать эти параметры? Хотя как по мне это изврат... В какую сторону копать, где б что почитать в этом направлении.
Если говорить о данных (которые у каждого класса свои), то хост обычно хранит сериализованный экземпляр, напр в виде QByteArray, и никаких подробностей класса-плагина не знает.

С методами сложнее. Это в учебных примерах все ложится в virtual draw, а в жизни получается класс вроде и общий, но имеет специфику. Для начала неплохо зарядить так
Код
C++ (Qt)
virtual int Interface( int command, int message, void * ioData );
 
Что-то типа Вындоуз месяг. Конечно это превратится в огромный свитч - ну это все равно лучше чем латания и приведения при каждом новом изменении ф-ционала. Более продвинутое решение - экспорт/импорт "интерфейса". Хотя здесь есть опасность увлечься красивыми идеями - и загубить проект.

Хай всем.
А не лучше ли использовать простые "здравствуйте", "добрый день", можно и "привет"  :)


Название: Re: Классы-плагины
Отправлено: Fregloin от Ноябрь 07, 2012, 10:40
Добрый день  ;D
Я так и понял, что скорее всего все сведется к многочисленным switch.
Как вариант еще рассматриваю вызов методов через invokeMethod. Правда я этим еще ни разу не пользовался, на сколько они эффективны в плане производительности?


Название: Re: Классы-плагины
Отправлено: Igors от Ноябрь 07, 2012, 10:57
Как вариант еще рассматриваю вызов методов через invokeMethod. Правда я этим еще ни разу не пользовался, на сколько они эффективны в плане производительности?
По существу вопрос "насколько быстрые слот/сигнал", что обсуждалось многократно  :)

Я так и понял, что скорее всего все сведется к многочисленным switch.
Ну и что в этом такого уж плохого? На мой взгляд достоинства Qt сигналов сильно преувеличены  :)


Название: Re: Классы-плагины
Отправлено: _OLEGator_ от Ноябрь 07, 2012, 11:21
Не обязательно в switch. Можно использовать контейнеры. У каждого объекта есть тип, при подгрузке плагина, узнавать у него тип и создавать контейнер связи типа с плагином. Потом при загрузке данных, вызывать конструктор у нужного плагина. Как то так.


Название: Re: Классы-плагины
Отправлено: Fregloin от Ноябрь 07, 2012, 11:49
Можно по подробнее??


Название: Re: Классы-плагины
Отправлено: _OLEGator_ от Ноябрь 07, 2012, 11:57
Что подробнее, тут уже нужно конкретно для задачи смотреть.
Абстрактно есть классы-плагины, с интерфейсами:
- интерфейс получения типа класса.
- конструктор класса, на вход которому допустим подается бинарный поток, для десериализации.

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

Далее при загрузке данных используется только его тип, выбирается соответствующий плагин и на вход подается блок бинарных данных. Вся работа на уровне абстракций, никаких конкретных классов не используется, все только на уровне базовых классов.
Тоже самое и при добавлении нового объекта нужного типа.


Название: Re: Классы-плагины
Отправлено: Fregloin от Ноябрь 09, 2012, 13:55
Вот что получается, к примеру:
Код:
class   RAILCORESHARED_EXPORT QAbstractRailItem : public  QGraphicsObject, public QCommonRailItem
{
    Q_OBJECT
    bool                fodd;                      //признак направления элемента (слева направо или наоборот)
public:
    Q_INVOKABLE bool                isOdd()     const;
    virtual     void    executeCommand(const QString & commandName, const QVariant & params);
    virtual     QVariant commandValue(const QString & commandValue);
public slots:
    Q_INVOKABLE void                setOdd(bool OddValue);

Код:
void QAbstractRailItem::executeCommand(const QString &commandName, const QVariant &params)
{
    if(commandName=="odd")  setOdd(params.value<bool>());
}

QVariant QAbstractRailItem::commandValue(const QString &commandValue)
{
    if(commandValue=="odd") return  isOdd();
    return  QVariant();
}
У наследников довольно много параметров, все разные, получается много if()... как то совсем не красиво получается.


Название: Re: Классы-плагины
Отправлено: Igors от Ноябрь 09, 2012, 14:51
У наследников довольно много параметров, все разные, получается много if()... как то совсем не красиво получается.
А кто заставлял делать команду строкой? Просто int как 4-байтовая сигнатура, и незатейливый switch совсем неплох, напр

Код
C++ (Qt)
..
switch (command) {
 case 'LOAD':
   ...
   break;
 
 case 'SAVE':
   ...
   break;
}
 
Ну можно и мапу зарядить
Код
C++ (Qt)
typedef void (QAbstractRailItem ::* CMD_FUNC)(const QVariant & params);
QMap <int, CMD_FUNC> theCommandMap;
 
Так красивше, но отладка труднее. Вообще Ваш код смотрится, на мой взгляд, слишком "академично". Часто команда имеет 1 параметр, а иногда и вообще никаких - зачем такой жирный QVariant? Делая его константной ссылкой Вы вынуждены были добавить commandValue - не смертельно, но все-таки разрыв (чему оно соответствует если не было команды?). Гибче выглядит напр так
Код
C++ (Qt)
typedef int TResultCode;
virtual     TResultCode   executeCommand(int command, int param, const QVariant * inParams, QVariant * outParams);
 
// а для удобства вызова можно попереливать
virtual     TResultCode   executeCommand(int command, int param, const QVariant & inParams, QVariant & outParams)
{
return executeCommand(command, param, &inParams, &outParams);
}
 
В первую очередь нужен код возврата (команда прошла или нет). Также спорно нужен ли QVariant вообще - создавать структуры для команд так или иначе придется


Название: Re: Классы-плагины
Отправлено: Fregloin от Ноябрь 09, 2012, 15:06
Да дело в том, что хочется уйти от int. Раньше почти так и было, но во первых, в будущем возможно кто то плагигы будет дописыват вместо меня, плюс строки в гибкости. В интах лугече запутаться чем в строках. На счёт QVariant - хочется отойти от каких либо конкретных структур, что бы программа не знала о внутренней реализации каждого плагина, а вызывала все через вирутальный метод например так

someItem->setParam("color.main",someColor);

QVariantMap map;
map["color"] = Qt::red;
map["lamp_index"]=12;
someItem->setParam("color.lamp",map);

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


Название: Re: Классы-плагины
Отправлено: Igors от Ноябрь 09, 2012, 15:41
плюс строки в гибкости. В интах лугече запутаться чем в строках.
Но минус в скорости и, еще важнее, в отладке. Использование 4-х байтовых сигнатур - стандартная практика на Mac, стоит попробовать, может понравится. 'LAMP', 'COLR' - достаточно понятно.

На счёт QVariant - хочется отойти от каких либо конкретных структур, что бы программа не знала о внутренней реализации каждого плагина, а вызывала все через вирутальный метод например так
Ну какие-то структуры так или иначе должны быть известны как плагину так и хосту, иначе как они могут обмениваться данными? Одеть все в QVariant эту проблему все равно не решает.

map["lamp_index"]=12;
Смысл понятен, не надо рихтовать структуры при всяком изменении. К сожалению, за эту гибкость придется платить - так оба (хост и плагин) просто помнят чему соответствует "lamp_index" в ихнем коде. На приемной стороне уже не получится так лихо
Код
C++ (Qt)
lamp.mIndex = map["lamp_index"];  // а его-то в мапе не было
 
И поиски "почему не работает" (а ошибок нет) могут быть долгими. Придется проверяться, теряя элегантность  :)

someItem->setParam("color.main",someColor);
Код возврата: не нужно надеяться что все команды выполнятся без ошибок.


Название: Re: Классы-плагины
Отправлено: Fregloin от Декабрь 28, 2012, 12:26
все оказалось намного проще! воспользовался Q_PROPERTY!  ;D