Russian Qt Forum

Qt => Кладовая готовых решений => Тема начата: m_ax от Февраль 17, 2011, 17:35



Название: Частный случай механизма сигнал-слот
Отправлено: m_ax от Февраль 17, 2011, 17:35
Решил тут для себя разобраться в реализации сигнально-слотового механизма, основаного на шаблонах.
Выкладываю то что успел накатать (описание ниже):

файл signal_slot.h
Код
C++ (Qt)
#ifndef SIGNAL_SLOT_H
#define SIGNAL_SLOT_H
 
#include <list>
 
template <class T_receiver, class T_return, class T_arg>
class Slot
{
public:
   typedef T_return (T_receiver::*function_type_1)(T_arg);
   typedef T_return (T_receiver::*function_type_2)(const T_arg &);
   typedef T_return (T_receiver::*function_type_3)(T_arg) const;
   typedef T_return (T_receiver::*function_type_4)(const T_arg &) const;
 
 
   Slot(T_receiver *obj, function_type_1 slot);
   Slot(T_receiver *obj, function_type_2 slot);
   Slot(T_receiver *obj, function_type_3 slot);
   Slot(T_receiver *obj, function_type_4 slot);
 
   ~Slot();
 
   T_return operator() (const T_arg &arg);
 
private:
   Slot();
   function_type_1 _slot_1;
   function_type_2 _slot_2;
   function_type_3 _slot_3;
   function_type_4 _slot_4;
 
   T_receiver *_obj;
   int _index;
};
 
 
template <class T_receiver, class T_return, class T_arg>
inline Slot<T_receiver, T_return, T_arg>::Slot()
{
}
 
template <class T_receiver, class T_return, class T_arg>
inline Slot<T_receiver, T_return, T_arg>::Slot(T_receiver *obj, function_type_1 slot)
{
   _obj = obj;
   _slot_1 = slot;
   _index = 1;
}
 
template <class T_receiver, class T_return, class T_arg>
inline Slot<T_receiver, T_return, T_arg>::Slot(T_receiver *obj, function_type_2 slot)
{
   _obj = obj;
   _slot_2 = slot;
   _index = 2;
}
 
template <class T_receiver, class T_return, class T_arg>
inline Slot<T_receiver, T_return, T_arg>::Slot(T_receiver *obj, function_type_3 slot)
{
   _obj = obj;
   _slot_3 = slot;
   _index = 3;
}
 
template <class T_receiver, class T_return, class T_arg>
inline Slot<T_receiver, T_return, T_arg>::Slot(T_receiver *obj, function_type_4 slot)
{
   _obj = obj;
   _slot_4 = slot;
   _index = 4;
}
 
template <class T_receiver, class T_return, class T_arg>
inline T_return Slot<T_receiver, T_return, T_arg>::operator ()(const T_arg &arg)
{
   if (_index == 1)
      return (_obj->*_slot_1)(arg);
   if (_index == 2)
       return (_obj->*_slot_2)(arg);
   if (_index == 3)
       return (_obj->*_slot_3)(arg);
 
   return (_obj->*_slot_4)(arg);
}
 
 
template <class T_receiver, class T_return, class T_arg>
class Signal
{
public:
   typedef T_return (T_receiver::*function_type_1)(T_arg);
   typedef T_return (T_receiver::*function_type_2)(const T_arg &);
   typedef T_return (T_receiver::*function_type_3)(T_arg) const;
   typedef T_return (T_receiver::*function_type_4)(const T_arg &) const;
 
   Signal();
   ~Signal();
 
   void operator()(const T_arg &arg);
 
   void connect(T_receiver &obj, function_type_1 slot);
   void connect(T_receiver &obj, function_type_2 slot);
   void connect(T_receiver &obj, function_type_3 slot);
   void connect(T_receiver &obj, function_type_4 slot);
 
private:
   std::list<Slot<T_receiver, T_return, T_arg>* > _list;
   typedef typename std::list<Slot<T_receiver, T_return, T_arg>* >::iterator _iterator;
 
};
 
template <class T_receiver, class T_return, class T_arg>
inline Signal<T_receiver, T_return, T_arg>::Signal()
{
}
 
template <class T_receiver, class T_return, class T_arg>
inline Signal<T_receiver, T_return, T_arg>::~Signal()
{
   _list.clear();
}
 
template <class T_receiver, class T_return, class T_arg>
inline void Signal<T_receiver, T_return, T_arg>::connect(T_receiver &obj, function_type_1 slot)
{
   _list.push_back(new Slot<T_receiver, T_return, T_arg>(&obj, slot));
}
 
template <class T_receiver, class T_return, class T_arg>
inline void Signal<T_receiver, T_return, T_arg>::connect(T_receiver &obj, function_type_2 slot)
{
   _list.push_back(new Slot<T_receiver, T_return, T_arg>(&obj, slot));
}
 
template <class T_receiver, class T_return, class T_arg>
inline void Signal<T_receiver, T_return, T_arg>::connect(T_receiver &obj, function_type_3 slot)
{
   _list.push_back(new Slot<T_receiver, T_return, T_arg>(&obj, slot));
}
 
template <class T_receiver, class T_return, class T_arg>
inline void Signal<T_receiver, T_return, T_arg>::connect(T_receiver &obj, function_type_4 slot)
{
   _list.push_back(new Slot<T_receiver, T_return, T_arg>(&obj, slot));
}
 
template <class T_receiver, class T_return, class T_arg>
inline void Signal<T_receiver, T_return, T_arg>::operator ()(const T_arg &arg)
{
   for (_iterator it = _list.begin(); it != _list.end(); it++)
       (*it)->operator()(arg);
}
 
#endif // SIGNAL_SLOT_H
 


Краткое описание.

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

Класса Slot
Он фактически представляет из себя функторный класс, который биндит указатель на метод класса, который (метод) будет реализовывать слот.
Этот класс пользователь в своём коде явно нигде не использует. Он введён для удобства и используется классом Signal

Класс Signal объявлен как
Код
C++ (Qt)
template <class T_receiver, class T_return, class T_arg> class Signal
 
где
T_receiver - это класс получатель
T_return - тип возвращаемого аргумента функции (слота)
T_arg - тип аргумента (слота и сигнала, соответственно)

Используется это так:

файл main.cpp
Код
C++ (Qt)
#include <iostream>
#include <string>
 
#include "signal_slot.h"
 
 
template <class T>
class Sender
{
public:
   Sender() {}
   Signal<T, void, std::string> signal;
 
   void run() {
       signal("Hello word!");
   }
};
 
class Receiver
{
public:
   Receiver(){}
 
   void mySlot1(const std::string &str) {
       std::cout << "mySlot1 received a signal: " << str << std::endl;
   }
   void mySlot2(std::string str) {
       std::cout << "mySlot2 receiver a signal: " << str << std::endl;
   }
 
};
 
 
int main()
{
   Sender<Receiver> sender;
   Receiver receiver;
 
   sender.signal.connect(receiver, &Receiver::mySlot1);
   sender.signal.connect(receiver, &Receiver::mySlot2);
 
   sender.run();
 
   return 0;
}
 

Недостатком такого подхода является то, что общатся между собой могут лишь объекты тех классов, что указаны (в параметрах шаблона) при их создании. Т.е. в данном случае, объекты класса Receiver могут получать сигналы от объектов класса Sender. Но объекты класса Sender не могут слать сигналы объектам другого класса, если при определении класа Sender не указать дополнительный параметр шаблона и соответствующий сигнал с этим параметром.

Я пока не раскурил, как эта проблема решена, например в boost или в libsigc++  :(
Если кто подскажет, скажу спасибо)

Далее хотелось бы реализовать метод disconnect, а также при коннекте проверять, есть ли в списке зарегистрированных слотов тот, который коннектится. Сейчас, если дважды подрят вызвать connect то в список будет содержать два одинаковых слота, что не есть гут.
Как это сделать пока думаю над этим..

Ещё такой вопрос: кто знает как присвоить указатель на метод одного класса к указателю на метод другого класса? И хорошо ли это?

В общем делаю это,  в первую очередь, чтоб разобраться со всем этим делом))   

архив с проектом прикреплён   




Название: Re: Частный случай механизма сигнал-слот
Отправлено: GreatSnake от Февраль 17, 2011, 17:43
А чем boost::signal не устроил?


Название: Re: Частный случай механизма сигнал-слот
Отправлено: m_ax от Февраль 17, 2011, 17:50
А чем boost::signal не устроил?
Вопрос носит чисто теоретический характер)


Название: Re: Частный случай механизма сигнал-слот
Отправлено: brankovic от Февраль 18, 2011, 00:59
Недостатком такого подхода является то, что общатся между собой могут лишь объекты тех классов, что указаны (в параметрах шаблона) при их создании.
 
Ещё такой вопрос: кто знает как присвоить указатель на метод одного класса к указателю на метод другого класса? И хорошо ли это?

нет, это очень плохо, в частности из-за гемороя с указателями на виртуальные методы. Но есть другой путь. Можно хранить связанную пару объект-метод в виде boost::function. При этом boost::function требует только возвращаемый тип и типы аргументов, но не говорит какие классы сбайнженные с методами можно в него положить. Собственно библиотека сигналов это boost::function + накрутки. Как написать boost::function это отдельный вопрос. Накрутки это на первый взгляд не страшно, но на самом деле в них вся сложность. На тему можно почитать rationale в документации boost::signal, там очень наглядно разобрано что, как и зачем. После прочтения мне, например, расхотелось самому углубляться :).


Название: Re: Частный случай механизма сигнал-слот
Отправлено: m_ax от Февраль 18, 2011, 14:16
Цитировать
нет, это очень плохо, в частности из-за гемороя с указателями на виртуальные методы.
Ну вот, так и знал))

Ладно, будем думать дальше)


Название: Re: Частный случай механизма сигнал-слот
Отправлено: brankovic от Февраль 18, 2011, 14:21
Цитировать
нет, это очень плохо, в частности из-за гемороя с указателями на виртуальные методы.
Ну вот, так и знал))

Ладно, будем думать дальше)

boost::function


Название: Re: Частный случай механизма сигнал-слот
Отправлено: m_ax от Февраль 18, 2011, 17:14
Цитировать
нет, это очень плохо, в частности из-за гемороя с указателями на виртуальные методы.
Ну вот, так и знал))

Ладно, будем думать дальше)

boost::function

Спасибо)) Я кажысь раскурил как это всё можно реализовать)

Сейчас накатаю код, выложу


Название: Re: Частный случай механизма сигнал-слот
Отправлено: m_ax от Февраль 18, 2011, 19:28
Вроде всё работает  :)

Теперь всё хорошо и работает так:

main.cpp
Код
C++ (Qt)
#include <iostream>
#include <string>
#include "signal_slot.h"
 
class A
{
public:
   A() {}
   signal<void, std::string> my_signal;
 
   void run() {
       my_signal("Hello Word!");
   }
};
 
class B
{
public:
   B() {}
 
   void slot1(const std::string &str) {
       std::cout << "slot1, signal - " << str << std::endl;
   }
   void slot2(std::string str) const {
       std::cout << "slot2, signal - " << str << std::endl;
   }
};
 
int main()
{
 
   A a;
   B b;
 
   connect(&b, &B::slot1, &a.my_signal);
   connect(&b, &B::slot2, &a.my_signal);
 
   a.run();
 
   return 0;
}
 
Теперь всё как в Qt и только с использованием шаблонов  8)

Функция connect создаёт соединение между слотом объекта класса b и сигналом объекта a.
Сам сигнал представляет из седя шаблонный класс
template <class T_return, class T_arg> class signal
где
T_return - тип возвращаемого аргумента
T_arg - тип передаваемого аргумента

Если нужно чтобы ваш клас умел испускать сигналы необходимо просто включить класс сигнала в определение класса, как в примере:
Код
C++ (Qt)
class A
{
public:
   A() {}
/*
Объявляем сигнал, который будет соеденён с функцией, которая возвращает void
и принимает один аргумент std::string
*/

   signal<void, std::string> my_signal;  
 
/*
В той функции, где необходимо вызвать сигнал, просто вызываем сигнал как обычную функцию  
*/
                                   
   void run() {
       my_signal("Hello Word!");
   }
};
 


Сама реализация механизма сигнал-слот:
Код
C++ (Qt)
#ifndef SIGNAL_SLOT_H
#define SIGNAL_SLOT_H
 
#include <list>
 
template <class T_return, class T_arg>
class base_signal
{
public:
   base_signal() {}
   virtual ~base_signal() {}
   virtual T_return operator() (const T_arg &) = 0;
};
 
template <class T_receiver, class T_return, class T_arg>
class manager_connection;
 
template <class T_return, class T_arg>
class signal
{
public:
   signal() {}
   ~signal() {
       for (_iterator it = _list.begin(); it != _list.end(); it++) {
           delete *it;
       }
       _list.clear();
   }
 
   T_return operator() (const T_arg &arg) {
       for (_iterator it = _list.begin(); it != _list.end(); it++) {
           (*it)->operator()(arg);
       }
   }
 
   template <class T1, class T2, class T3> friend class manager_connection;
 
private:
   std::list<base_signal<T_return, T_arg>* > _list;
   typedef typename std::list<base_signal<T_return, T_arg>* >::iterator _iterator;
 
   void connect(base_signal<T_return, T_arg> *_signal) {
       _list.push_back(_signal);
   }
};
 
template <class T_receiver, class T_return, class T_arg>
class manager_connection : public base_signal<T_return, T_arg>
{
public:
   typedef T_return (T_receiver::*function_type_1)(T_arg);
   typedef T_return (T_receiver::*function_type_2)(const T_arg &);
   typedef T_return (T_receiver::*function_type_3)(T_arg) const;
   typedef T_return (T_receiver::*function_type_4)(const T_arg &) const;
 
   manager_connection(T_receiver *obj, function_type_1 slot, signal<T_return, T_arg> *s) {
       _receiver = obj;
       _slot_1 = slot;
       _index = 1;
       s->connect(this);
   }
 
   manager_connection(T_receiver *obj, function_type_2 slot, signal<T_return, T_arg> *s) {
       _receiver = obj;
       _slot_2 = slot;
       _index = 2;
       s->connect(this);
   }
 
   manager_connection(T_receiver *obj, function_type_3 slot, signal<T_return, T_arg> *s) {
       _receiver = obj;
       _slot_3 = slot;
       _index = 3;
       s->connect(this);
   }
 
   manager_connection(T_receiver *obj, function_type_4 slot, signal<T_return, T_arg> *s) {
       _receiver = obj;
       _slot_4 = slot;
       _index = 4;
       s->connect(this);
   }
 
   ~manager_connection() {
   }
 
   T_return operator()(const T_arg &arg) {
       if (_index == 1)
           return (_receiver->*_slot_1)(arg);
       if (_index == 2)
           return (_receiver->*_slot_2)(arg);
       if (_index == 3)
           return (_receiver->*_slot_3)(arg);
       if (_index == 4)
           return (_receiver->*_slot_4)(arg);
 
       return T_return();
   }
 
private:
   T_receiver *_receiver;
   function_type_1 _slot_1;
   function_type_2 _slot_2;
   function_type_3 _slot_3;
   function_type_4 _slot_4;
   int _index;
   manager_connection() : _receiver(0), _index(0) {}
   manager_connection(const manager_connection &) {}
 
};
 
 
template <class T_receiver, class T_return, class T_arg>
void connect(T_receiver *obj, T_return (T_receiver::*slot)(T_arg), signal<T_return, T_arg> *s) {
   new manager_connection<T_receiver, T_return, T_arg>(obj, slot, s);
}
 
template <class T_receiver, class T_return, class T_arg>
void connect(T_receiver *obj, T_return (T_receiver::*slot)(const T_arg &), signal<T_return, T_arg> *s) {
   new manager_connection<T_receiver, T_return, T_arg>(obj, slot, s);
}
 
template <class T_receiver, class T_return, class T_arg>
void connect(T_receiver *obj, T_return (T_receiver::*slot)(T_arg) const, signal<T_return, T_arg> *s) {
   new manager_connection<T_receiver, T_return, T_arg>(obj, slot, s);
}
 
