Модераторы: Daevaorn
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Механизм signal/slot 
:(
    Опции темы
Mal Hack
Дата 2.8.2008, 01:37 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Мудрый...
****


Профиль
Группа: Участник Клуба
Сообщений: 9926
Регистрация: 15.2.2004

Репутация: 2
Всего: 261



Есть ГУЙ, использующий Qt. Есть модули, написанные на стандартном С++, модули оформлены как namespace (классов нет).

Задача в том, что ГУЙ должен реагировать (изменять информацию в таблице), в зависимости от работы модуля. Т.е. в модуле есть долгоработающая функция, в данный момент без потока, но потом и он будет реализован. В процессе выполнения обрабатывается std::vector<struct AAA>.
Пусть в структуре есть поле status, которое характеризует состояние объекта. 
Что я хочу. Чтобы в процесс обработке этого вектра в зависимости от установления статуса объекта генерировался сигнал, который ловился бы функцией в ГУЕ, передавая параметр - номер элемента стека. Грубо говоря, хочу получить:
connect(nsModul, SIGNAL(signal1(int)), guiClass, SLOT(slot1(int));

Но, не хочу вставлять в модуль части Qt, чтоб через Qt реализовывать события... Решения нужна как для Linux'a так и для Windows.
Посмотрел WinAPI, там есть SetEvent, но что-то я не нашел как там привязать функции друг к другу.

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

Куда копать? 

СУВ.
PM ICQ   Вверх
mes
Дата 2.8.2008, 13:03 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 144
Всего: 250



Задача решается callback'ом. Вариантов много.   

1 можно на каждое событие регистрировать прокси функцию (функтор)
2. можно каждому событию назначить номер (если нужно передавать только состояние)
3. можно сделать прокси класс - базовый интерфейс будет известен модулю, а реализацию наследника выполнить в части где есть qt
4. можно описать для каждого события свой класс от обшего наследника и управлять потком событий на основе перегрузки функций и патерна визитер.
5.. 
чтоб посоветовать вариант нужно хорошо представлять что именно Вам необходимо

Добавлено через 11 минут и 25 секунд
Цитата(Mal Hack @  2.8.2008,  01:37 Найти цитируемый пост)
Что я хочу. Чтобы в процесс обработке этого вектра в зависимости от установления статуса объекта генерировался сигнал, который ловился бы функцией в ГУЕ, передавая параметр - номер элемента стека. Грубо говоря, хочу получить:
connect(nsModul, SIGNAL(signal1(int)), guiClass, SLOT(slot1(int));


http://dobrokot.nm.ru/cpp/CppMethodsCallback.html


--------------------
PM MAIL WWW   Вверх
Mal Hack
Дата 2.8.2008, 18:18 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Мудрый...
****


Профиль
Группа: Участник Клуба
Сообщений: 9926
Регистрация: 15.2.2004

Репутация: 2
Всего: 261



Цитата(mes @  2.8.2008,  14:03 Найти цитируемый пост)
чтоб посоветовать вариант нужно хорошо представлять что именно Вам необходимо

Попробую описать задачу так:
Код

nsModul::someFnc()
{
 std::vector<struct> aaa;

 for(i...)
{
aaa[i].state = STATE1;
// вот тут надо чтобы вызвалось updateRow(int element), чтобы показать, что команда пошла на выполнение
bool success = someFunction(&aaa);

aaa[i].state = (success)?STATE2:STATE3;
// вот тут надо чтобы вызвалось updateRow(int element), чтобы показать, что команда выполнилась и была ли ошибка
}

}

GuiImpl::updateRow(int element)
{
  struct stInfo info = nsModul::getinfo(element);
//Обновление QTableWidget - информации о состоянии команды.
}

При этом, msModul находится в отдельном файле и вставлять туда gui.h я не хочу. По сути, есть какой-то фреймворк, который в определеных случаях должен генерировать сигнал, мол такой-то элемент изменил свое состояние.
Гуй, должен при необходимости получать этот сигнал и обрабатывать.

Не знаю, на сколько правильно, но я представляю себе это так:
есть класс или namespace, который отвечает за связь событий модуля и слотов Гуя, при этом он не должен быть привязан к конкретному модулю или Гую. в нем должен быть std::vector указателей на фукнции сигнала и слота, и как-то он должен ловит сигнал и по нему вызывать слот, причем идальный вариантом было бы как в Qt (на подобии, скажем так), сигнал описывается как функция, но ее реализации - нет, но при emit func() поисхдит вызов события и вызывается слот, котороый связвн с сигналом через connect.

Цитата(mes @  2.8.2008,  14:03 Найти цитируемый пост)
http://dobrokot.nm.ru/cpp/CppMethodsCallback.html 

Спасибо. Сижу осмысливаю...

Цитата(mes @  2.8.2008,  14:03 Найти цитируемый пост)
 можно на каждое событие регистрировать прокси функцию (функтор)

Можно чутьподробнее...

PM ICQ   Вверх
mes
Дата 2.8.2008, 19:03 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 144
Всего: 250



Цитата(Mal Hack @  2.8.2008,  18:18 Найти цитируемый пост)
Спасибо. Сижу осмысливаю...

вот пример на основе closure  

Код

#include <cstdlib>
#include <iostream>
#include <vector>
#include "closure.h"

class Signal
{
       typedef  Closure< void (int) >    cb_t;
       typedef  std::vector < cb_t >  container_t;
    public:
       void operator << (const cb_t& cb) { m_v.push_back(cb); } // добавление делегата
                  // с удалением думаю ты сам разберешься ))                                                     
       void operator () (int  i) { for (container_t::iterator it= m_v.begin(); it!=m_v.end(); ++it) { (*it)(i); }; } // вызов в очередности добавления 
    private:

       container_t m_v;
};

class A
{
    public:

       void test1 (int i) { std::cout << "Ura !"; }
       void test2 (int i) { std::cout << "Hello "; }
       void test3 (int i) { std::cout << "World ! "; }
       void test4 (int i) { std::cout << i << std::endl; }
};

int main(int argc, char *argv[])
{

    A a;
    Signal sig;
      sig <<    CLOSURE(&a, &A::test4);
      sig <<    CLOSURE(&a, &A::test2);
      sig <<    CLOSURE(&a, &A::test1);
      sig <<    CLOSURE(&a, &A::test3);

      sig(1);
    system("PAUSE");
    return EXIT_SUCCESS;
}




--------------------
PM MAIL WWW   Вверх
Mal Hack
Дата 3.8.2008, 20:17 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Мудрый...
****


Профиль
Группа: Участник Клуба
Сообщений: 9926
Регистрация: 15.2.2004

Репутация: 2
Всего: 261



mes, у класса А public обязателен? На private работать будет?
closure не стандартное средство С++, как я понимаю? Это совершенно стороння либа?
PM ICQ   Вверх
Mal Hack
Дата 3.8.2008, 20:59 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Мудрый...
****


Профиль
Группа: Участник Клуба
Сообщений: 9926
Регистрация: 15.2.2004

Репутация: 2
Всего: 261



Сейчас имею вот такие наметки (в качестве сигнала - std::string, т.е. модуль вызывает ssEmit("SOME_STRING")):
Заголовочный файл.
Код

#include <iostream>
#include <vector>

namespace nsSignalSlots
{
    struct stSignalSlot
    {
        std::string signal;
        void (__thiscall  *ssFnc)(int, bool);
    };

    typedef std::vector<stSignalSlot> signalSlotVector_t;

    bool ssConnect(std::string, void (__thiscall *ssFnc)(int, bool));
    void ssEmit(std::string, int);
};

CPP:
Код

namespace nsSignalSlots
{
    signalSlotVector_t signalSlotList;
    enErrorCode errorCode;

    bool ssConnect(std::string signal, void (__thiscall *ssFnc)(int, bool))
    {
        signalSlotVector_t::const_iterator it = signalSlotList.begin();

        while (it++!=signalSlotList.end())
        {
            if((*it).signal.compare(signal)==0)
            {
                errorCode = ERR_SIGNAL_SLOT_SIGNAL_EXISTS;
                return false;
            }
        }

        stSignalSlot st;

        st.signal = signal;
        st.slot = ssFnc;

        signalSlotList.push_back(st);
        
        return true;
    }

    void ssEmit(std::string signal, int p1)
    {
        signalSlotVector_t::const_iterator it = signalSlotList.begin();

        while (it++!=signalSlotList.end())
        {
            if((*it).signal.compare(signal)==0)
            {
                (*(*it).slot)(p1, true);
                break;
            }
        }

        errorCode = ERR_SIGNAL_SLOT_SIGNAL_NOT_EXISTS;
    }
};

В Qt модуле пытаюсь соединить:
Код

    nsSignalSlots::ssConnect("PRINTER_COMMAND_EXECUTING", (void (__thiscall *)(int,bool))&frmManualCommandsImpl::tblCommandsSetRow);
    nsSignalSlots::ssConnect("PRINTER_COMMAND_EXECUTED", &frmManualCommandsImpl::tblCommandsSetRow);

Компилятор ругается, мол не может привести типы: 
Цитата

1>.\src\frm_manualcommandsimpl.cpp(84) : error C2440: 'type cast' : cannot convert from 'void (__thiscall frmManualCommandsImpl::* )(int,bool)' to 'void (__thiscall *)(int,bool)'
1>        There is no context in which this conversion is possible
1>.\src\frm_manualcommandsimpl.cpp(85) : error C2664: 'nsSignalSlots::ssConnect' : cannot convert parameter 2 from 'void (__thiscall frmManualCommandsImpl::* )(int,bool)' to 'void (__thiscall *)(int,bool)'
1>        There is no context in which this conversion is possible

Обозвать static'ом frmManualCommandsImpl::tblCommandsSetRow нельзя, т.к. метод становится вне класса qt, и не видит того, что надо.
Сделать ssConnect шаблоном, что приводить к void(__thiscall T:: *ssFnc)() тоже не могу, класс находится в другом модуле, следовательно шаблоны не работают. Вот как данный сабж обойти и передать имя класса еще... Можно ли вообще так сделать?
PM ICQ   Вверх
mes
Дата 3.8.2008, 22:44 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 144
Всего: 250



Цитата(Mal Hack @  3.8.2008,  20:17 Найти цитируемый пост)
mes, у класса А public обязателен? На private работать будет?


будет если класс сам будет добавлять слоты к сигналам.

[quote=Mal Hack, 3.8.2008,  20:17, post1600051]closure не стандартное средство С++, как я понимаю? Это совершенно стороння либа? 
да стороняя либа (размером в два .h файла примерно 7 кб.. скачать можно по ссылке приведенной в первом ответе)
нужна для того чтоб легко превратить метод объекта в делегат


Цитата(Mal Hack @  3.8.2008,  20:59 Найти цитируемый пост)
Компилятор ругается, мол не может привести типы: 

метод и функция две разные веши  smile 

Цитата(Mal Hack @  3.8.2008,  20:59 Найти цитируемый пост)
Обозвать static'ом frmManualCommandsImpl::tblCommandsSetRow нельзя,

 оно и понятно иначе бы и вопроса не стояло бы

Цитата(Mal Hack @  3.8.2008,  20:59 Найти цитируемый пост)
Сделать ssConnect шаблоном, что приводить к void(__thiscall T:: *ssFnc)() тоже не могу, класс находится в другом модуле, следовательно шаблоны не работают. Вот как данный сабж обойти и передать имя класса еще... Можно ли вообще так сделать?

метод без объекта смысла приводить к функции нет,  ну а если вместе то эта и делает closure  smile 

P.S

Цитата(Mal Hack @  3.8.2008,  20:59 Найти цитируемый пост)
  nsSignalSlots::ssConnect("PRINTER_COMMAND_EXECUTING",


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



Это сообщение отредактировал(а) mes - 3.8.2008, 22:45


--------------------
PM MAIL WWW   Вверх
Mal Hack
Дата 4.8.2008, 19:58 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Мудрый...
****


Профиль
Группа: Участник Клуба
Сообщений: 9926
Регистрация: 15.2.2004

Репутация: 2
Всего: 261



Цитата(mes @  3.8.2008,  23:44 Найти цитируемый пост)
да стороняя либа (размером в два .h файла примерно 7 кб.. скачать можно по ссылке приведенной в первом ответе)
нужна для того чтоб легко превратить метод объекта в делегат

А как у нее с кроссплатформенностью?

Цитата(mes @  3.8.2008,  23:44 Найти цитируемый пост)
позволит избежать кучи головной боли полученной при случайной опечатке  smile 

Почему? То, что char* будет меньше памяти кущать - понятно...
PM ICQ   Вверх
mes
Дата 4.8.2008, 21:24 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 144
Всего: 250



Цитата(Mal Hack @  4.8.2008,  19:58 Найти цитируемый пост)
А как у нее с кроссплатформенностью?

на разных компиляторах не тестил.. может не которые откажутся прогалатить шаблоны.. а от платформы ИМХО код не зависим..
буст предоставляет более гибкое решение, но она громадная либа 

можно пойти иначе:  надо будет описать абстрактный класс в модуле, как интерфейс событий а реализовать его в гуишной части 
1. и уже реализацию связывать qt шными сигналами
2. сделать прокси класс, для вливания (делегации) команд главному классу, но тогда нужно организовывать цепочку обязаностей
3. использовать двойное наследие



Цитата(Mal Hack @  4.8.2008,  19:58 Найти цитируемый пост)
позволит избежать кучи головной боли полученной при случайной опечатке  smile 
Почему? То, что char* будет меньше памяти кущать - понятно... 


да нет. дело совершено не в размере...
 Например ты сразу определишь на глаз результат действия  ? 
        "PRINTER_СОMMАND_EXECUTED" == "PRINTER_COMMAND_EXECUTED"
или "PRINTER_COMMAND_EXECUTED" == "PRINTER_COMMAND_EXECUTED "
в обоих случаях результат сравнения false. A компилятор даже не пошевелится, чтоб подказать об опечатке..

поэтому желательно использовать так const string PRINTER_COMMAND_EXECUTED ( "PRINTER_COMMAND_EXECUTED");
а чтоб не подключать хидер можно писать extern const string PRINTER_COMMAND_EXECUTED;
и тогда 
  компилятор завопит в случае опечатки,
  строка будет описана в одном месте, а доступна везде
   да и самому легче будет 




Это сообщение отредактировал(а) mes - 4.8.2008, 21:34


--------------------
PM MAIL WWW   Вверх
Mal Hack
Дата 4.8.2008, 21:36 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Мудрый...
****


Профиль
Группа: Участник Клуба
Сообщений: 9926
Регистрация: 15.2.2004

Репутация: 2
Всего: 261



Цитата(mes @  4.8.2008,  22:24 Найти цитируемый пост)
да нет. дело совершено не в размере...

Все, понял о чем ты. Приму к сведению.

Цитата(mes @  4.8.2008,  22:24 Найти цитируемый пост)
можно пойти иначе:  надо будет описать абстрактный класс в модуле, как интерфейс событий а реализовать его в гуишной части 
1. и уже реализацию связывать qt шными сигналами
2. сделать прокси класс, для вливания (делегации) команд главному классу, но тогда нужно организовывать цепочку обязаностей
3. использовать двойное наследие

Думал я над этими вариантами. Тут у меня опять-таки возникат вопрос, то же, только с другой стороны, как в этот класс nsModul::signal запихнуть...
PM ICQ   Вверх
mes
Дата 4.8.2008, 22:04 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 144
Всего: 250



Цитата(Mal Hack @  4.8.2008,  21:36 Найти цитируемый пост)
 Тут у меня опять-таки возникат вопрос, то же, только с другой стороны, 

опиши что должен отдавать модуль, какие типы сигналов, сколько их примерно , связаны ли некоторые между собой в логические группы ?
как должен быть осушествлен прием? цепочкой обязоностей? то есть каждое сообшение должно пройти по (условно) всему  гуи ?
или обработка точечная ? обьект гуи получает единичные сигналы или обрабатывает значительную группу ?



--------------------
PM MAIL WWW   Вверх
Mal Hack
Дата 5.8.2008, 15:10 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Мудрый...
****


Профиль
Группа: Участник Клуба
Сообщений: 9926
Регистрация: 15.2.2004

Репутация: 2
Всего: 261



Цитата(mes @  4.8.2008,  23:04 Найти цитируемый пост)
опиши что должен отдавать модуль, какие типы сигналов, сколько их примерно

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

Цитата(mes @  4.8.2008,  23:04 Найти цитируемый пост)
как должен быть осушествлен прием? цепочкой обязоностей? то есть каждое сообшение должно пройти по (условно) всему  гуи ?

Принцип такой. Есть событие - вызывается определенная функция (в данном случае GUI), при этом модуль не ждет ее выполнения, а продолжает работать дальше.

Цитата(mes @  4.8.2008,  23:04 Найти цитируемый пост)
или обработка точечная ?

Точечная. Т.е. надо назначить на каждый сигнал четко определенную функцию.

Цитата(mes @  4.8.2008,  23:04 Найти цитируемый пост)
обьект гуи получает единичные сигналы или обрабатывает значительную группу ?

Как модуль сгенерирует сигналы так они и должна обрабатываться.  
PM ICQ   Вверх
mes
Дата 6.8.2008, 01:00 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 144
Всего: 250



Цитата(Mal Hack @  5.8.2008,  15:10 Найти цитируемый пост)
Тип сигнала - строка, т.к. это наиболее оптимальный вариант чтобы использовать несколько модулей.

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

Цитата(Mal Hack @  5.8.2008,  15:10 Найти цитируемый пост)
Кол-во - разное, ничем не ограничено, ну т.е. внутри модуля оно фиксированное, но чтобы потом эту либу (сигналов) можно было бы использовать в другом приложении нельзя его жестко привзывать к текущему модулю.

а вот нужна ли либа если у вас и так все средства есть ? Вам ведь всего лишь не хочется связывать модуль с гуи ? кстати а кам Вам то что теперь модуль будет видеть вашу библиотеку ? 

Цитата(Mal Hack @  5.8.2008,  15:10 Найти цитируемый пост)
Принцип такой. Есть событие - вызывается определенная функция (в данном случае GUI), при этом модуль не ждет ее выполнения, а продолжает работать дальше.

чем дальше в лес тем больше дров.. ))

Для начала упростим все избавимся от зависимостей модуля и посмотрим что нам нужно
для этого определим "ответный"(callback) интерфейс  . Если длл - то нам придется использовать абстрактный класс, иначе можно обойтись просто связыванием.
Код


namespace Modul
{
   class ICallback   
  {
     public:
       void Signal1 () { doSignal1(); }
       void Signal2 () { doSignal2(); }
 
     protected:
      virtual void doSignal1 () =0; 
      virtual void doSignal2 () =0; 
   };


   class CCallbackAdapter
   {    
       public:
           void SetImpl  (ICallback * ptr) { p_ptr =ptr; }

           void Signal1 () { if (p_ptr) ptr->doSignal1(); } // можно так
           bool Signal2 () { if (!p_ptr ) return false; p_ptr->doSignal2(); return true; } // или так

       private:
           ICallBack* p_ptr;
   }  Callback; // тут лучше обернуть в функцию или синглетон (или модуль сделать классом)
   

     void f1 ()
     {
            //  .... 
            if Callback.Signal2 () ... else ...
     }
};





теперь осталось,  реализовать  наследника Modul::ICallback и зарегистрировать его в модуле..
Ну теперь вроде ничего не мешает использовать  QSignal ?



Это сообщение отредактировал(а) mes - 6.8.2008, 01:01


--------------------
PM MAIL WWW   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Правила форума "С++:Общие вопросы"
Earnest Daevaorn

Добро пожаловать!

  • Черновик стандарта C++ (за октябрь 2005) можно скачать с этого сайта. Прямая ссылка на файл черновика(4.4мб).
  • Черновик стандарта C (за сентябрь 2005) можно скачать с этого сайта. Прямая ссылка на файл черновика (3.4мб).
  • Прежде чем задать вопрос, прочтите это и/или это!
  • Здесь хранится весь мировой запас ссылок на документы, связанные с C++ :)
  • Не брезгуйте пользоваться тегами [code=cpp][/code].
  • Пожалуйста, не просите написать за вас программы в этом разделе - для этого существует "Центр Помощи".
  • C++ FAQ

Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Earnest Daevaorn

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | C/C++: Общие вопросы | Следующая тема »


 




[ Время генерации скрипта: 0.0614 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.