crossplatform.ru

Здравствуйте, гость ( Вход | Регистрация )

2 страниц V   1 2 >  
Ответить в данную темуНачать новую тему
> Архитектурный вопрос, Команда, NVI и вct все все... xD
lanz
  опции профиля:
сообщение 26.3.2013, 13:27
Сообщение #1


Старейший участник
****

Группа: Участник
Сообщений: 690
Регистрация: 28.12.2012
Пользователь №: 3660

Спасибо сказали: 113 раз(а)




Репутация:   8  


В общем суть такова, есть некоторое количество(N) классов, наследующих от общего предка.
У предка есть интерфейс управления для обработки команд.
Раскрывающийся текст
class Base {
    public:
        virtual void Command (QString, QVariant);
};

Естественно конкретные реализации по разному обрабатывают команды.
В первом приближении примерно так:
Раскрывающийся текст
class DerivedN {
    public:
        virtual void Command (QString, QVariant) {
            switch (QString) {
                ...
                ...
            }
        };
};

Естественно это не очень красиво, поэтому сразу второе приближение:
Раскрывающийся текст
class DerivedN {
    public:
        virtual void Command (QString, QVariant) {
            this->map[QString] (QVariant);
        };
};

В map хранятся указатели на функции члены. Естественно мне захотелось вынести все это в базовый класс.
Раскрывающийся текст
class Base {
    public:
        void Command (QString, QVariant) {
            if (this->processors.contains (QString)) {
                this->processors[QString] (this, QVariant);
            };
        };
    protected:
        typedef std::function<void (Base*, QVariant)> CommandProc;
        QMap<QString,CommandProc> processors;


Загвоздка в том что мы не можем добавлять функции-члены наследующих классов в список. Можно конечно обойтись списком свободных функций,
но интересно, как описать Map который будет содержать функции члены не только базового но и производных классов?
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
Влад
  опции профиля:
сообщение 26.3.2013, 13:37
Сообщение #2


Участник
**

Группа: Участник
Сообщений: 146
Регистрация: 20.3.2009
Из: Санкт-Петербург
Пользователь №: 627

Спасибо сказали: 46 раз(а)




Репутация:   8  


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

А еще я бы предложил подумать вот над чем: по-моему, тут как раз на схему обработки очень хорошо ляжет паттерн "Посетитель" (Visitor).
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
lanz
  опции профиля:
сообщение 26.3.2013, 14:10
Сообщение #3


Старейший участник
****

Группа: Участник
Сообщений: 690
Регистрация: 28.12.2012
Пользователь №: 3660

Спасибо сказали: 113 раз(а)




Репутация:   8  


Цитата
Вот так и описать.

Не выходит.
Цитата
error C2647: '.*' : cannot dereference a 'void (__thiscall DerivedN::* )(QVariant)' on a 'Base'
error C2440: 'newline' : cannot convert from 'Base *' to 'DerivedN *'

Так понимаю поскольку это downcast, он отказывается неявно его преобразовывать, и в принципе правильно.

Цитата
А еще я бы предложил подумать вот над чем: по-моему, тут как раз на схему обработки очень хорошо ляжет паттерн "Посетитель" (Visitor).

Посетитель не очень подходит ИМХО, поскольку у каждого наследника есть 3-15 уникальных для него команд.
Или я неправильно понимаю Посетителя или он нужен когда одну и ту же команду можно применить к объектам разного типа.
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
Влад
  опции профиля:
сообщение 26.3.2013, 14:21
Сообщение #4


Участник
**

Группа: Участник
Сообщений: 146
Регистрация: 20.3.2009
Из: Санкт-Петербург
Пользователь №: 627

Спасибо сказали: 46 раз(а)




Репутация:   8  


Цитата(lanz @ 26.3.2013, 15:10) *
Так понимаю поскольку это downcast, он отказывается неявно его преобразовывать, и в принципе правильно.

И это правильно. Но в наследнике-то ты уже точно знаешь, с объектом какого именно класса ты работаешь; поэтому что мешает downcast'ить "ручками"? Или я чего-то не понимаю? Если так, попробуй накидать код минимального примера, иллюстрирующего проблему, - попробуем разобраться "на кошках".
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
lanz
  опции профиля:
сообщение 26.3.2013, 14:59
Сообщение #5


Старейший участник
****

Группа: Участник
Сообщений: 690
Регистрация: 28.12.2012
Пользователь №: 3660

Спасибо сказали: 113 раз(а)




Репутация:   8  


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

Приложил минимальный пример.
Прикрепленные файлы
Прикрепленный файл  qscratch.zip ( 1.12 килобайт ) Кол-во скачиваний: 106
 
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
lanz
  опции профиля:
сообщение 26.3.2013, 16:16
Сообщение #6


Старейший участник
****

Группа: Участник
Сообщений: 690
Регистрация: 28.12.2012
Пользователь №: 3660

Спасибо сказали: 113 раз(а)




Репутация:   8  


Сделал так:
Раскрывающийся текст
class Base : public QObject {
    Q_OBJECT
    public:
        void Command (QString cmd, QVariant arg) {
            if (this->procs.contains (cmd)) {                
                QMetaObject::invokeMethod (this, this->procs[cmd], Q_ARG(QVariant,arg));
            };
        };
    protected:
        QMap<QString, const char*> procs;
};

Раскрывающийся текст
class Derived : public Base {
    Q_OBJECT
    public:
        Derived () {            
            this->procs["do-derived"] = "cmdDoDerivedThing";
        };
    private:
        Q_INVOKABLE    void cmdDoDerivedThing (QVariant arg) {        
            qDebug () << "hello!";
        };
};

Раскрывающийся текст
int main(int argc, char *argv[])
{
    Base *ptr = new Derived ();
    ptr->Command ("do-derived", QVariant());
    return 0;
}


Пинки, мысли вслух, философские размышления и критика приветствуются! :lol:
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
Алексей1153
  опции профиля:
сообщение 26.3.2013, 18:04
Сообщение #7


фрилансер
******

Группа: Участник
Сообщений: 2939
Регистрация: 19.6.2010
Из: Обливион
Пользователь №: 1822

Спасибо сказали: 215 раз(а)




Репутация:   34  


lanz, все названия функций - в текстовом виде, все параметры- в текстовом виде. Работать будет как часы (небольшая потеря производительности, причём вряд ли заметная - плата за универсальность)

по сути - экземпляру передаётся XML с названием функции и перечисленными атрибутами - параметрами (с их значениями)

Сообщение отредактировал Алексей1153 - 26.3.2013, 18:06
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
Iron Bug
  опции профиля:
сообщение 26.3.2013, 22:43
Сообщение #8


Профессионал
*****

Группа: Модератор
Сообщений: 1611
Регистрация: 6.2.2009
Из: Yekaterinburg
Пользователь №: 533

Спасибо сказали: 219 раз(а)




Репутация:   12  


Вот, по-быстрому написала, как в С++11 это сделать можно.
Это без Qt(я её не юзаю), но не суть. Можно любые классы и контейнеры использовать, это не принципиально.
У меня заюзан простой map и функция, принимающая int, для примера.
В GCC компилируется с опцией -std=c++11.
Раскрывающийся текст
#include <map>
#include <string>
#include <stdio.h>
#include <functional>

using namespace std;

class Base
{
public:
        typedef std::function<void(int d)> CommandProc;
        typedef map<string,CommandProc> map_type;

protected:
        map_type processors;
public:
        Base() {
                processors.insert(map_type::value_type("BaseCommand",std::bind(&Base::BaseCommand, this, std::placeholders::_1)));

        }
        void Command (string str, int value) {
                map_type::iterator iter = processors.find(str);
                if(iter != processors.end())
                        (iter->second)(value);
        };
protected:
        void BaseCommand(int value) {
                printf("this is Base::BaseCommand with argument %d\n",value);
        }
};

class Child1 : public Base
{
protected:
        void MyCommand(int value) {
                printf("this is Child1::MyCommand with argument %d\n",value);
        }
public:
        Child1() {
                processors.insert(map_type::value_type("MyCommand",std::bind(&Child1::MyCommand, this, std::placeholders::_1)));
        }
};

class Child2 : public Base
{
protected:
        void MyCommand(int value) {
                printf("this is Child2::MyCommand with argument %d\n",value);
        }
public:
        Child2() {
                processors.insert(map_type::value_type("MyCommand",std::bind(&Child2::MyCommand, this, std::placeholders::_1)));

        }
};


int main(int argc, char* argv[])
{
        Child1 test1;
        test1.Command("BaseCommand",10);
        test1.Command("MyCommand",10);
        Child2 test2;
        test2.Command("BaseCommand",20);
        test2.Command("MyCommand",20);
}


Сообщение отредактировал Iron Bug - 26.3.2013, 22:45
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
lanz
  опции профиля:
сообщение 27.3.2013, 8:39
Сообщение #9


Старейший участник
****

Группа: Участник
Сообщений: 690
Регистрация: 28.12.2012
Пользователь №: 3660

Спасибо сказали: 113 раз(а)




Репутация:   8  


Цитата
Вот, по-быстрому написала


Настоящая черная магия :lol:
Супер, спасибо!

Насколько я понимаю, в старом std/tr1 так не получится или я что-то упустил?

Цитата
lanz, все названия функций - в текстовом виде, все параметры- в текстовом виде.


Кстати насчет массивов данных, что тут лучше, base64? Нормально будет для объемов до 500Кб?
А то что то меня душит, непойму что :lol:
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
Iron Bug
  опции профиля:
сообщение 27.3.2013, 10:53
Сообщение #10


Профессионал
*****

Группа: Модератор
Сообщений: 1611
Регистрация: 6.2.2009
Из: Yekaterinburg
Пользователь №: 533

Спасибо сказали: 219 раз(а)




Репутация:   12  


Цитата(lanz @ 27.3.2013, 11:39) *
Насколько я понимаю, в старом std/tr1 так не получится или я что-то упустил?

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

тут главная проблема - это разные представления "указателей" на методы классов. на самом деле, это нифига не указатели, а сложные структуры, которые не стандартизованы и у каждого компилятора они свои и даже меняются от версии к версии внутри одного компилятора. поэтому делегирование методов комитет по стандартизации переложил на разработчиков стандартных библиотек, идущих вместе с компилятором, что вполне логично. чистого делегирования в С++ нет, зато есть связывание, которое заменяет делегирование на практике.
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение

2 страниц V   1 2 >
Быстрый ответОтветить в данную темуНачать новую тему
Теги
Нет тегов для показа


1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0




RSS Текстовая версия Сейчас: 19.4.2024, 10:41