template <class T_receiver, class T_return, class T_arg>
void connect(T_receiver *obj, T_return (T_receiver::*slot)(const T_arg &) const, signal<T_return, T_arg> *s) {
   new manager_connection<T_receiver, T_return, T_arg>(obj, slot, s);
}
 
#endif // SIGNAL_SLOT_H
 

Архив с проектом прикреплён.


Название: Re: Частный случай механизма сигнал-слот
Отправлено: brankovic от Февраль 18, 2011, 19:40
Вроде всё работает  :)

Теперь всё хорошо и работает так:

Уже симпатично выглядит. А что будет если объект 'b' разрушится (delete например), а потом 'a' испустит сигнал?


Название: Re: Частный случай механизма сигнал-слот
Отправлено: m_ax от Февраль 18, 2011, 19:48
Вроде всё работает  :)

Теперь всё хорошо и работает так:

Уже симпатично выглядит. А что будет если объект 'b' разрушится (delete например), а потом 'a' испустит сигнал?

Известно что: грохинг программы  ;D

Я как раз курю на эту тему)) Если есть предложения буду рад выслушать.


Название: Re: Частный случай механизма сигнал-слот
Отправлено: Igors от Февраль 18, 2011, 19:48
m_ax, мои замечания - чисто субъективны, я не пытаюсь о чем-то авторитетно судить, делать выводы и.т.п.

Если возникнет вопрос "пользоваться этим или нет", то мой однозначный ответ "нет", и (возможно) я буду здесь совсем не один. Наверняка Вы сделали все правильно, но каждый template требует как-то "настроить мозги". То есть "врубиться" конечно можно, но это обходится недешево, это надо держать в памяти и не забывать. Когда программист сосредоточен на прикладном аспекте - это особенно болезненно. Нет желанной "простоты использования".

Еще раз - это мое субъективное мнение, не более того  :)


Название: Re: Частный случай механизма сигнал-слот
Отправлено: m_ax от Февраль 18, 2011, 20:03
m_ax, мои замечания - чисто субъективны, я не пытаюсь о чем-то авторитетно судить, делать выводы и.т.п.

Если возникнет вопрос "пользоваться этим или нет", то мой однозначный ответ "нет", и (возможно) я буду здесь совсем не один. Наверняка Вы сделали все правильно, но каждый template требует как-то "настроить мозги". То есть "врубиться" конечно можно, но это обходится недешево, это надо держать в памяти и не забывать. Когда программист сосредоточен на прикладном аспекте - это особенно болезненно. Нет желанной "простоты использования".

Еще раз - это мое субъективное мнение, не более того  :)

Отчасти я Вас понимаю, но с другой стороны не писать же с нуля весь инструментарий? Я например, как и многие другие использую Qt и я совсем не парюсь по поводу сколько там у них template или ещё чего. Я в исходники то их от силы раза 3-4 заглядывал)) Мне остаётся только довериться разработчикам)

Или Вы что-то другое имеете ввиду?
Просто в последним варианте, при использовании этого механизма никаких явных template писать уже не нужно, говорю ж всё максимально приближено к Qt варианту сигнал-слот.
Есть, тут правда ещё недопиленные места, но это детали..
 


Название: Re: Частный случай механизма сигнал-слот
Отправлено: Igors от Февраль 18, 2011, 20:20
Просто в последним варианте, при использовании этого механизма никаких явных template писать уже не нужно, говорю ж всё максимально приближено к Qt варианту сигнал-слот.
Ну а зачем Вы выбрали тему которая уже известна, более того (в каком-то смысле "авторитетна")? Это, на мой взгляд, неразумно/непрактично. Ну посидеть пару вечерков конечно можно - но на этом дело кончится.

Да и что "такого уж хорошего" в ихней схеме слот/сигнал? Везде есть нормальное "sendEvent" (выполнить немедленно) и "postEvent" (засунуть в очередь). Нормальный программист легко этим пользуется, зачем это накручивать в виде MOC компилятора? В результате мы часто видим "месиво из сигналов" вместо нормального, логичного текста.


Название: Re: Частный случай механизма сигнал-слот
Отправлено: m_ax от Февраль 18, 2011, 20:43
Просто в последним варианте, при использовании этого механизма никаких явных template писать уже не нужно, говорю ж всё максимально приближено к Qt варианту сигнал-слот.
Ну а зачем Вы выбрали тему которая уже известна, более того (в каком-то смысле "авторитетна")? Это, на мой взгляд, неразумно/непрактично. Ну посидеть пару вечерков конечно можно - но на этом дело кончится.

Да и что "такого уж хорошего" в ихней схеме слот/сигнал? Везде есть нормальное "sendEvent" (выполнить немедленно) и "postEvent" (засунуть в очередь). Нормальный программист легко этим пользуется, зачем это накручивать в виде MOC компилятора? В результате мы часто видим "месиво из сигналов" вместо нормального, логичного текста.

Ну во-первых лично моё имхо, механизм сигнал-слот - это очень удобная штука. И реализация в Qt это не единственная её реализация. Я не хочу сказать, что в Qt с помощью moc - это круто, напротив, вызов обходится дороже, чем например в схеме сигнал-слот в boost.

Во-вторых я предложил вариант сигнал-слот отличный от реализованного в Qt и отличный от реализации в boost::signal. (как правильно заметил brankovic в boost всё завязано на boost::function)
Это своего рода ещё одна вариация на тему)) И я считаю имеет право быть, хотя бы как объект теоретического исследования)
Короче,
Нет ничего практичнее хорошей теории))


Название: Re: Частный случай механизма сигнал-слот
Отправлено: m_ax от Февраль 18, 2011, 22:38
Вроде всё работает  :)

Теперь всё хорошо и работает так:

Уже симпатично выглядит. А что будет если объект 'b' разрушится (delete например), а потом 'a' испустит сигнал?

В общем самое простое решение я вижу так:
Нужно разорвать соединение, если объект b разрушится.
Это можно сделать, если завернуть объект b в умный указатель, который занулит себя при уничтожении b. А поскольку этот указатель хранится в manager_connection
то перед тем как вызывать operator()() проверять его на валидность.
Как то так.. Сейчас попробую код накатать..   


Название: Re: Частный случай механизма сигнал-слот
Отправлено: brankovic от Февраль 18, 2011, 23:26
И реализация в Qt это не единственная её реализация. Я не хочу сказать, что в Qt с помощью moc - это круто, напротив, вызов обходится дороже, чем например в схеме сигнал-слот в boost.

У схемы qt есть и преимущества, но в контексте программирования их бессмысленно обсуждать. Потому что схема qt не програмисстская -- это бизнес-схема. Во времена создания qt не было _ни одного_ компилятора, который поддерживал бы шаблоны в полном объёме, и глючили они все по-разному. Кросс-платформенная библиотека основанная на шаблонах была бы обречена. Оставались макросы. Тролли решили, что moc выделит их из серой толпы макросовых фреймворков, и были по-своему правы.

Во-вторых я предложил вариант сигнал-слот отличный от реализованного в Qt и отличный от реализации в boost::signal.

чем ближе ваша реализация подойдёт к функционалу boost::signal, тем более похожей на него она станет. Потом они сольются и станет всё равно, что использовать. И тогда, возможно, вы станете использовать boost::signal.

Да и что "такого уж хорошего" в ихней схеме слот/сигнал? Везде есть нормальное "sendEvent" (выполнить немедленно) и "postEvent" (засунуть в очередь).

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

И я считаю имеет право быть, хотя бы как объект теоретического исследования)
Короче,
Нет ничего практичнее хорошей теории))

Жил некогда Чжу, который учился убивать драконов.. :)

Но упражнение в любом случае полезное.


Название: Re: Частный случай механизма сигнал-слот
Отправлено: m_ax от Февраль 19, 2011, 20:13
Дописал)

Теперь соединение автоматически разрывается, если один из объектов уничтожается.
Пришлось для этого писать свой умный указатель (smart_ptr), который следит за всем этим делом..
Зато получаем все плюшки автоматического удаления объектов  ;D

Все классы помещены в пространство имён ssc (от signal slot connection)
Базовые два класса это:
1) signal
2) smart_ptr

Используется так:
Код
C++ (Qt)
#include <iostream>
#include <string>
#include "signal_slot.h"
 
class A
{
public:
   A() {
       std::cout << "A()" << std::endl;
   }
   ~A() {
       std::cout << "~A()" << std::endl;
   }
   ssc::signal<void, std::string> my_signal;
 
   void run() {
       my_signal("Hello Word!");
   }
};
 
class B
{
public:
   B() : _i(0) {
       std::cout << "B()" << std::endl;
   }
   B(int i) : _i(i) {
       std::cout << "B(int  i = " << _i << ")" << std::endl;
   }
   ~B() {
       std::cout << "~B();  i = " << _i << std::endl;
   }
 
   void slot1(const std::string &str) {
       std::cout << "slot1, signal - " << str << "  i = " << _i << std::endl;
   }
   void slot2(std::string str) const {
       std::cout << "slot2, signal - " << str << "  i = " << _i << std::endl;
   }
private:
   int _i;
};
 
int main()
{
   ssc::smart_ptr<A> a = new A;
   // A *a = new A;        можно и так, но придётся удалять руками
 
   //{  если раскомментировать, соединения автоматически разорвуться
 
       ssc::smart_ptr<B> p(new B(12));
   /*  B *b = new B(12)       а вот так можно, но в connect мы его не сможем засунуть
        поскольку connect хочет чтоб b был умным))    
   */

       connect(a->my_signal, p, &B::slot1);
       connect(a->my_signal, p, &B::slot2);
 
   //} см. выше
 
   a->run();
 
   return 0;
}
 


Исходники прилогаются. Прошу потестить, кому не лень. Возможно я где нить накасячил)
Конструктивная критика приветствуется)

 


Название: Re: Частный случай механизма сигнал-слот
Отправлено: brankovic от Февраль 20, 2011, 00:13
1) signal
2) smart_ptr

smart_ptr просто ужасный:

1. деструктор и operator= должны вызывать общий метод destroy, чтобы их поведение было консистентным (в operator= вы забыли *valid=0, кроме того is_killer обнуляется и объект никто не удалит -- утечка памяти)

2. smart_ptr это симметричный объект, не может быть один "киллер", потому что если он погибнет раньше остальных, у других ptr-а не станет. Каждый владелец ptr может оказаться последним, "киллером".


Название: Re: Частный случай механизма сигнал-слот
Отправлено: brankovic от Февраль 20, 2011, 00:22
1) signal
2) smart_ptr

По сигналу: теперь ресивера _обязательно_ заворачивать в smart_ptr?


Название: Re: Частный случай механизма сигнал-слот
Отправлено: m_ax от Февраль 20, 2011, 01:06
1) signal
2) smart_ptr

По сигналу: теперь ресивера _обязательно_ заворачивать в smart_ptr?
Теперь обязательно, для того что бы при уничтожении receiver а разрывалось и соответствующее соединение.
Хотя есть и другой вариант как это сделать без использования smart_ptr.

1) signal
2) smart_ptr

smart_ptr просто ужасный:

1. деструктор и operator= должны вызывать общий метод destroy, чтобы их поведение было консистентным (в operator= вы забыли *valid=0, кроме того is_killer обнуляется и объект никто не удалит -- утечка памяти)
Постараюсь объяснить логику этого указателя (для краткости в дальнейшем просто ptr):
ptr может быть либо киллером либо нет. У киллера есть право убить объект. Т.е. право убить объект принадлежит тому, кто первый им завладел. пример:
Код
C++ (Qt)
smart_ptr<B> p1(new B); // p1 - киллер и убъёт объект
smart_ptr<B> p2 = p1;
/* p2 - не имееет права убивать объект, он просто копирует указатель на
на него. флаг _is_killer = false. Сам объект не клонируется.  */

 
Другая ситуация:
Код
C++ (Qt)
smart_ptr<B> p1(new B); // p1 - киллер и убъёт объект
smart_ptr<B> p2(new B); // p2 - тоже киллер!
// Внимание фокус!
p2 = p1; /* Поскольку p2 - киллер он вначале убъёт объект на который ссылается,
затем скопирует указатель на объект на который ссылается p1.
Далее он сбросит флаг _is_killer = false и лишится права удалять объект,
поскольку им уже владел до него p1 и владеет.
Флаг _valid он скопирует также, и соответственно _valid == true, так что пока всё хорошо))  */

// А вот здесь:
 
p1 = p2; /* Ничего не произойдёт, поскольку  operatop=() сначало проверяет не ссылаются
ли они на один и тот же объект. И если да (а в данном случае так и есть)
то return *this и всё.*/

 

Т.е. идея в том, что указателей на объект может быть много, но среди них только у одного есть право его убить. Каждый указатель может изменять данные объекта, но не клонировать его при этом. После того, как киллер  убъёт объект, все остальные смогут узнать об этом, за счёт флага _valid, который будет равен *_valid == false.
Кстати, вот он так и будет висеть и никто его никогда не удалит, но это 1 висячий байт... За то польза))

Цитировать
2. smart_ptr это симметричный объект, не может быть один "киллер", потому что если он погибнет раньше остальных, у других ptr-а не станет. Каждый владелец ptr может оказаться последним, "киллером".

Если киллер умрёт, то он за одно убъёт и сам объект и зделает не валидными оставшиеся ptrЫ. Я такого поведения от него и добивался.

А как Вы видете это всё? Как по Вашему более правильно организовать логику?
Спасибо за обсуждение)  


Название: Re: Частный случай механизма сигнал-слот
Отправлено: brankovic от Февраль 20, 2011, 09:53
А как Вы видете это всё? Как по Вашему более правильно организовать логику?

Чем не нравится такой ptr:

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

Почему бы не выкинуть is_killer и вместо bool is_valid не сделать int counter? Завладев указателем счётчик увеличиваем, потеряв его (через деструктор или operator=) счётчик уменьшаем. Последний владелец это тот, кто опустит счётчик до нуля, тогда можно удалить и счётчик и T.

Теперь обязательно, для того что бы при уничтожении receiver а разрывалось и соответствующее соединение.
Хотя есть и другой вариант как это сделать без использования smart_ptr.

как раз хочется другой вариант. smart_ptr всё равно понадобится, но чтобы спрятать его внутрь


Название: Re: Частный случай механизма сигнал-слот
Отправлено: brankovic от Февраль 20, 2011, 10:26
Код:
smart_ptr<B> p1(new B); // p1 - киллер и убъёт объект
smart_ptr<B> p2(new B); // p2 - тоже киллер!
// Внимание фокус!
p2 = p1; /* Поскольку p2 - киллер он вначале убъёт объект на который ссылается,

operator= должен установить is_valid для p2 в ноль


Название: Re: Частный случай механизма сигнал-слот
Отправлено: m_ax от Февраль 20, 2011, 12:30
Цитировать
Чем не нравится такой ptr:

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

Почему бы не выкинуть is_killer и вместо bool is_valid не сделать int counter? Завладев указателем счётчик увеличиваем, потеряв его (через деструктор или operator=) счётчик уменьшаем. Последний владелец это тот, кто опустит счётчик до нуля, тогда можно удалить и счётчик и T.
Да, Вы правы, я малость перемудрил с ptr. Вариант с каунтером гораздо лучше.
У меня просто в голове крутилась идея что убить объект должен именно тот, кто им первым завладеет..

Цитировать
как раз хочется другой вариант. smart_ptr всё равно понадобится, но чтобы спрятать его внутрь
 
Другой вариант заклюсается в том, что мы создаём в класса receivera некий объект шпион, который будет следить за своим хозяином:
Код
C++ (Qt)
class B
{
public:
   B() {}
 
   void slot1(const std::string &str) {
       std::cout << "slot1, signal - " << str << std::endl;
   }
   void slot2(std::string str) const {
       std::cout << "slot2, signal - " << str << std::endl;
   }
 
   ssc::spy spy;     // Вот этот объект. Его предназначение, следить за своим владельцем.
 
private:
   int _i;
};
 
Но теперь, при создании соединения необходимо также передать ссылку на шпиона (млин, теперь в connect на один аргумент стало больше  >:( ) пример:
Код
C++ (Qt)
int main()
{
   A *a = new A;
   B *b = new B(12);
 
   connect(a->my_signal, b, &B::slot1, b->spy);
   connect(a->my_signal, b, &B::slot2, b->spy);
 
   a->run();
 
   return 0;
}
 

Всё.. Если теперь удалить объект delete b то соединение разорвётся.

Сам шпион представляет из себя следующее:
Код
C++ (Qt)
class spy
{
public:
   spy() : _valid(new bool(true)) {}
   ~spy() { *_valid = false; }
   bool operator()() const { return *_valid; }
   template <class T1, class T2, class T3> friend class manager_connection;
private:
   bool *_valid;
};
 

Хотя на первый взгляд, при уничтожении spy происходит утечка (остаётся висячим _valid) это не совсем так, поскольку его другом является manager_connection и соответственно имеет доступ к его закрытым членам и он ответственен за зачистку указателя _valid.
Теперь, при уничтожении хозяина: delete b, будет также вызван деструктор шпиона, который сделает: *_valid = false;
Но соединение с объектом b создано раньше и владеет указателем на _valid и поэтому всегда знает, что в данный момент происходит с b.


Название: Re: Частный случай механизма сигнал-слот
Отправлено: BRE от Февраль 20, 2011, 12:49
Следующий шаг - это наследование класса recivera от класса spy, вместо создания его экземпляра и можно переименовывать spy в trackble.  :)


Название: Re: Частный случай механизма сигнал-слот
Отправлено: Igors от Февраль 20, 2011, 13:20
А если делать это по простому/народному, не связываясь ни с какими указателями? Ну у объекта 2 контейнера указателей - кому он посылает и кто ему посылает. Деструкторы вычеркивают из этих контейнеров, оператор = может копировать контейнеры, а может и нет (как нравится). Ну "навести песики" типа
Код
C++ (Qt)
class CMyClass : public CSignalReceiver, public CSignalSender {..
 



Название: Re: Частный случай механизма сигнал-слот
Отправлено: m_ax от Февраль 20, 2011, 13:50
Следующий шаг - это наследование класса recivera от класса spy, вместо создания его экземпляра и можно переименовывать spy в trackble.  :)
;) Но всё же эти две реализации схемы сигнал-слот отличаются)
Идея отнаследоваться от spy это хорошо, мы теперь уже явно в коннект не будем передавать шпиона.. 

А если делать это по простому/народному, не связываясь ни с какими указателями? Ну у объекта 2 контейнера указателей - кому он посылает и кто ему посылает. Деструкторы вычеркивают из этих контейнеров, оператор = может копировать контейнеры, а может и нет (как нравится). Ну "навести песики" типа
Код
C++ (Qt)
class CMyClass : public CSignalReceiver, public CSignalSender {..
 

А что если я хочу посылать сигналы объектам разных классов и принимать сигналы от объектов разных классов? И не думать о том каким именно объектам мне слать и получать до того момента когда наступет нужда создать соединение (уже в самом пользовательском коде)? 


Название: Re: Частный случай механизма сигнал-слот
Отправлено: m_ax от Февраль 20, 2011, 15:00
Переписал smart_ptr, как предложил brankovic
Вроде сейчас утечек нет:
Код
C++ (Qt)
#ifndef SMART_PTR_H
#define SMART_PTR_H
 
namespace ssc {
 
template <class T>
class smart_ptr
{
public:
   smart_ptr();
   smart_ptr(const smart_ptr<T> &other);
   smart_ptr(T *p);
   ~smart_ptr();
 
   bool is_null() const;
   T* operator->() const;
   T& operator*() const;
   T* get() const;
 
   smart_ptr<T> &operator=(const smart_ptr<T> &p);
 
private:
   T *_obj;
   int *_counter;
 
   void try_destroy();
   smart_ptr<T> &operator=(T*);
};
 
template <class T>
inline smart_ptr<T>::smart_ptr()
   : _obj(0), _counter(0)
{
}
 
template <class T>
inline smart_ptr<T>::smart_ptr(const smart_ptr<T> &other)
   : _obj(other._obj), _counter(other._counter)
{
   if (_counter)
       (*_counter)++;
}
 
template <class T>
inline smart_ptr<T>::smart_ptr(T *p)
   : _obj(p), _counter(new int(1))
{
}
 
template <class T>
inline void smart_ptr<T>::try_destroy()
{
   if (!_obj)
       return;
 
   if ((*_counter) == 1) {
       delete _counter;
       _counter = 0;
       delete _obj;
       _obj = 0;
   }
}
 
template <class T>
inline smart_ptr<T>::~smart_ptr()
{
   try_destroy();
}
 
template <class T>
inline bool smart_ptr<T>::is_null() const
{
   return !_obj;
}
 
template <class T>
inline T* smart_ptr<T>::operator->() const
{
   return _obj;
}
 
template <class T>
inline T& smart_ptr<T>::operator*() const
{
   return *_obj;
}
 
template <class T>
inline T* smart_ptr<T>::get() const
{
   return _obj;
}
 
template <class T>
inline smart_ptr<T>& smart_ptr<T>::operator=(const smart_ptr<T> &p)
{
   if (_obj != p._obj) {
       try_destroy();
       _counter = p._counter;
       _obj = p._obj;
 
       if (_counter)
           (*_counter)++;
   }
   return *this;
}
 
template <class T>
inline smart_ptr<T>& smart_ptr<T>::operator=(T*)
{
   return *this;
}
 
template <class T>
inline bool operator==(const T *o, const smart_ptr<T> &p)
{
   return o == p.operator->();
}
 
template<class T>
inline bool operator==(const smart_ptr<T> &p, const T *o)
{
   return p.operator->() == o;
}
 
 
template <class T>
inline bool operator==(T *o, const smart_ptr<T> &p)
{
   return o == p.operator->();
}
 
template<class T>
inline bool operator==(const smart_ptr<T> &p, T *o)
{
   return p.operator->() == o;
}
 
template<class T>
inline bool operator==(const smart_ptr<T> &p1, const smart_ptr<T> &p2)
{
   return p1.operator->() == p2.operator->();
}
 
template <class T>
inline bool operator!=(const T *o, const smart_ptr<T> &p)
{
   return o != p.operator->();
}
 
template<class T>
inline bool operator!= (const smart_ptr<T> &p, const T *o)
{
   return p.operator->() != o;
}
 
 
template <class T>
inline bool operator!=(T *o, const smart_ptr<T> &p)
{
   return o != p.operator->();
}
 
template<class T>
inline bool operator!= (const smart_ptr<T> &p, T *o)
{
   return p.operator->() != o;
}
 
template<class T>
inline bool operator!= (const smart_ptr<T> &p1, const smart_ptr<T> &p2)
{
   return p1.operator->() != p2.operator->();
}
 
}
 
#endif // SMART_PTR_H
 
Как сделать теперь его тред-сейф ?

Так какой вариант: со шпионом (он же trackable в libsigc++) или через умные указатели использовать? Как идеалогически правильнее?


Название: Re: Частный случай механизма сигнал-слот
Отправлено: Igors от Февраль 20, 2011, 16:11
А что если я хочу посылать сигналы объектам разных классов и принимать сигналы от объектов разных классов? И не думать о том каким именно объектам мне слать и получать до того момента когда наступет нужда создать соединение (уже в самом пользовательском коде)? 
А в чем помеха? Объект создается с пустыми контейнерами sender'ов (receiver'ов), connect (который так или иначе неизбежен) их заполняет


Название: Re: Частный случай механизма сигнал-слот
Отправлено: m_ax от Февраль 20, 2011, 16:26
А что если я хочу посылать сигналы объектам разных классов и принимать сигналы от объектов разных классов? И не думать о том каким именно объектам мне слать и получать до того момента когда наступет нужда создать соединение (уже в самом пользовательском коде)? 
А в чем помеха? Объект создается с пустыми контейнерами sender'ов (receiver'ов), connect (который так или иначе неизбежен) их заполняет
Помеха в том, что в контейнере должны будут находится объекты одного класса.
Либо сделать один абстрактный класс, но тогда придётся писать интерфейс на все случаи жизни, что не есть гут)
Идея в том, что общаться могут объекты совершенно разных классов. Задаётся лишь общий шаблон функции сигнала и слота.

Или как Вы имеете в виду?


Название: Re: Частный случай механизма сигнал-слот
Отправлено: m_ax от Февраль 21, 2011, 00:49
Вобщем остановился на варианте BRE: отнаследовать receiver от trackable (он же в прошлом spy), т.е. на схеме используемой в libsigc++.
Это означает, что объект который получает сигналы (receiver) должен быть отнаследован от класса trackable. trackable - автоматически разрывает соединение, если receiver будет уничтожен.

Выглядит это так:
Код
C++ (Qt)
#include <iostream>
#include <string>
#include "signal_slot.h"
 
/* Класс A испускает сигналы */
class A
{
public:
   A() {}
 
   ssc::signal<void, std::string> my_signal;
 
   void run() {
       my_signal("Hello Word!");
   }
};
 
/* Класс B  содержит слоты, поэтому он должен отнаследоваться от trackable */
class B : public ssc::trackable
{
public:
   B() {}
 
   void slot1(const std::string &str) {
       std::cout << "slot1, signal - " << str << std::endl;
   }
 
   void slot2(std::string str) const {
       std::cout << "slot2, signal - " << str << std::endl;
   }
};
 
int main()
{
   B *b = new B;
   A *a = new A;
 
   connect(a->my_signal, b, &B::slot1);
   connect(a->my_signal, b, &B::slot2);
 
   a->run();
 
   delete b;
   delete a;
 
   return 0;
}
 
 

Сам механизм сигнал-слот здесь реализован по следующей схеме:
1) Класс base_signal:
Код
C++ (Qt)
template <class T_return, class T_arg> class base_signal
 
Чисто абстрактный класс, реализующий интерфейс:
Код
C++ (Qt)
virtual T_return operator() (const T_arg &) const = 0;
virtual bool valid() const = 0;
 

2) Класс signal:
Код
C++ (Qt)
template <class T_return, class T_arg> class signal
 
Знает о всех соединениях со слотами: имеет список (std::list) соединений (manager_connection) являющихся потомками base_signal.
Имеет перегруженный оператор
Код
C++ (Qt)
T_return operator() (const T_arg &arg) const
 
 
в котором перебирается весь список соединений и для каждого объекта manager_connection вызывается свой переопределённый virtual T_return operator() (const T_arg &) const.

3) Класс manager_connection:
Код
C++ (Qt)
template <class T_receiver, class T_return, class T_arg>
class manager_connection : public base_signal<T_return, T_arg>
 
наследуется от класса base_signal и реализует весь функционал.

4) Класс trackable и trigger:
Код
C++ (Qt)
class trigger
{
public:
   trigger() : _flag(true) {}
   void set(bool flag) { _flag = flag; }
   bool get() const { return _flag; }
 
private:
   bool _flag;
};
 
class trackable
{
public:
   trackable() {
       spy = smart_ptr<trigger>(new trigger);
   }
   virtual ~trackable() {
       spy->set(false);
   }
   smart_ptr<trigger> spy;
};
 
trigger - простой класс имеющий два состояния: true - receiver жив, false - уничтожен
Класс trackable - обёртка над trigger, причём trigger заворачивается в умный указатель, который знает сколько других указателей ссылаются на объект. smart_ptr уничтожит объект если число ссылок counter == 1.
manager_connection также содержит
Код
C++ (Qt)
private:
   T_receiver *_receiver; // указатель на receiver
   smart_ptr<trigger> _spy; // шпион, он же trackable
   ...
 


При вызове одного из 4 конструктора  manager_connection происходит следующее:
Код
C++ (Qt)
manager_connection(signal<T_return, T_arg> &s, T_receiver *obj, function_type_4 slot) {
       _receiver = obj;  // копируем указатель
       _spy = _receiver->spy;  /* копируем шпиона, который вкурсе что происходит с receiverОМ
                                               по средством функции bool get() const */

       _slot_4 = slot;
       _index = 4;
       s.connect(this);  // Заносим данное соединение в список соединений нашего сигнала
   }
 
 

Конечно, в пользовательском коде реально используется только два класса: signal и trackable
и ещё один из вариантов перегруженной функции connect:
Код
C++ (Qt)
template <class T_receiver, class T_return, class T_arg>
void connect(signal<T_return, T_arg> &s, T_receiver *obj, T_return (T_receiver::*slot)(T_arg)) {
   new manager_connection<T_receiver, T_return, T_arg>(s, obj, slot);
}
 

в которой просто создаётся объект manager_connection и передаются все аргументы.

Как то так..
 
Выкладываю для тестирования файл проекта.
Немного причесал код, сейчас вроде всё работает адекватно))
 


Название: Re: Частный случай механизма сигнал-слот
Отправлено: m_ax от Февраль 21, 2011, 15:55
Обобщил решение))

Теперь при создании объекта signal в классе который будет его имитеть, нужно лишь указать один шаблонный аргумент - T_art:
Код
C++ (Qt)
class A : public ssc::trackable
{
public:
   A(){}
 
   ssc::signal<std::string> my_signal;  /* вот здесь указываем [b]только[/b]
                                                                тип передаваемого арг:  std::string */

 
   void run() {
       my_signal("Hello Word!");   // эмитем сигнал
   }
 
};
 


Соединятся сигнал может с любой функцией, представимой в виде:
Код
C++ (Qt)
   typedef T_return (T_receiver::*function_type_1)(T_arg);
   typedef T_return (T_receiver::*function_type_2)(const T_arg &);
   typedef T_return (T_receiver::*function_type_3)(T_arg) const;
   typedef T_return (T_receiver::*function_type_4)(const T_arg &) const;
 
 

Т.е. в дальнейшем всё как обычно:
Код
C++ (Qt)
class B : public ssc::trackable
{
public:
   B() {}
 
   void slot1(const std::string &str) {
       std::cout << "slot1, signal - " << str << std::endl;
   }
 
   int slot2(std::string str) const {
       std::cout << "slot2, signal - " << str << std::endl;
       return 123;
   }
};
 
int main()
{
   B *b = new B;
   A *a = new A;
 
   connect(a->my_signal, b, &B::slot1);
   connect(a->my_signal, b, &B::slot2);
 
   a->run();
 
   delete b;
   delete a;
 
   return 0;
}
 

Исходники прилагаются.


Название: Re: Частный случай механизма сигнал-слот
Отправлено: m_ax от Февраль 21, 2011, 16:50
И вновь обобщил решение))

Теперь с сигналом могут соединятся не только методы класса, но и обычные функции:

Код
C++ (Qt)
int my_func(const std::string &str) {
   std::cout << str << std::endl;
   return str.size();
}
 
class A
{
public:
 
   ssc::signal<std::string> my_signal;
 
   void run() {
       my_signal("Hello Word!");
   }
};
 
int main()
{
   B *b = new B;
   A *a = new A;
 
   connect(a->my_signal, b, &B::slot1);
   connect(a->my_signal, b, &B::slot2);
   connect(a->my_signal, my_function);  // Соединение с обычной функцией
 
   a->run();
 
   delete b;
   delete a;
 
   return 0;
}
 

Далее я изменил название основных классов:
Основным абстрактным классом соединения, предоставляющем интерфейс теперь является
Код
C++ (Qt)
template <class T_arg>
class base_connection
{
public:
   base_connection() {}
   virtual ~base_connection() {}
   virtual void operator() (const T_arg &) const = 0;
   virtual bool valid() const = 0;
};
 

вместо base_signal, что правильнее отражает суть и предназначение этого класса

Соответственно класс signal теперь содержит список всех своих соединений base_connection
Код
C++ (Qt)
std::list<base_connection<T_arg>* > _list;
 


Класс manager_connection переименован в mem_func_connection:
Код
C++ (Qt)
template <class T_receiver, class T_return, class T_arg>
class mem_func_connection : public base_connection<T_arg>
 

и предназначен для создания соединений с методами класса

Появился новый класс func_connection
Код
C++ (Qt)
template <class T_return, class T_arg>
class func_connection : public base_connection<T_arg>
 
перназначенный для создания соединения с обычными функциями

Соответственно появились версии перегруженных функций connection для соединения сигнала с обычными функциями.

Исходники приатачены, прошу потестить.




Название: Re: Частный случай механизма сигнал-слот
Отправлено: m_ax от Февраль 22, 2011, 17:35
Решил дополнить функционал, добавив
1) функцию disconnect, которая бы разрывала соединение
2) В самом соединении connect, вначале проверять, есть ли в списке уже зарегистрированное такое же соединение и если есть, то не вносить его повторно в список.

Но столкнулся с маленькой проблемкой.  Суть которой в следующем:
Поскольку список соединений содержит указатели на объекты base_connection - предоставляющим интерфейс соединения, то манипуляции все происходят через его интерфейс.
Т.е. это означает, что я например, мог бы определить например функцию address в базовой реализации:
Код
C++ (Qt)
template <class T_arg>
class base_connection
{
public:
   base_connection() {}
   virtual ~base_connection() {}
   virtual void operator() (const T_arg &) const = 0;
   virtual bool valid() const = 0;
   virtual long address_obj() const = 0; /* Вот эта новая функция, которая, к примеру,  
                                                                  будет возвращать адрес объекта receivera */

   virtual long address_mem_func() const = 0; /* А это адрес функции (слота) */
};
 
 
Тогда я бы мог перед тем как добавить новое соединение в список, проверить использую пару новых функций не содержится ли уже в списке такое же соединение.

А вот пример реализации этих функция в mem_func_connection:
Код
C++ (Qt)
long mem_func_connection::address_obj() const
{
   return (long)_receiver; // _receiver - это указатель
}
long mem_func_connection::address_mem_func() const
{
   return (long)&_slot; /* _slot - принадлежитодному из 4 типов:
                                       typedef T_return (T_receiver::*_function_type_1)(T_arg); (N = 1, 2, 3, 4)
                                       typedef T_return (T_receiver::*_function_type_2)(T_arg) const;
                                       ...
*/

}
 


Но меня мучают сомнения в корректности реализации address_mem_func() const
Хотя формально всё работает..
Дело в том, что как выяснилось sizeof(long) и sizeof(function_type_N) (N = 1,2,3,4) различаются, причём последний в два раза больше чем размкер long.  

Как быть? Куда копать?
Спасибо за помощь)


Название: Re: Частный случай механизма сигнал-слот
Отправлено: brankovic от Февраль 23, 2011, 16:34
2) В самом соединении connect, вначале проверять, есть ли в списке уже зарегистрированное такое же соединение и если есть, то не вносить его повторно в список.

Т.е. это означает, что я например, мог бы определить например функцию address в базовой реализации:

Надо же, проект цветёт и колосится! Наискосок просмотрел, похоже на полную победу сил разума.

Про connect, может вместо address сделать так:

Код
C++ (Qt)
 
template <typename T>
class base_connection
{
 virtual bool operator== (base_connection const &) const = 0;
};
 
template <typename T, typename Obj, typename Method>
class custom_connection : public base_connection <T>
{
  Obj *o;
  Method m;
 
  bool operator== (base_connection <T> const &b) const
  {
     custom_connection *c = dynamic_cast <custom_connection *> (&b);
     return c && c->o == o && c->m == m;
  }
};
 
 

если я правильно понял проблему..

P.S.: про шаред птр -- его трудно сделать полностью тред-сейф, но чтобы он стал не хуже бустовского, нужно заменить ++counter и --counter на атомарные ++ и --. В кьют это будет QAtomicInt, а в gcc есть __sync_fetch_and_add для такого.


Название: Re: Частный случай механизма сигнал-слот
Отправлено: m_ax от Февраль 23, 2011, 16:48
2) В самом соединении connect, вначале проверять, есть ли в списке уже зарегистрированное такое же соединение и если есть, то не вносить его повторно в список.

Т.е. это означает, что я например, мог бы определить например функцию address в базовой реализации:

Надо же, проект цветёт и колосится! Наискосок просмотрел, похоже на полную победу сил разума.

Про connect, может вместо address сделать так:

Код
C++ (Qt)
 
template <typename T>
class base_connection
{
 virtual bool operator== (base_connection const &) const = 0;
};
 
template <typename T, typename Obj, typename Method>
class custom_connection : public base_connection <T>
{
  Obj *o;
  Method m;
 
  bool operator== (base_connection <T> const &b) const
  {
     custom_connection *c = dynamic_cast <custom_connection *> (&b);
     return c && c->o == o && c->m == m;
  }
};
 
 

если я правильно понял проблему..

P.S.: про шаред птр -- его трудно сделать полностью тред-сейф, но чтобы он стал не хуже бустовского, нужно заменить ++counter и --counter на атомарные ++ и --. В кьют это будет QAtomicInt, а в gcc есть __sync_fetch_and_add для такого.

Спасибо)) Это именно то что нужно :)
Сейчас сделаем)


Название: Re: Частный случай механизма сигнал-слот
Отправлено: m_ax от Февраль 23, 2011, 21:15
Так, сделал по совету brankovic: определил оператор
Код
C++ (Qt)
template <class T_arg>
class base_connection
{
public:
   base_connection() {}
   virtual ~base_connection() {}
   virtual void operator() (const T_arg &) const = 0;
   virtual bool valid() const = 0;
   virtual bool operator==(base_connection<T_arg> *) const = 0;  // Вот этот оператор
};
 
И всё заработало, но.. Но работает только так:
Код
C++ (Qt)
class signal
{
public:
   signal() {}
   ~signal() {
       for (_iterator it = _list.begin(); it != _list.end(); it++) {
           delete *it;
       }
       _list.clear();
   }
 
   void operator() (const T_arg &arg) const {
       for (_const_iterator it = _list.begin(); it != _list.end(); it++) {
           if ((*it)->valid())
               (*it)->operator()(arg);
       }
   }
 
   template <class T1, class T2, class T3> friend class mem_func_connection;
   template <class T1, class T2> friend class func_connection;
 
private:
   std::list<base_connection<T_arg>* > _list;
   typedef typename std::list<base_connection<T_arg>* >::iterator _iterator;
   typedef typename std::list<base_connection<T_arg>* >::const_iterator _const_iterator;
 
   void connect(base_connection<T_arg> *c) {
       for (_iterator it = _list.begin(); it != _list.end(); it++) {
           if (c->operator ==(*it)) {  /* Вот здесь приходится явно вызывать operator==(...)
                                                         а если просто c == *it то не сработывает :(   Где подвох?
                                                         я сейчас немного не в состоянии думать))                   */

               delete c;
               return;
           }
       }
       _list.push_back(c);
   }
};
 


Вот полный код signal_slot.h
Код
C++ (Qt)
#ifndef SIGNAL_SLOT_H
#define SIGNAL_SLOT_H
 
#include <list>
#include "smart_ptr.h"
#include <iostream>
 
namespace ssc {
 
template <class T_arg>
class base_connection
{
public:
   base_connection() {}
   virtual ~base_connection() {}
   virtual void operator() (const T_arg &) const = 0;
   virtual bool valid() const = 0;
   virtual bool operator==(base_connection<T_arg> *) const = 0;
};
 
 
class trigger
{
public:
   trigger() : _flag(true) {}
   void set(bool flag) { _flag = flag; }
   bool get() const { return _flag; }
 
private:
   bool _flag;
};
 
class trackable
{
public:
   trackable() {
       spy = smart_ptr<trigger>(new trigger);
   }
   virtual ~trackable() {
       spy->set(false);
   }
   smart_ptr<trigger> spy;
};
 
template <class T_arg>
class signal
{
public:
   signal() {}
   ~signal() {
       for (_iterator it = _list.begin(); it != _list.end(); it++) {
           delete *it;
       }
       _list.clear();
   }
 
   void operator() (const T_arg &arg) const {
       for (_const_iterator it = _list.begin(); it != _list.end(); it++) {
           if ((*it)->valid())
               (*it)->operator()(arg);
       }
   }
 
   template <class T1, class T2, class T3> friend class mem_func_connection;
   template <class T1, class T2> friend class func_connection;
 
private:
   std::list<base_connection<T_arg>* > _list;
   typedef typename std::list<base_connection<T_arg>* >::iterator _iterator;
   typedef typename std::list<base_connection<T_arg>* >::const_iterator _const_iterator;
 
   void connect(base_connection<T_arg> *c) {
       for (_iterator it = _list.begin(); it != _list.end(); it++) {
           if (c->operator ==(*it)) {
               delete c;
               return;
           }
       }
       _list.push_back(c);
   }
};
 
template <class T_return, class T_arg>
class func_connection : public base_connection<T_arg>
{
public:
   typedef T_return (*function_type_1)(T_arg);
   typedef T_return (*function_type_2)(const T_arg &);
 
   func_connection(signal<T_arg> &s, function_type_1 slot) {
       _slot_1 = slot;
       _index = 1;
       s.connect(this);
   }
 
   func_connection(signal<T_arg> &s, function_type_2 slot) {
       _slot_2 = slot;
       _index = 2;
       s.connect(this);
   }
 
   void operator()(const T_arg &arg) const {
       if (_index == 1) {
           (*_slot_1)(arg);
           return;
       }
       if (_index == 2) {
           (*_slot_2)(arg);
           return;
       }
   }
 
   bool valid() const {
       return true;
   }
 
   bool operator==(base_connection<T_arg> *bc) const {
       func_connection<T_return, T_arg> *fc = dynamic_cast<func_connection<T_return, T_arg>* >(bc);
       if (fc) {
           if (_index == 1)
               return (fc->_slot_1 == _slot_1);
           if (_index == 2)
               return (fc->_slot_2 == _slot_2);
       }
       return false;
   }
 
private:
   function_type_1 _slot_1;
   function_type_2 _slot_2;
   int _index;
   func_connection();
   func_connection(const func_connection &);
};
 
template <class T_receiver, class T_return, class T_arg>
class mem_func_connection : public base_connection<T_arg>
{
public:
   typedef T_return (T_receiver::*function_type_1)(T_arg);
   typedef T_return (T_receiver::*function_type_2)(const T_arg &);
   typedef T_return (T_receiver::*function_type_3)(T_arg) const;
   typedef T_return (T_receiver::*function_type_4)(const T_arg &) const;
 
   mem_func_connection(signal<T_arg> &s, T_receiver *obj, function_type_1 slot) {
       _receiver = obj;
       _spy = _receiver->spy;
       _slot_1 = slot;
       _index = 1;
       s.connect(this);
   }
 
   mem_func_connection(signal<T_arg> &s, T_receiver *obj, function_type_2 slot) {
       _receiver = obj;
       _spy = _receiver->spy;
       _slot_2 = slot;
       _index = 2;
       s.connect(this);
   }
 
   mem_func_connection(signal<T_arg> &s, T_receiver *obj, function_type_3 slot) {
       _receiver = obj;
       _spy = _receiver->spy;
       _slot_3 = slot;
       _index = 3;
       s.connect(this);
   }
 
   mem_func_connection(signal<T_arg> &s, T_receiver *obj, function_type_4 slot) {
       _receiver = obj;
       _spy = _receiver->spy;
       _slot_4 = slot;
       _index = 4;
       s.connect(this);
   }
 
   void operator()(const T_arg &arg) const {
       if (_index == 1) {
           (_receiver->*_slot_1)(arg);
           return;
       }
       if (_index == 2) {
           (_receiver->*_slot_2)(arg);
           return;
       }
       if (_index == 3) {
           (_receiver->*_slot_3)(arg);
           return;
       }
       if (_index == 4) {
           (_receiver->*_slot_4)(arg);
           return;
       }
   }
 
   bool valid() const {
       return _spy->get();
   }
 
   bool operator==(base_connection<T_arg> *bc) const {
       mem_func_connection<T_receiver, T_return, T_arg> *mfc = dynamic_cast<mem_func_connection<T_receiver, T_return, T_arg> *>(bc);
       if (mfc && (mfc->_receiver == _receiver)) {
           if (_index == 1)
               return (mfc->_slot_1 == _slot_1);
           if (_index == 2)
               return (mfc->_slot_2 == _slot_2);
           if (_index == 3)
               return (mfc->_slot_3 == _slot_3);
           if (_index == 4)
               return (mfc->_slot_4 == _slot_4);
       }
       return false;
   }
 
private:
   T_receiver *_receiver;
   smart_ptr<trigger> _spy;
   function_type_1 _slot_1;
   function_type_2 _slot_2;
   function_type_3 _slot_3;
   function_type_4 _slot_4;
   int _index;
   mem_func_connection();
   mem_func_connection(const mem_func_connection &);
};
 
template <class T_return, class T_arg>
void connect(signal<T_arg> &s, T_return (*slot)(T_arg)) {
  new func_connection<T_return, T_arg>(s, slot);
}
 
template <class T_return, class T_arg>
void connect(signal<T_arg> &s, T_return (*slot)(const T_arg &)) {
   new func_connection<T_return, T_arg>(s, slot);
}
 
template <class T_receiver, class T_return, class T_arg>
void connect(signal<T_arg> &s, T_receiver *obj, T_return (T_receiver::*slot)(T_arg)) {
   new mem_func_connection<T_receiver, T_return, T_arg>(s, obj, slot);
}
 
template <class T_receiver, class T_return, class T_arg>
void connect(signal<T_arg> &s, T_receiver *obj, T_return (T_receiver::*slot)(const T_arg &)) {
   new mem_func_connection<T_receiver, T_return, T_arg>(s, obj, slot);
}
 
template <class T_receiver, class T_return, class T_arg>
void connect(signal<T_arg> &s, T_receiver *obj, T_return (T_receiver::*slot)(T_arg) const) {
   new mem_func_connection<T_receiver, T_return, T_arg>(s, obj, slot);
}
 
template <class T_receiver, class T_return, class T_arg>
void connect(signal<T_arg> &s, T_receiver *obj, T_return (T_receiver::*slot)(const T_arg &) const) {
   new mem_func_connection<T_receiver, T_return, T_arg>(s, obj, slot);
}
 
}
 
#endif // SIGNAL_SLOT_H
 
 


Название: Re: Частный случай механизма сигнал-слот
Отправлено: brankovic от Февраль 23, 2011, 21:29
Где подвох?

не

virtual bool operator==(base_connection<T_arg> *) const = 0;

а

virtual bool operator==(base_connection<T_arg> const &) const = 0;

мы же хотим сравнивать объект и другой объект, а не объект и указатель.


Название: Re: Частный случай механизма сигнал-слот
Отправлено: m_ax от Февраль 23, 2011, 22:15
Где подвох?

не

virtual bool operator==(base_connection<T_arg> *) const = 0;

а

virtual bool operator==(base_connection<T_arg> const &) const = 0;

мы же хотим сравнивать объект и другой объект, а не объект и указатель.
Понял в чём баг..
Но в самой вункции connect  я сравниваю именно указатели
Код
C++ (Qt)
void connect(base_connection<T_arg> *c) {
       for (_iterator it = _list.begin(); it != _list.end(); it++) {
           if (c == *it) {
               delete c;
               return;
           }
       }
       _list.push_back(c);
 

Может здесь целесообразнее объявить функцию сравнения указателей?
Например:
Код
C++ (Qt)
virtual bool compare(base_connection<T_arg> *) const = 0;
 


Название: Re: Частный случай механизма сигнал-слот
Отправлено: BRE от Февраль 23, 2011, 22:39
Но в самой вункции connect  я сравниваю именно указатели
А какой в этом смысл?


Название: Re: Частный случай механизма сигнал-слот
Отправлено: m_ax от Февраль 23, 2011, 23:03
Но в самой вункции connect  я сравниваю именно указатели
А какой в этом смысл?
В смысле?
Я должен их сравнивать, чтобы не дублировать соединения.. Если в списке уже имеется такое же соединение то и нефиг его туда вновь заносить..
Потом, мне нужно в саму функцию compare я передаю указатель и если соединение уже создано ранее, то я уничтожаю текущее.\
Или я чего то не понммаю?


Название: Re: Частный случай механизма сигнал-слот
Отправлено: BRE от Февраль 23, 2011, 23:05
Я должен их сравнивать, чтобы не дублировать соединения.. Если в списке уже имеется такое же соединение то и нефиг его туда вновь заносить..
Потом, мне нужно в саму функцию compare я передаю указатель и если соединение уже создано ранее, то я уничтожаю текущее.\
Или я чего то не понммаю?
Если написать два одинаковых конекта, в каждом из них создастся свой объект-соединение и их указатели всегда будут разные.

И может указатели:
Код
C++ (Qt)
   function_type_1 _slot_1;
   function_type_2 _slot_2;
   function_type_3 _slot_3;
   function_type_4 _slot_4;
 
объединить в union. Для чего их все держать, если в каждый момент времени используется только один.


Название: Re: Частный случай механизма сигнал-слот
Отправлено: m_ax от Февраль 23, 2011, 23:45
Цитировать
Если написать два одинаковых конекта, в каждом из них создастся свой объект-соединение и их указатели всегда будут разные.
Это да, но я сейчас ввёл функцию compare:
Код
C++ (Qt)
template <class T_arg>
class base_connection
{
public:
   base_connection() {}
   virtual ~base_connection() {}
   virtual void operator() (const T_arg &) const = 0;
   virtual bool valid() const = 0;
   virtual bool compare(base_connection<T_arg> *) const = 0;
};
 
которая сравнивает не сами указатели на соединения непосредственно указатели на receiverОв и указатели на функции классов: (вот пример её реализации в классе mem_func_connection)
Код
C++ (Qt)
bool compare(base_connection<T_arg> *bc) const {
       mem_func_connection<T_receiver, T_return, T_arg> *mfc = dynamic_cast<mem_func_connection<T_receiver, T_return, T_arg> *>(bc);
       if (mfc && (mfc->_receiver == _receiver)) {
           if (_index == 1)
               return (mfc->_slot_1 == _slot_1);
           if (_index == 2)
               return (mfc->_slot_2 == _slot_2);
           if (_index == 3)
               return (mfc->_slot_3 == _slot_3);
           if (_index == 4)
               return (mfc->_slot_4 == _slot_4);
       }
       return false;
   }
 
аналогично и в случае func_conection:
Код
C++ (Qt)
bool compare(base_connection<T_arg> *bc) const {
       func_connection<T_return, T_arg> *fc = dynamic_cast<func_connection<T_return, T_arg>* >(bc);
       if (fc) {
           if (_index == 1)
               return (fc->_slot_1 == _slot_1);
           if (_index == 2)
               return (fc->_slot_2 == _slot_2);
       }
       return false;
   }
 
и теперь в самой функции connect (класс signal):
Код
C++ (Qt)
void connect(base_connection<T_arg> *c) {
       for (_iterator it = _list.begin(); it != _list.end(); it++) {
           if (c->compare(*it)) {
               delete c;
               return;
           }
       }
       _list.push_back(c);
   }
 

Такой вариант работает, я проверял))
Цитировать
И может указатели:
Код

C++ (Qt)
    function_type_1 _slot_1;
    function_type_2 _slot_2;
    function_type_3 _slot_3;
    function_type_4 _slot_4;
 

объединить в union. Для чего их все держать, если в каждый момент времени используется только один.
Хорошая идея, спасибо)


Название: Re: Частный случай механизма сигнал-слот
Отправлено: kuzulis от Февраль 24, 2011, 08:41
Замени
Код
C++ (Qt)
...
if (_index == 1)
...
 

на
Код
C++ (Qt)
...
switch(_index) {
}
...
 

а то всё забываешь  :)


Название: Re: Частный случай механизма сигнал-слот
Отправлено: m_ax от Февраль 24, 2011, 12:20
Сделал)
Список изменений:
1) Ввёл в базовый класс (base_connection) функцию compare замещающую функции operator==()
2) Объединил указатели на функции в union
3) Заменил все if (_index) ... на switch(_index)
4) Дополнил функционал функцией disconnect, которая возвращает true если соединение было удачно разорвано и false  в противном случае. Также теперь bool возвращают и функции connect - true в случае успешного соединения, false - в противном случае.
5) По мелочи: причесал код...

Исходники для тестирования прилагаются.


Название: Re: Частный случай механизма сигнал-слот
Отправлено: m_ax от Февраль 24, 2011, 15:26
И так, общими усилиями мы это сделали))
Сейчас всё работает и думаю уже приобрело законченную форму..
Ну во всяком случае можно немного успокоится)

Я тут внёс ещё одно изменение, которое не отражается на интерфейсе сигнала, но так думаю, будет более нагляднее: изменил в base_connection
Код
C++ (Qt)
virtual void operator()(const T_arg &) const = 0
 
на
Код
C++ (Qt)
virtual void handle_evant(const T_arg &) const = 0;
 

приаттачеваю на последок исходники release версии

Всем спасибо)


Название: Re: Частный случай механизма сигнал-слот
Отправлено: m_ax от Февраль 25, 2011, 13:42
Выявил тут один баг, когда внедрял эту штуку в програмку..
Вобщем возникала проблема, при использовании типа void
Код
C++ (Qt)
class A
{
public:
   ssc::signal<void> sig;
};
 
Сигнал sig - соединяется с функциями которые возвращают T_return и не имеют аргументов:
Код
C++ (Qt)
class B : public ssc::trackable
{
public:
   int slot() const {   // Вот например с такой функцией
       return 123;
   }
};
 

Сейчас проблема решена с помощью специализации шаблонов на тип void..
Кстати, такой приём в стандартной библиотеке применён и для std::vector<bool>  

Исходники прилагаются


Название: Re: Частный случай механизма сигнал-слот
Отправлено: m_ax от Февраль 25, 2011, 18:37
Млин! Ещё одна опечатка выявилась, которая приводит к утечке :
В файле smart_ptr.h в методе try_destroy():
Код
C++ (Qt)
template <class T>
inline void smart_ptr<T>::try_destroy()
{
   if (!_obj)
       return;
 
   if ((*_counter) == 1) {
       delete _counter;
       _counter = 0;
       delete _obj;
       _obj = 0;
   }
}
 
Забыл уменьшить счётчик _counter
Правильно так:
Код
C++ (Qt)
template <class T>
inline void smart_ptr<T>::try_destroy()
{
   if (!_obj)
       return;
 
   if ((*_counter)-- == 1) {
       delete _counter;
       _counter = 0;
       delete _obj;
       _obj = 0;
   }
}
 



Название: Re: Частный случай механизма сигнал-слот
Отправлено: m_ax от Февраль 26, 2011, 04:25
Изменил архитектуру ssc
Теперь сигнал может принимать два аргумента и связываться с функциями вида:
Код
C++ (Qt)
// не методы класса
T_return (*fun_type)(T_arg1, T_arg2);
T_return (*fun_type)(T_arg1);
T_return (*fun_type)();
 
// методы класса
T_return (T_receiver::*mem_fun_type)(T_arg1, T_arg2);
T_return (T_receiver::*const_mem_fun_type)(T_arg1, T_arg2) const;
T_return (T_receiver::*mem_fun_type)(T_arg1);
T_return (T_receiver::*const_mem_fun_type)(T_arg1) const;
T_return (T_receiver::*mem_fun_type)();
T_return (T_receiver::*const_mem_fun_type)() const;
 


Сам интерфейс практически не изменился, пример:
Код
C++ (Qt)
#include <iostream>
#include <string>
#include "signal_slot.h"
 
void func(const std::string &str) {
   std::cout << "func slot: " << str << std::endl;
}
 
class A
{
public:
   A() {
       std::cout << "A()" << std::endl;
   }
   ~A() {
       std::cout << "~A()" << std::endl;
   }
 
   ssc::signal<const std::string &> sig_fun;  /* теперь нужно тип аргумента расписывать подробно!
                                                                      И чтоб он в точности соответствовал арг. слота  */

   ssc::signal<int> sig_1;
   ssc::signal<const std::string &, int> sig_2;
 
   void run() const {
       sig_fun("Hello!");
       sig_1(2011);
       sig_2("Hello World!", 1234567);
   }
};
 
class B : public ssc::trackable
{
public:
   B() {
       std::cout << "B()" << std::endl;
   }
   ~B() {
       std::cout << "~B()" << std::endl;
   }
 
   void slot1(int x) const {
       std::cout << "slot1: x = " << x << std::endl;
   }
   int slot2(const std::string &str, int x) const {
       std::cout << "slot2: " << str << "  x = " << x << std::endl;
       return 67;
   }
};
 
int main()
{
   A *a = new A;
   B *b = new B;
 
   connect(a->sig_fun, func);
   connect(a->sig_1, b, &B::slot1);
   connect(a->sig_2, b, &B::slot2);
 
   a->run();
 
   delete a;
   delete b;
   return 0;
}
 

P.S. Valgrind сказал, что утечек нет))

 


Название: Re: Частный случай механизма сигнал-слот
Отправлено: brankovic от Февраль 26, 2011, 21:47
А нельзя подключать с неполным совпадением прототипа? т.е. signal <int> к void slot (float)?


Название: Re: Частный случай механизма сигнал-слот
Отправлено: m_ax от Февраль 26, 2011, 22:17
А нельзя подключать с неполным совпадением прототипа? т.е. signal <int> к void slot (float)?
Ну вообщето, то что стоит в <> и определяет тип параметра слота..
Однако, можно (но нужно ли?) сделать так, что передавать в сигнал тип, отличный от типа указанного в слоте и уж компилятор сам будет делать преобразование типов. Это делается так:
в класса signal вместо:
Код
C++ (Qt)
template <class T_arg1, class T_arg2 = void>
class signal
{
   ...
 
   void operator() (T_arg1 arg1, T_arg2 arg2) const {
       for (_const_iterator it = _list.begin(); it != _list.end(); it++) {
           if ((*it)->valid())
               (*it)->handle_evant(arg1, arg2);
       }
   }
 

можно заменить на
Код
C++ (Qt)
template <class T_arg1, class T_arg2 = void>
class signal
{
 ...
   template <class U1, class U2>
   void operator() (U1 arg1, U2 arg2) const {
       for (_const_iterator it = _list.begin(); it != _list.end(); it++) {
           if ((*it)->valid())
               (*it)->handle_evant(arg1, arg2);
       }
   }
 
Но я бы не стал так делать..

Короче сейчас с неполным совпадением аргументов не подключить..
А в boostЕ, кстати можно?


Название: Re: Частный случай механизма сигнал-слот
Отправлено: m_ax от Февраль 26, 2011, 22:46
А нельзя подключать с неполным совпадением прототипа? т.е. signal <int> к void slot (float)?
Хотя чисто теоретически это возможно, но потребуется некий посредник - объект между сигналом и слотом, в котором и будут храниться все соединения. Тогда типы аргументов можно писать какими угодно..
Вот простой пример, демонстрирующий эту идею:
Код
C++ (Qt)
#include <iostream>
#include <string>
#include "signal_slot.h"
 
class A
{
public:
   ssc::signal<int> sig_a;
 
   void run() const {
      sig_a(2011);
   }
};
 
class B : public ssc::trackable
{
public:
   void slot_b(float x) {
       std::cout << "slot_b, x = " << x << std::endl;
   }
 
};
 
class C : public ssc::trackable
{
public:
   C() {}
   ssc::signal<float> sig_c;
   void slot_c(int x) {
       sig_c(x);
   }
};
 
int main()
{
   A *a = new A;
   B *b = new B;
   C *c = new C;   // перенаправляет сигнал
 
   connect(a->sig_a, c, &C::slot_c);
   connect(c->sig_c, b, &B::slot_b);
 
   a->run();
 
   delete b;
   delete a;
   delete c;
 
   return 0;
}
 
Вобщем идея в том, что соединения теперь нужно чтоб хранил отдельный шаблонный класс, который будет жить сам по седе и следить за всеми общающимися через него объектами..

Как то так)


Название: Re: Частный случай механизма сигнал-слот
Отправлено: m_ax от Февраль 27, 2011, 01:01
А нельзя подключать с неполным совпадением прототипа? т.е. signal <int> к void slot (float)?
Теперь можно))
Решение неожиданно оказалось очень простым без всяких лишних введений посредника c.
Короче, теперь будет работать и так:
Код
C++ (Qt)
#include <iostream>
#include <string>
#include "signal_slot.h"
 
 
class A
{
public:
   ssc::signal<int> sig_a;
 
   ssc::signal<const std::string &> sig_a2;
 
   void run() const {
      sig_a(2011);
      sig_a2("Hello!");
   }
};
 
class B : public ssc::trackable
{
public:
   void slot_b(float x) {
       std::cout << "slot_b, x = " << x << std::endl;
   }
 
   void slot_b2(std::string str) {
       std::cout << str << std::endl;
   }
 
};
 
 
int main()
{
   A *a = new A;
   B *b = new B;
 
   connect(a->sig_a, b, &B::slot_b);
   connect(a->sig_a2, b, &B::slot_b2);
 
   a->run();
 
   delete b;
   delete a;
 
   return 0;
}
 

Исходники приаттачены..


Название: Re: Частный случай механизма сигнал-слот
Отправлено: m_ax от Февраль 27, 2011, 14:58
По этому принципу сделал ещё одно обобщение. Теперь можно соединять сигналы со слотами, у которых число аргументов может быть и меньше, чем число аргументов у сигнала.
Например сейчас можно подсоединить сигнал
Код
C++ (Qt)
ssc::signal<int, float> sig;
 
например с такими слотами:
Код
C++ (Qt)
void slot1();
void slot2(int);
void slot3(int, float); // (float, int); (double, double); (int, int) и т.д.    
 


Note: если имеется несколько перегруженных слотов, то чтоб компилятор понял какой имменно слот подключать к сигналу, нужно явно в шаблонной функции connect указать параметры шаблонов.
прмер:
Код
C++ (Qt)
#include <iostream>
#include <string>
#include "signal_slot.h"
#include <cmath>
 
class A
{
public:
   ssc::signal<int> sig_a2;
 
   ssc::signal<const double &, int> sig_a;
 
   void run() const {
 
      sig_a(M_PI, 123);
 
      sig_a2(1);
   }
};
 
class B : public ssc::trackable
{
public:
/* Класс B имеет 2 перегруженных слота */
 
   void slot_b(float x, int y) {
       std::cout << "slot_b, x = " << x << std::endl;
       std::cout << "slot_b, y = " << y << std::endl;
   }
 
   void slot_b() {
       std::cout << "slot_b" << std::endl;
   }
 
   void other_slot() {
       std::cout << "other slot" << std::endl;
   }
 
};
 
int main()
{
   A *a = new A;
   B *b = new B;
 
/* Для того, чтоб не смущать компилятор, нужно в этом случае явно указывать типы шаблонов */
   ssc::connect<B, void, float, int, const double&, int>(a->sig_a, b, &B::slot_b);
   ssc::connect<B, void, const double &, int>(a->sig_a, b, &B::slot_b);
 
/* А здесь всё как обычно, компилятор сам справится  */
   ssc::connect(a->sig_a2, b, &B::other_slot);
 
   a->run();
 
   delete b;
   delete a;
 
   return 0;
}
 


Название: Re: Частный случай механизма сигнал-слот
Отправлено: m_ax от Февраль 28, 2011, 16:23
И ещё такой вопрос по поводу потокобезопасности:

Правильно ли то, чтобы гарантировать потокобезопасность соединения необходимо
сделать операцию
Код
C++ (Qt)
spy->set(false);
 
в деструкторе класса trackable атомарной?
Вот код trackable и trigger:
Код
C++ (Qt)
#ifndef TRACKABLE_H
#define TRACKABLE_H
 
#include "smart_ptr.h"
 
namespace ssc {
 
class trigger
{
public:
   trigger() : _flag(true) {}
   void set(bool flag) { _flag = flag; }
   bool get() const { return _flag; }
 
private:
   bool _flag;
};
//-----------------------------------------------------------------------------
 
class trackable
{
public:
   trackable() {
       spy = smart_ptr<trigger>(new trigger);
   }
   virtual ~trackable() {
       spy->set(false);    // Вот это должно быть атомарным?
   }
   smart_ptr<trigger> spy;
};
//-----------------------------------------------------------------------------
 
} /* namespace ssc*/
 
#endif // TRACKABLE_H
 
Или нужно что то большее  ???


Название: Re: Частный случай механизма сигнал-слот
Отправлено: brankovic от Февраль 28, 2011, 18:37
И ещё такой вопрос по поводу потокобезопасности:

Правильно ли то, чтобы гарантировать потокобезопасность соединения необходимо
сделать операцию
Код
C++ (Qt)
spy->set(false);
 
в деструкторе класса trackable атомарной?

нет, хотя бы потому, что само по себе присвоение и так атомарно. Чтобы сделать всё потокобезопасным надо в каждый разделяемый элемент системы засунуть мьютекс (в trackable, signal) или же сделать общий для всех операций библиотеки мьютекс. Это работающий вариант, но тяжёлый, плюс безвинно пострадает производительность в однопоточном режиме.

Альтернативно можно попробовать реализовать используя только атомарные операции, но дело не сведётся к простому atomic_inc. Не уверен, что это вообще возможно сделать как-то "просто и изящно".

В качестве иллюстрации: первая версия boost.signals не поддерживала потокобезопасность, и её всё равно приняли -- на тот момент было непонятно, стоит ли жертвовать производительностью ради многопоточности сигналов, которая может ещё никому не понадобится. Высказывалось мнение, что это как написать потокобезопасный вектор, вроде хорошо, но только в разы медленнее. В конце концов выпилили signals2, чтобы никому не было обидно.


Название: Re: Частный случай механизма сигнал-слот
Отправлено: m_ax от Февраль 28, 2011, 20:44
Ясненько)
Значит так просто я не смогу посылать сигналы из одного потока, скажем объекту в другом..
Вернее смогу но нет гарантий, что при выполнении слота, другой поток не прибьёт этот объект.
Или хранить в сигнале не указатели на объекты, а умные указатели (counted_ptr - он же smart_ptr в прошлом) 

Несколько обновил код, в частности,
1) smart_ptr теперь называется counted_ptr
2) метод void set(bool flag) в классе trigger перенёс в private:
Код
C++ (Qt)
class trigger
{
public:
   trigger() : _flag(true) {}
   bool get() const { return _flag; }
 
   friend class trackable;
 
private:
   void set(bool flag) { _flag = flag; }
   bool _flag;
};
 
чтоб никто, кроме trackable не мог изменить состояние объекта.
3) Всё же поскольку случай частный, оставил только передачу одного аргумета (в том числе и void)
не думаю что это вызовет неудобства, зато сразу кода скока поубавилось)

 


Название: Re: Частный случай механизма сигнал-слот
Отправлено: m_ax от Март 02, 2011, 16:11
Зарелизел третью версию libssc  :)

Вненсены следующие изменения:
1) mem_fun_connection и fun_connection теперь ничего не знают о signal, что делает их более обособленными (самодостаточными)
2) В signal теперь список соединений содержит не указатели, а counted_ptr на base_connection, что повышает безопасность
3) Функции connect, disconnect перенесены в signal
4) в signal добавлены функции
Код
C++ (Qt)
void disconnect_all() // разрывает все сондинения
 
и
Код
C++ (Qt)
size_t slot_count() const // колличество всех соединений
 
5) Добавлены шаблонные классы messenger4, messenger3, messenger2, содержащие набор данных из 4, 3 и 2 полей соответственно:
Код
C++ (Qt)
template <class T_arg1, class T_arg2>
class messenger2
{
public:
   template <class T1, class T2>
   messenger2(const messenger2<T1, T2> &other)
       : arg1(other.arg1), arg2(other.arg2) {}
   messenger2() {}
   messenger2(const T_arg1 &_arg1, const T_arg2 &_arg2)
       : arg1(_arg1), arg2(_arg2) {}
   T_arg1 arg1;
   T_arg2 arg2;
};
//-----------------------------------------------------------------------------
 
template <class T1, class T2>
inline bool operator== (const messenger2<T1, T2> &p1, const messenger2<T1, T2> &p2)
{
   return ((p1.arg1 == p2.arg1) && (p1.arg2 == p2.arg2));
}
//-----------------------------------------------------------------------------
 
template <class T1, class T2>
inline bool operator!= (const messenger2<T1, T2> &p1, const messenger2<T1, T2> &p2)
{
   return ((p1.arg1 != p2.arg1) || (p1.arg2 != p2.arg2));
}
//-----------------------------------------------------------------------------
 

Пример использования:
Код
C++ (Qt)
#include <iostream>
#include <string>
#include "signal_slot.h"
#include "messenger.h"
 
using namespace std;
 
typedef ssc::messenger4<int, int, string, int> dataA;
typedef ssc::messenger4<float, double, string, int> dataB;
 
class A
{
public:
   A() {
       cout << "A()" << endl;
   }
   ~A() {
       cout << "~A()" << endl;
   }
 
   void run() {
       sig_a1(3.14);
       sig_a2(dataA(1, 2, "Hello!", 3));
   }
 
   ssc::signal<float> sig_a1;
   ssc::signal<const dataA &> sig_a2;
};
//-----------------------------------------------------------------------------
 
class B : public ssc::trackable
{
public:
   B() {
       cout << "B()" << endl;
   }
   ~B() {
       cout << "~B()" << endl;
   }
   void slot_b1(int x) {
       cout << "slot_b(int), i = " << x << endl;
   }
 
   void slot_b2(const dataB &x) {
       cout << "slot_b2(const dataB &)" << endl;
       cout << x.arg1 << endl;
       cout << x.arg2 << endl;
       cout << x.arg3 << endl;
       cout << x.arg4 << endl;
   }
   void slot_b2(const dataA &x) {
       cout << "slot_b2(const dataA &)" << endl;
       cout << x.arg1 << endl;
       cout << x.arg2 << endl;
       cout << x.arg3 << endl;
       cout << x.arg4 << endl;
   }
   void slot_b2() {
       cout << "slot_b2()" << endl;
   }
};
//-----------------------------------------------------------------------------
 
int main()
{
   A *a = new A;
   B *b = new B;
 
   a->sig_a1.connect(b, &B::slot_b1); // соединение с неперегруженным слотом
 
/* Слот slot_b2 - перегружен, поэтому необходимо указывать явно параметры шаблонов */
   a->sig_a2.connect<B, void, const dataB &>(b, &B::slot_b2);  /* Note: сигнал отправляет тип dataA, а
слот принимает тип dataB и это сработает!  */

   a->sig_a2.connect<B, void, const dataA &>(b, &B::slot_b2);
   a->sig_a2.connect<B, void>(b, &B::slot_b2);  // И этот слот также сработает
 
   a->run();
 
   delete a;
   delete b;
 
   return 0;
}
 

P.S. Протестировано Valgrind'ом, утечек не обнаружено.


Название: Re: Частный случай механизма сигнал-слот
Отправлено: m_ax от Март 05, 2011, 03:47
Вышла версия 3.1.1

Добавлен следующий функционал:
1) Поддержка приоритета соединения: Теперь каждое соединение имеет свойство connection_priority:
Код
C++ (Qt)
template <class R_return>
int connection_priority(R_return (*slot)()) const
... // и так далее, для всех типов слотов
 

Обработка слотов идёт в порядке убывания этого значения, т.е. соединение с самым высоким значением обрабатывается первым и т.д.
При отрицательных значениях connection_priority соединение блокируется (не обрабатывается).

По умолчанию connection_priority = 0. Изменить его можно либо явно задав при создании соединения:
Код
C++ (Qt)
a.sig_a.connect(&b, &B::slot_b1, 1); // connection_priority = 1
 

либо с помощью метода:
Код
C++ (Qt)
template <class R_return, class R_arg>
bool set_connection_priority(R_return (*slot)(R_arg), int priority)
...
 
Если изменение приоритета прошло удачно, возвращает true, в противном случае false:
Код
C++ (Qt)
a.sig_a.set_connection_priority(&b, &B::slot_b4, 100); // connection_priority = 100
 


Пример использования:
Код
C++ (Qt)
#include <iostream>
#include "signal_slot.h"
 
using namespace std;
 
class A
{
public:
   ssc::signal<int> sig_a;
 
   void run() const {
       sig_a(123);
   }
};
 
class B : public ssc::trackable
{
public:
   void slot_b1(int x) {
       cout << "slot_b1, x = " << x << endl;
   }
 
   void slot_b2(int x) {
       cout << "slot_b2, x = " << x << endl;
   }
 
   void slot_b3(int x) {
       cout << "slot_b3, x = " << x << endl;
   }
 
   void slot_b4() {
       cout << "slot_b4" << endl;
   }
 
   void slot_b5() {
       cout << "slot_b5" << endl;
   }
};
 
int main()
{
   A a;
   B b;
   /* по умолчанию connection_priority = 0*/
   a.sig_a.connect(&b, &B::slot_b1, 1);
   a.sig_a.connect(&b, &B::slot_b2, 2);
   a.sig_a.connect(&b, &B::slot_b3, 3);
   a.sig_a.connect(&b, &B::slot_b4, 4);
   a.sig_a.connect(&b, &B::slot_b5, 5);
 
   a.sig_a.set_connection_priority(&b, &B::slot_b4, 100);
 
   cout << "slot_b1, priority = " << a.sig_a.connection_priority(&b, &B::slot_b1) << endl;
   cout << "slot_b2, priority = " << a.sig_a.connection_priority(&b, &B::slot_b2) << endl;
   cout << "slot_b3, priority = " << a.sig_a.connection_priority(&b, &B::slot_b3) << endl;
   cout << "slot_b4, priority = " << a.sig_a.connection_priority(&b, &B::slot_b4) << endl;
   cout << "slot_b5, priority = " << a.sig_a.connection_priority(&b, &B::slot_b5) << endl;
 
   cout << endl;
 
   a.run();
 
   return 0;
}
 



Название: Re: Частный случай механизма сигнал-слот
Отправлено: m_ax от Март 10, 2011, 14:31
Вышла версия libssc-4

Качественно изменена архитектура, что позволяе достаточно просто расширять функционал до любого числа передоваемых параметров в сигнале.

Пока ограничился случаем с числом параметров 2.
Поддерживается не неполное совпадение прототипа, т.е можно соединять:
signal<int, int> например со слотом func(float, float) и т. д.
Число параметров сигнала должно совпадать с числом параметров слота.

пример:
Код
C++ (Qt)
#include <iostream>
#include "signal_slot.h"
#include <cmath>
 
using namespace std;
 
class A
{
public:
   ssc::signal<void> sig_a0;
   ssc::signal<int> sig_a1;
   ssc::signal<int, float> sig_a2;
 
   void run() const {
       sig_a0();
       sig_a1(123);
       sig_a2(4, 2.5);
   }
};
 
class B : public ssc::trackable
{
public:
 
   void slot_b0() {
       cout << "slot_b0" << endl;
   }
 
   void slot_b1(int x) {
       cout << "slot_b1, x = " << x << endl;
   }
 
   double slot_b2(double x, float y) {
       double res = pow(x, y);
       cout << "slot_b2, x^y = " << res <<  endl;
       return res;
   }
};
 
 
int main()
{
   A a;
   B b;
 
   a.sig_a0.connect(&b, &B::slot_b0);
   a.sig_a1.connect(&b, &B::slot_b1);
   a.sig_a2.connect(&b, &B::slot_b2);
 
   a.run();
 
   return 0;
}
 

  


Название: Re: Частный случай механизма сигнал-слот
Отправлено: m_ax от Июль 16, 2011, 21:04
В ходе внедрения libssc в ядро одной программки выявилась одна маленькая неприятность :( , которая впрочем сейчас устранена. Хотя не знаю насколько это правильно..

1) Дело в том, что если необходимо скопировать один объект (который имеет свои сигналы) другому объекту, то в результате второй объект приобретает и все списки соединений, что имел первый объект. Что как то противоестественно, имхо..
В boost::signals и в libsic++ это решено просто запретом копирования объектов (NonCopybal) А иногда этого хочется)

2) Во-вторых в предыдущем варианте libssc следующий код работал неправильно
Код
C++ (Qt)
class A
{
public:
   ssc::signal<void> sig_a0;
 
   void run() const {
       sig_a0();
   }
};
 
class B : public ssc::trackable
{
public:
   void slot() const { cout << "slot B" << endl; }
};
 
int main()
{
  A a;
  B b;
  {
       B b_other;
       a.sig_a0.connect(&b_other, &B::slot);
       b_other = b;
  }
   a.run();
  return 0;
}
 
   
По идеи метод a.run() не должен вызывать слот, поскольку объект b_other - уже разрушен, однако, присваивание
b_other = b
меняет ситуацию, что не есть гут)

Проблема сейчас решена переопределением оператора присваивания и копирующего конструктора в классах trackable и signal
Код
C++ (Qt)
class trackable
{
public:
   trackable(const trackable &) {
       spy = counted_ptr<trigger>(new trigger);
   }
   trackable &operator=(const trackable &) { return *this; } // Оператор присваивания ничего не делает
   trackable() {
       spy = counted_ptr<trigger>(new trigger);
   }
   virtual ~trackable() {
       spy->set(false);
   }
   counted_ptr<trigger> spy;
};
 
И аналогично у signal
Код
C++ (Qt)
   signal2(const signal2<S_arg1, S_arg2> &) {} // Пуст
   signal2<S_arg1, S_arg2> &operator=(const signal2<S_arg1, S_arg2> &) { return *this; } // Ничего не делает
 
Короче, присваивание и конструктор копирования ничего не делают, но совсем без них поведение было бы неправильным.

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

   


Название: Re: Частный случай механизма сигнал-слот
Отправлено: m_ax от Ноябрь 25, 2011, 20:49
Я вернулся))

Дополнил libssc сигнал-сигнальным соединением. Т.е. сейчас возможно соединять сигнал одного объекта (или просто сигнал) с иным сигналом:

Код
C++ (Qt)
#include <iostream>
#include "signal_slot.h"
#include <cmath>
 
using namespace std;
 
class A
{
public:
   ssc::signal<int, float> sig_a2;
 
   void run() const {
       sig_a2(4, 2.5);
   }
};
 
class B : public ssc::trackable
{
public:
   B() {}
 
   double slot_b2(double x, float y) {
       double res = pow(x, y);
       cout << "slot_b2, x^y = " << res <<  endl;
       return res;
   }
};
 
int main()
{
   ssc::signal<double, double> sig_test;
   B b;
   sig_test.connect(&b, &B::slot_b2);
 
   A a;
   a.sig_a2.connect(&sig_test);
 
   a.run();
   return 0;
}
 


Исходники прилагаются.


Название: Re: Частный случай механизма сигнал-слот
Отправлено: whitecemetery от Март 21, 2012, 17:04
Огромное спасибо за библиотеку.
Пишу программы под микроконтроллеры в среде uVision, в которой даже STL корректно не поддерживается на некоторых моделях.
Раньше писал унылый низкоуровневый код, а потом надоело, плюс выдалась пауза в работе, начал писать с нуля библиотеку, в которой все, что нужно для работы - от контейнеров до виджетов. Интерфейсы, в основном, максимально близки к Qt-шным. Ну и понятное дело, как тема с виджетами поднялась, сразу понадобились сигналы-слоты. Любая готовая либа не подходит по своему размеру.
А эта в самый раз. Плюс очень здорово, что можно в данной теме отследить развитие с самого простого варианта(потому что в магии шаблонов я не силен пока).


Название: Re: Частный случай механизма сигнал-слот
Отправлено: m_ax от Март 21, 2012, 18:47
Огромное спасибо за библиотеку.
Пишу программы под микроконтроллеры в среде uVision, в которой даже STL корректно не поддерживается на некоторых моделях.
Раньше писал унылый низкоуровневый код, а потом надоело, плюс выдалась пауза в работе, начал писать с нуля библиотеку, в которой все, что нужно для работы - от контейнеров до виджетов. Интерфейсы, в основном, максимально близки к Qt-шным. Ну и понятное дело, как тема с виджетами поднялась, сразу понадобились сигналы-слоты. Любая готовая либа не подходит по своему размеру.
А эта в самый раз. Плюс очень здорово, что можно в данной теме отследить развитие с самого простого варианта(потому что в магии шаблонов я не силен пока).

Да не за что)
Планирую её перевести на c++11, там появилось несколько вкусных плюшек)
Да и архитектуру тож, наверное, можно будет слегка поправить)
Если что не понятно, пишите...   


Название: Re: Частный случай механизма сигнал-слот
Отправлено: whitecemetery от Март 22, 2012, 15:34
Пока все понятно. Сегодня переписал свой класс List-а, чтобы максимально соответствовал STL-ному, повозился со всякими предикатами, заменил List в библиотеке. Подключается, работает. : )
C++11 круто, но не думаю, что Keil его скоро начнет поддерживать. : )


Название: Re: Частный случай механизма сигнал-слот
Отправлено: Igors от Март 22, 2012, 17:18
Пока все понятно. Сегодня переписал свой класс List-а, чтобы максимально соответствовал STL-ному,
А в чем идея своего класса  List(а) ?


Название: Re: Частный случай механизма сигнал-слот
Отправлено: m_ax от Март 22, 2012, 17:51
Пока все понятно. Сегодня переписал свой класс List-а, чтобы максимально соответствовал STL-ному,
А в чем идея своего класса  List(а) ?
Минимализм)) Видимо, идея в полной независимости от stl..
Хотя странно, что если такая плохая поддержка stl и нет проблем с шаблонами..
Ведь libssc практически вся на шаблонах написана... 


Название: Re: Частный случай механизма сигнал-слот
Отправлено: whitecemetery от Март 23, 2012, 06:50
Проблема заключается в двух вещах:
1) для работы STL требуется определение низкоуровневых функций, связанных с вводом-выводом, для корректной работы(на некоторых вариантах микроконтроллеров просто подключаются два C файла с этими определениями, и все работает, но на других нет). Пробовал сделать это вручную топором, но не получилось. Почему не хочется в этом особо разбираться. Нас двое - человек, который паяет платы и пишет драйвера под процессоры, знает их устройство, регистры и т.п. И я, пишу конечные программы. Есть понятное желание у меня полностью абстрагироваться от конкретного процессора и писать все программы одинаково. У человека тоже есть желание не лазить по моему коду и не искать, где нужно еще какой-нибудь регистр переназначить. Через библиотеку это решается замечательно - мы договариваемся об интерфейсе каких-то устройств(дисплея, ацп, файловой системы, органов управления и т.д.), я закладываю абстрактные интерфейсы в библиотеку и навешиваю на них все, что хочу. А он пишет их реализацию под конкретное устройство.
Второй аспект - я пишу программы как под эти процессора, так и на компьютере, у них очень много совпадающих кусков кода. Хочется и там и там писать в одном стиле и не переделывать код из-за того, что что-то не поддерживается.
2) упомянутый минимализм. Ограничение по размеру кода.

Ну и пункт со звездочкой(засекречен =))
*3) повышение собственных знаний и навыков.


Название: Re: Частный случай механизма сигнал-слот
Отправлено: m_ax от Март 23, 2012, 10:22
Круто) Для меня всегда это было чем-то таким космическим (процессоры, микроконтроллеры, драйвера, регистры...)
Мой респект)


Название: Re: Частный случай механизма сигнал-слот
Отправлено: m_ax от Март 23, 2012, 11:26
Нашёл тут узкое место в реализации libssc, которое отрицательно на производительности сказывается..

Посему такой вопрос: Так ли уж необходимо такое свойство, как connection_priority, которое для каждого соединения определяет порядок его обработки? А т при каждом конекте приходится сортировать весь список соединений..
Или сортировать только один раз, перед вызовом самого сигнала.. Пока не решил, что с этим делать.. 


Название: Re: Частный случай механизма сигнал-слот
Отправлено: Igors от Март 23, 2012, 12:02
Проблема заключается в двух вещах:
1) для работы STL требуется определение низкоуровневых функций, связанных с вводом-выводом, для корректной работы(на некоторых вариантах микроконтроллеров просто подключаются два C файла с этими определениями, и все работает, но на других нет).
Никогда не слышал о таких низкоуровневых ф-циях

Ну и пункт со звездочкой(засекречен =))
*3) повышение собственных знаний и навыков.
Лучше "с двух рук", т.е. написав свою реализация - сравнить ее со стандартной (по скорости, расходу памяти, удобству использования). Иногда получаются просто удивительные результаты (причем в обе стороны)


Название: Re: Частный случай механизма сигнал-слот
Отправлено: whitecemetery от Март 23, 2012, 16:19
Цитировать
Посему такой вопрос: Так ли уж необходимо такое свойство, как connection_priority, которое для каждого соединения определяет порядок его обработки? А т при каждом конекте приходится сортировать весь список соединений..
Или сортировать только один раз, перед вызовом самого сигнала.. Пока не решил, что с этим делать..
Как я понимаю, сортировка происходит непосредственно в функции connect и до следующего ее вызова сортировки не происходит? Тогда я не вижу никакой существенной потери производительности, по крайней мере, для своих задач, когда все соединения происходят на этапе инициализации программы и больше их никто никогда не трогает.

Цитировать
Никогда не слышал о таких низкоуровневых ф-циях
Если интересно, прилагаю два файла, которые позволяют запускать STL на некоторых процессорах.
Цитировать
Лучше "с двух рук", т.е. написав свою реализация - сравнить ее со стандартной (по скорости, расходу памяти, удобству использования). Иногда получаются просто удивительные результаты (причем в обе стороны)
Да, конечно. :) Помню, мне как-то подкинули задачу для интереса реализовать функцию abs() без условного оператора. Придумал пару вариантов, один из которых, по-моему, оказался быстрее библиотечной функции для VS компилятора, но безнадежно проиграл Intel-овскому. :)
Мне сейчас очень полезно оказалось, что нужно думать не с нуля о какой-то архитектуре(ведь ничто не мешает сделать ее ужасной), а есть интерфейс, придуманный и отшлифованный умными людьми, под который надо подстроиться.
Автор этой темы, по-моему, мыслил в том же ключе - хотя реализации сигналов-слотов присутствуют в большом количестве, почему бы не попробовать сделать свою, хорошо зная, какой у этого должен быть конечный вид. : )

Тестирую под VS, залез в autoexp.dat, настроил отображение своих контейнеров при отладке, вообще красота. : )


Название: Re: Частный случай механизма сигнал-слот
Отправлено: m_ax от Март 24, 2012, 00:48
Цитировать
Как я понимаю, сортировка происходит непосредственно в функции connect и до следующего ее вызова сортировки не происходит? Тогда я не вижу никакой существенной потери производительности, по крайней мере, для своих задач, когда все соединения происходят на этапе инициализации программы и больше их никто никогда не трогает.
Да, сортируется только при создании соединения.. Вообще то, если соединений на один сигнал порядка сотни то это особо не ощутимо.. Но если динамически, например в цикле, подрят создать порядка тысячи соединений на один сигнал, то задержка в пару сек наблюдается( (Ну на моём компе, во всяком случае) Может и фигня, но можно это ускорить..
К тому же при написании гуя, так или и иначе, при создании виджетов будут происходить автомаические соединения..
Да, кстати, а как вы гуй реализуете? Что то стороннее используете или как? Как там всё это у вас происходит?

Сделал несколько изменений в libssc. Сейчас работает чуть быстрее.
1) Переименовал две виртуальные функции в base_connection:
valid  на is_valid;
compare на is_same

2) Убрал полностью из signal сортировку. Сейчас всё это выглядит так: В функции connect:
Код
C++ (Qt)
bool connect(const ptr_base_connection &p) {
 
       int priority = p->priority();
       m_iterator position = m_list.begin();
 
       for (m_iterator it = m_list.begin(); it != m_list.end(); ++it) {
           if (p->is_same((*it).get()))
               return false;
 
           if (priority < (*it)->priority())
               ++position;
       }
       m_list.insert(position, p);
       return true;
   }
 
 
и в функции set_connection_priority:
Код
C++ (Qt)
bool set_connection_priority(const ptr_base_connection &p) {
       for (m_iterator it = m_list.begin(); it != m_list.end(); ++it) {
           if (p->is_same((*it).get()) && (*it)->is_valid()) {
               if ((*it)->priority() != p->priority()) {
                  m_list.erase(it);
                  connect(p);
               }
           return true;
           }
       }
       return false;
   }
 
Соответственно отпала необходимость в предикате (статической функции класса signal) compare.

Пока серьёзно не тестил, выкладываю как есть) Но скорость при создании соединений возросла. Если раньше при больших N она была пропорциональна N log N, то сейчас линейна по N (N - число соединений на один сигнал)




 


Название: Re: Частный случай механизма сигнал-слот
Отправлено: whitecemetery от Март 26, 2012, 08:36
Цитировать
Да, сортируется только при создании соединения.. Вообще то, если соединений на один сигнал порядка сотни то это особо не ощутимо.. Но если динамически, например в цикле, подрят создать порядка тысячи соединений на один сигнал, то задержка в пару сек наблюдается( (Ну на моём компе, во всяком случае) Может и фигня, но можно это ускорить..
Для меня такие количественные цифры нереальны. Поэтому, например, ту же сортировку в своей библиотеке я реализовал не быструю, а Шелла. У последней O(n^(7/6)), это до десятков-сотен тысяч элементов занимает меньше, чем O(n ln n). Впрочем, чтобы более надежно что-то утверждать, надо реализовать и то, конечно, и сравнить.
Я тут напрягался, что у меня в List-е гораздо больший косяк по производительности - добавление элементов занимает терпимое время, а вот при опереции Clear() на сотнях тысяч элементов происходит подвисание на три-четыре секунды, а взял, попробовал сравнить с std::list, у него такая же шняга. Оказалось, у меня проигрывает как раз по добавлению в несколько раз. Буду разбираться.
Цитировать
Да, кстати, а как вы гуй реализуете? Что то стороннее используете или как? Как там всё это у вас происходит?
Нет, я уже писал об этом. Желания и возможности реализовывать навороченный гуй нет. Да и как, если на наших дисплеях видно невооруженным взглядом, как прямоугольник размером с экран перерисовывается ? : )
Просто не хочется, чтобы отрисовка, обработка тачпада и основные события были разбросаны по программе как попало, вот и появилась идея с простейшими виджетами, которые сами себя перерисовывают, когда надо, и сами проверяют, жмякали по ним или нет, шлют события.


Название: Re: Частный случай механизма сигнал-слот
Отправлено: m_ax от Март 26, 2012, 09:35
Понятно)
Я просто к тому, что никакой необходимости сортировать список при создании соединения, на самом деле нет. Достаточно лишь при создании конекта вставлять его (соединение) в нужное место, чтоб они (соединения) всегда располагались по убыванию свойства priority. Что собственно и было сделано в последней версии. 


Название: Re: Частный случай механизма сигнал-слот
Отправлено: Igors от Март 26, 2012, 12:36
whitecemetery, мы тут с m_ax обсуждаем один не совсем банальный контейнер (хотя возможно в теории это есть, просто не нашли). http://www.prog.org.ru/index.php?topic=21385.msg148007#msg148007 (http://www.prog.org.ru/index.php?topic=21385.msg148007#msg148007). Возможно Вам будет интересно поучаствовать


Название: Re: Частный случай механизма сигнал-слот
Отправлено: whitecemetery от Март 26, 2012, 17:25
Прошу прощения за оффтоп, но я просто в шоке.
Разбирался, почему вставка в мой List работает в 10 раз медленней, чем в std::list.
Выяснил, что все касается оператора new.
То есть, даже если в функции PushBack не делается ничего, кроме выделения памяти под простейший элемент:
void PushBack()
{
    int * p = new int;
}

Все, уже в 10 раз медленней std::list.


Название: Re: Частный случай механизма сигнал-слот
Отправлено: Igors от Март 26, 2012, 20:39
Для list и ассоциативных контейнеров custom allocator эффективен (в отличие от линейных - вектор, дека). Идея проста: распределяются блоки одинаковой длины, ну значит распределить сразу N блоков. А если удаляем - не спешить мочить, а связать удаленные в список и при следующем выделении использовать повторно. Там все отлично получается, а на каждый чих звать malloc/free - ну так конечно в пролете


Название: Re: Частный случай механизма сигнал-слот
Отправлено: whitecemetery от Март 27, 2012, 07:00
Цитировать
Для list и ассоциативных контейнеров custom allocator эффективен
Да, я уже понял, что без placement new и выделения большего количества памяти, чем нужно, никуда для быстрого добавления.
Я подумываю о листе, похожем на Qt-шный - когда память выделяется цельным блоком - просто вектором - под несколько элементов, а потом они содержат индексы относительно начала массива на элементы до них и после в листе. Тогда добавление эффективно, сортировка тоже эффективна, потому что производится с вектором, а не листом. : )
Единственно, при удалении элементов память висит, пока весь лист не будет удален. Хотя и с placement new будет также.


Название: Re: Частный случай механизма сигнал-слот
Отправлено: twp от Март 27, 2012, 09:34
еще одна деталь: не целесообразно выделять память для объекта, если его размер <= sizeof(void*). Т.е. внутри хранить указатели на void. Насколько я знаю именно так реализованы контейнеры в Qt.


Название: Re: Частный случай механизма сигнал-слот
Отправлено: Igors от Март 27, 2012, 10:06
Я подумываю о листе, похожем на Qt-шный - когда память выделяется цельным блоком - просто вектором - под несколько элементов, а потом они содержат индексы относительно начала массива на элементы до них и после в листе. Тогда добавление эффективно, сортировка тоже эффективна, потому что производится с вектором, а не листом. : )
Чую - что-то упущено (уж слишком хорошо получается). Давайте детали/псевдокод, перетрем


Название: Re: Частный случай механизма сигнал-слот
Отправлено: whitecemetery от Март 28, 2012, 07:28
Упущено то, что контейнер отжирает память. Если из него удаляются элементы - он становится прореженным, но не уменьшается в размере. Если после этого в него снова добавляются элементы, он становится еще больше, чем был.
И так, пока его полностью не удалят.


Название: Re: Частный случай механизма сигнал-слот
Отправлено: Igors от Март 28, 2012, 10:29
Все равно список и не планируется доступ [] по индексу - так почему бы не использовать "дырки" повторно?
Также представим что память распределяется не одним, а цепочкой блоков. И в этом случае сортировка не должна быть проблемой - сначала раскидываем "по блокам", затем сортируем каждый блок (почти так делает quicksort).


Название: Re: Частный случай механизма сигнал-слот
Отправлено: m_ax от Март 28, 2012, 12:03
Ура, товарищи!

Вышла новая версия libssc-5.0

Изменилась структура проекта.
Претерпела изменения (разумеется, в лучшую сторону) архитектура libssc, став более гибкой для последующих расширений. Появилась полная независимость от типа контэйнера (при условии, что он поддерживает stl интерфейс итераторов и алгоритмов)

Повышена производительность.
Заменены все конструкции вида for(...) на std::for_each.

Выкинуты свойства priority - которые нафиг на самом деле не нужны..

Короче, эта версия стала более продвинутой, более гибкой, более быстрой)

Исходники приатачены.
 


Название: Re: Частный случай механизма сигнал-слот
Отправлено: whitecemetery от Март 28, 2012, 14:55
Как хорошо-то, когда поддержка от разработчика в реальном времени. ; )


Название: Re: Частный случай механизма сигнал-слот
Отправлено: m_ax от Март 28, 2012, 15:00
Как хорошо-то, когда поддержка от разработчика в реальном времени. ; )
Не знаю, пойдёт ли она у вас.. Там сейчас активно используются stl алгоритмы:
std::find_if
std::for_each
std::remove_if
вместо самопальных циклов for()


Название: Re: Частный случай механизма сигнал-слот
Отправлено: m_ax от Март 28, 2012, 16:06
Нашёл тут один баг, в методе slot_count() приватного класс signal_p
Раньше, по не внимательности было
Код
C++ (Qt)
std::remove_if(m_list.begin(), m_list.end(), not_valid);
return m_list.size();
 

что давало не правильное число валидных соединений.
А надо бы так:
Код
C++ (Qt)
iterator it = std::remove_if(m_list.begin(), m_list.end(), not_valid);
m_list.erase(it, m_list.end());
return m_list.size();
 

Вобщем ради эффективности исправлено на
Код
C++ (Qt)
m_list.remove_if(not_valid);
return m_list.size();
 

Ещё пара мелочей, эстетического характера)

версия: 5.0.5


Название: Re: Частный случай механизма сигнал-слот
Отправлено: Igors от Март 28, 2012, 19:13
Это не попытка "развязать полемику", просто мое личное мнение - отношение к std algorithm должно быть разумным, как к водке
Цитировать
Папаша, зачем Вы так? Ну приняли литру... Ну две. Но зачем же так нажираться?


Название: Re: Частный случай механизма сигнал-слот
Отправлено: m_ax от Март 28, 2012, 19:51
Это не попытка "развязать полемику", просто мое личное мнение - отношение к std algorithm должно быть разумным, как к водке
Цитировать
Папаша, зачем Вы так? Ну приняли литру... Ну две. Но зачем же так нажираться?

Клуб анонимных stl зависимых программистов..
-Здрасвуйте, меня зовут Макс и я зависим от stl алгоритмов, контэйнеров.. Всё началось несколько лет назад, когда друзья дали мне попробовать почитать два томика Брюса Эккеля "Фмлософия C++". Я не заметил как втянулся в это( Вначале я употреблял stl очень редко, по праздникам и по особым случаям, чтоб просто не выглядеть лузером в глазах друзей (они то давно употребляют и даже вещи по крепче, boost например)..
Потом всё больше и больше начал употреблять stl и даже мешал его с различными патернами..

Недавно поймал себя на мысли, что уже разучился изобретать велосипеды.. Меня это очень пугает..
Но самое страшное это то, что мне уже мало stl.. stl уже не вставляет как прежде... руки тянутся к boost..
Не знаю, как с этим жить(

Эх, велосипеды, велосипеды, где ж те времена...
      
добавлено, чуть спустя:

Да, забыл смайлик поставить
 ;)


Название: Re: Частный случай механизма сигнал-слот
Отправлено: m_ax от Март 29, 2012, 01:06
Зарелизил версию libssc-5.0.8



Название: Re: Частный случай механизма сигнал-слот
Отправлено: m_ax от Март 29, 2012, 11:05
Появилась возможность соединять сигналы со слотами(др. сигналами), у которых число параметров может быть меньше (но не больше!), чем у сигнала.
пример:
Код
C++ (Qt)
using namespace std;
 
struct A
{
   ssc::signal<const double&, const double&> sig2;
   ssc::signal<float> sig1;
   ssc::signal<void> sig0;
 
   void run() const {
       sig2(1.0, 2.0);
       sig1(3.0f);
       sig0();
   }
};
 
struct B : public ssc::trackable
{
   void slot2(float x, float y) const {
       cout << "float x = " << x << " float y = " << y << endl;
   }
   void slot1(int x) const {
       cout << "int x = " << x << endl;
   }
 
   void slot0() { cout << "slot0" << endl;}
};
 
int main()
{
   A a;
   B *b = new B();
 
   a.sig2.connect(b, &B::slot2);
   a.sig2.connect(b, &B::slot1);
   a.sig2.connect(b, &B::slot0);
 
   a.sig1.connect(b, &B::slot1);
   a.sig1.connect(b, &B::slot0);
 
   a.sig0.connect(b, &B::slot0);
 
   a.run();
 
   delete b;
 
   return 0;
}
 

Версия libssc-5.1.0
 


Название: Re: Частный случай механизма сигнал-слот
Отправлено: Igors от Март 29, 2012, 14:25
Эх, велосипеды, велосипеды, где ж те времена...
Просто здесь много изучающих, часто рассматриваются типовые, хорошо изученные задачи - вот и создается впечатление что, якобы, все уже давно известно, сделано за нас и.т.п. Но стоит отъехать 2 остановки на тролебусе с сторону реальных задач (или просто копнуть те же типовые задачи чуть глубже) - и иллюзия сразу исчезает, и будут только велосипеды, велосипеды, велосипеды...  :)


Название: Re: Частный случай механизма сигнал-слот
Отправлено: m_ax от Март 29, 2012, 14:54
Цель теории не в том, чтоб учить как решать типовые задачи. А в том, как правильно подходить к решению задачи/проблемы, как мыслить в категориях того языка, на котором пишешь и как грамотно пользоваться инструментарием)
Поэтому в книжках (которые Вы не читаете) так много уделено именно инструменту)
А уже дальше всяким шаблонам, патернам и снова инструментам) 


Название: Re: Частный случай механизма сигнал-слот
Отправлено: BRE от Март 29, 2012, 15:01
Просто здесь много изучающих, часто рассматриваются типовые, хорошо изученные задачи - вот и создается впечатление что, якобы, все уже давно известно, сделано за нас и.т.п. Но стоит отъехать 2 остановки на тролебусе с сторону реальных задач (или просто копнуть те же типовые задачи чуть глубже) - и иллюзия сразу исчезает, и будут только велосипеды, велосипеды, велосипеды...  :)
Да наверное здесь это и не нужно. На этом форуме кроме тебя реальные задачи никто не решает. Всё больше учебные решаем, книжки всякие, статьи читаем - короче страдаем фигней.
Но если, не дай Бог, кому понадобится решить реальную задачу, то мы сразу к тебе. Ну а куда еще, у тебя есть что посоветовать. Одни темы про разные чуто-контейнеры чего стоят.  :)


Название: Re: Частный случай механизма сигнал-слот
Отправлено: Bepec от Март 29, 2012, 15:12
Зря говоришь. В каждой решённой теме есть зёрнышко. Если их собрать и посадить, то много чего полезного вырастет.


Название: Re: Частный случай механизма сигнал-слот
Отправлено: Igors от Март 29, 2012, 15:17
Цель теории не в том, чтоб учить как решать типовые задачи. А в том, как правильно подходить к решению задачи/проблемы, как мыслить в категориях того языка, на котором пишешь и как грамотно пользоваться инструментарием)
Поэтому в книжках (которые Вы не читаете) так много уделено именно инструменту)
А уже дальше всяким шаблонам, патернам и снова инструментам) 
Слова-то хорошие (с демагогией у Вас все отлично). Но ведь не секрет что часто (да чего там - в большинстве случаев) дело сводится к тупенькому запоминанию - и ничему больше. В советской школе это называлось простым словом "списывать".

2BRE "Не вынесла душа поэта"   :) :)
Кто же виноват что Вы никогда тем не создаете? Вот и остается обсуждать моих "уродцев" :) Изложите Ваши проблемы - и, если тема мне будет интересной, то приму участие в обсуждении.


Название: Re: Частный случай механизма сигнал-слот
Отправлено: BRE от Март 29, 2012, 15:25
2BRE "Не вынесла душа поэта"   :) :)
Кто же виноват что Вы никогда тем не создаете? Вот и остается обсуждать моих "уродцев" :) Изложите Ваши проблемы - и, если тема мне будет интересной, то приму участие в обсуждении.
Спасибки, но я как нибудь обойдусь.


Название: Re: Частный случай механизма сигнал-слот
Отправлено: m_ax от Март 29, 2012, 16:40
Короче выкладываю последнюю версию libssc-5.1.1

Все последующие версии будут написаны на C++11.
Но это будет не раньше чем, я поставлю себе нормальный линукс. дистрибутив.  8)


Название: Re: Частный случай механизма сигнал-слот
Отправлено: m_ax от Март 30, 2012, 14:40
Ещё несколько пост-архитектурных изменений перед переводом на c++11
Версия libssc-5.2.0



Название: Re: Частный случай механизма сигнал-слот
Отправлено: BRE от Март 30, 2012, 15:28
Ты релизишься как firefox. ;)


Название: Re: Частный случай механизма сигнал-слот
Отправлено: m_ax от Март 30, 2012, 16:15
Ты релизишься как firefox. ;)
Похоже это болезнь  ;D


Название: Re: Частный случай механизма сигнал-слот
Отправлено: m_ax от Апрель 01, 2012, 20:19
Первая версия под c++11 зарелизена)
Правда, использовано только то, что поддерживает gcc 4.4

Доморощенный counted_ptr заменён на нормальный std::shared_ptr.
+ использование decltype позволило сократить туеву хучу лишних строчек кода)





Название: Re: Частный случай механизма сигнал-слот
Отправлено: Gorthauer от Апрель 06, 2012, 21:55
Видел реализацию сигналов/слотов на variadic template'ах, в результате чего число аргументов может быть произвольным.
Плюс советую посмотреть в шаблонную реализацию сигналов слотов в Qt5.


Название: Re: Частный случай механизма сигнал-слот
Отправлено: m_ax от Апрель 06, 2012, 21:59
Видел реализацию сигналов/слотов на variadic template'ах, в результате чего число аргументов может быть произвольным.
Плюс советую посмотреть в шаблонную реализацию сигналов слотов в Qt5.
Да, varisdic template это хорошее дело) Тож думал об этом.. А ссылку не дадите, где видели это?
На Qt5 посмотрю, спасибо)


Название: Re: Частный случай механизма сигнал-слот
Отправлено: m_ax от Апрель 13, 2012, 12:38
Зарелизил версию libssc-cpp11-1.0.3
Так по-мелочи подправил кое-где код..


Название: Re: Частный случай механизма сигнал-слот
Отправлено: whitecemetery от Апрель 26, 2012, 11:51
Понадобилось тут сигнал-сигнальное соединение. Ожидаемый мной синтаксис не прошел, но так как помню, что библиотека должна его поддерживать, повозился еще некоторое время.
И получившийся вариант меня немного удивил.
Вот пример, того, что мне нужно.

Код
C++ (Qt)
class A
{
public:
   signal<void> TellSmth;
};
 
class B : public SlotObject
{
public:
   signal<void> TellSmth;
 
   B()
   {
        a.TellSmth.connect(&TellSmth);
   }
private:
   A a;
}
 

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


Название: Re: Частный случай механизма сигнал-слот
Отправлено: m_ax от Апрель 26, 2012, 17:59
Понадобилось тут сигнал-сигнальное соединение. Ожидаемый мной синтаксис не прошел, но так как помню, что библиотека должна его поддерживать, повозился еще некоторое время.
И получившийся вариант меня немного удивил.
Вот пример, того, что мне нужно.

Код
C++ (Qt)
class A
{
public:
   signal<void> TellSmth;
};
 
class B : public SlotObject
{
public:
   signal<void> TellSmth;
 
   B()
   {
        a.TellSmth.connect(&TellSmth);
   }
private:
   A a;
}
 

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

Вы же соединяете сигнал с сигналом..При чём здесь мембер/не мембер функции?
Вообще, какое то странное использование сигнально-слотового механизма.. Почему бы просто не вызывать сигнал TellSmth, объекта класса B?

Да, с мембер/не мембер функциями там всё нормально, signal знает всё что нужно о слотах и всё сделает правильно при вызове)

А в чём вопрос, я не понял?


Название: Re: Частный случай механизма сигнал-слот
Отправлено: whitecemetery от Май 15, 2012, 11:21
Цитировать
Почему бы просто не вызывать сигнал TellSmth, объекта класса B?
Инкапсуляция, не?  ;)
Почему я как пользователь должен знать, что внутри B есть A? Я знаю только про B.
Вопрос в синтаксисе языка, видимо. Видно у меня тут большое непонимание просто. Надо разбираться.




Название: Re: Частный случай механизма сигнал-слот
Отправлено: m_ax от Май 15, 2012, 20:04
Цитировать
Почему бы просто не вызывать сигнал TellSmth, объекта класса B?
Инкапсуляция, не?  ;)
Почему я как пользователь должен знать, что внутри B есть A? Я знаю только про B.
Вопрос в синтаксисе языка, видимо. Видно у меня тут большое непонимание просто. Надо разбираться.
Я просто не совсем понимаю в чём проблема?


Название: Re: Частный случай механизма сигнал-слот
Отправлено: whitecemetery от Февраль 15, 2013, 08:43
Привет!
Понадобилось ввести потоковую безопасность при вызове signal->operator() (у меня виджеты начинают перерисовыватся, еще не дорисовавшись, потому что их можно с тачскрина и клавиатуры контролировать, которые в разных потоках :)), вот сейчас буду разбираться.
Библиотеку по-прежнему нужно оставить кросс-платформенной, поэтому думаю ввести просто абстрактный класс мьютекса, реализацию которого отдать конкретным платформам.
А библиотека будет по какому-то методу этот конкретный класс вытаскивать. Если возникнут вопросы, отпишусь. Советы приветствуются. :)
Если получившийся код мне понравится, скину сюда.
А вообще, спасибо еще раз за библиотеку.


Название: Re: Частный случай механизма сигнал-слот
Отправлено: m_ax от Февраль 15, 2013, 11:01
Привет!
Понадобилось ввести потоковую безопасность при вызове signal->operator() (у меня виджеты начинают перерисовыватся, еще не дорисовавшись, потому что их можно с тачскрина и клавиатуры контролировать, которые в разных потоках :)), вот сейчас буду разбираться.
Библиотеку по-прежнему нужно оставить кросс-платформенной, поэтому думаю ввести просто абстрактный класс мьютекса, реализацию которого отдать конкретным платформам.
А библиотека будет по какому-то методу этот конкретный класс вытаскивать. Если возникнут вопросы, отпишусь. Советы приветствуются. :)
Если получившийся код мне понравится, скину сюда.
А вообще, спасибо еще раз за библиотеку.

Сделать её потокобезопасной, это будет не просто.. Как вариант можно воспользоваться boost::signals2

И кстати, в С++11 есть Thread support library: http://en.cppreference.com/w/cpp/thread (http://en.cppreference.com/w/cpp/thread)


Название: Re: Частный случай механизма сигнал-слот
Отправлено: whitecemetery от Февраль 15, 2013, 13:56
Спасибо, я знаю.
К несчастью, компилятор микроконтроллера, под который я пишу, не поддерживает C++11. Там вообще по умолчанию все библиотеки на C и приходится обертки писать с указателями void * на члены класса порой. )
А буст, боюсь, съест всю мою память. К тому же у меня есть личная нетерпимость буста за отвратительный интерфейс(опять же, на мой личный взгляд) и соответствующее этому низкое желание в еще каких-то его либах разбираться.


Название: Re: Частный случай механизма сигнал-слот
Отправлено: m_ax от Март 21, 2013, 01:45
Решил тут ради интереса освоить Variadic template (полезная фича, надо заметить) и в процессе обобщил libssc.
Теперь сигнально слотовое соединение может содержать произвольное число параметров.
(при этом объём кода libssc уменьшился более чем в три раза)

пример использования:
Код
C++ (Qt)
#include <iostream>
#include <string>
#include "signal_slot.h"
 
void some_func(int a, float f, const std::string &str, double &x) {
   std::cout << "x = " << x << std::endl;
   std::cout << "a = " << a << " f = " << f << " str = " << str << std::endl;
   x += 123.456;
   std::cout << "x = " << x << std::endl;
}
 
class object : public ssc::trackable
{
public:
 
   void print(const std::string &text) const { std::cout << text << std::endl; }
 
   int sum(int x, int y, int z) const {
       int s = x + y + z;
       std::cout << "sum = " << s << std::endl;
       return s;
   }
 
};
 
 
int main()
{
   object obj;
 
   ssc::signal<const std::string &> sig1;
   sig1.connect(&obj, &object::print);
 
   ssc::signal<long, float, int> sig3;
   sig3.connect(&obj, &object::sum);
 
   ssc::signal<int, double, const std::string &, double&> sig4;
   sig4.connect(some_func);
 
   sig1("hello word!");
   sig3(1, 3.14, 2);
 
   double x = 0.0;
   sig4(1, 3.14, "hello word!", x);
 
   return 0;
}
 

проект приатачен.


 


Название: Re: Частный случай механизма сигнал-слот
Отправлено: m_ax от Март 27, 2013, 18:03
Залил проект на gitorius:
https://www.gitorious.org/lightssp (https://www.gitorious.org/lightssp)