Russian Qt Forum
Ноябрь 22, 2024, 21:32 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1] 2 3 ... 6   Вниз
  Печать  
Автор Тема: Protected вызов сигналов в Boost.Signals2  (Прочитано 35095 раз)
ViTech
Гипер активный житель
*****
Offline Offline

Сообщений: 858



Просмотр профиля
« : Октябрь 15, 2015, 19:21 »

В системе сигнал-слот нужно обеспечить защищённый вызов сигнала. Т.е. вызвать сигнал может только класс, в котором этот сигнал задан, и его потомки. И метод вызова сигнала должен быть protected, а не public, чтобы кто угодно не мог вызвать сигнал "снаружи".

Пример на Qt:
Код
C++ (Qt)
#include <QObject>
#include <iostream>
 
class BaseQtShape : public QObject
{
   Q_OBJECT
 
public:
   void animate()
   {
       emit visibleChanged(true);
       emit enabledChanged(false);
       emit positionChanged(5, 7);
       emit sizeChanged(10, 15);
   }
 
signals:
   void visibleChanged(bool visible);
   void enabledChanged(bool enable);
   void positionChanged(int x, int y);
   void sizeChanged(int height, int width);
};
 
class ChildQtShape : public BaseQtShape
{
   Q_OBJECT
 
public:
   void animate()
   {
       BaseQtShape::animate();
       emit scaleChanged(3, 8);
   }
 
signals:
   void scaleChanged(int x, int y);
};
 
class BaseQtLogger : public QObject
{
   Q_OBJECT
 
public slots:
   void onVisibleChanged(bool visible)
   { std::cout << "visibleChanged: " << visible << std::endl; }
   void onEnabledChanged(bool enable)
   { std::cout << "enabledChanged: " << enable << std::endl; }
   void onPositionChanged(int x, int y)
   { std::cout << "positionChanged: " << x << ", " << y << std::endl; }
   void onSizeChanged(int height, int width)
   { std::cout << "sizeChanged: " << height << ", " << width << std::endl; }
};
 
class ChildQtLogger : public BaseQtLogger
{
   Q_OBJECT
 
public slots:
   void onScaleChanged(int x, int y)
   { std::cout << "scaleChanged: " << x << ", " << y << std::endl; }
};
 
int main(int /* argc */, char ** /* argv[] */)
{
   ChildQtShape shape;
   ChildQtLogger logger;
 
   QObject::connect(&shape, &BaseQtShape::visibleChanged, &logger, &BaseQtLogger::onVisibleChanged);
   QObject::connect(&shape, &BaseQtShape::enabledChanged, &logger, &BaseQtLogger::onEnabledChanged);
   QObject::connect(&shape, &BaseQtShape::positionChanged, &logger, &BaseQtLogger::onPositionChanged);
   QObject::connect(&shape, &BaseQtShape::sizeChanged, &logger, &BaseQtLogger::onSizeChanged);
   QObject::connect(&shape, &ChildQtShape::scaleChanged, &logger, &ChildQtLogger::onScaleChanged);
 
   shape.animate();
 
   // Invalid usage. emit must be protected.
   shape.emit visibleChanged(true);
 
   return 0;
}
 
#include "main.moc"
 

В СигналСлотах Qt творится магия. Там по исходному тексту программы хорошо проходится moc, который творит волшебство с "ключевыми словами" signal, slot, emit. Влезть в эти магические действия, и что-то изменить под свои требования, постороннему вряд ли удастся Улыбающийся.

Посему интересует вариант с СигналСлотами Boost'а. Сам Boost'ом не владею, поэтому прошу знатоков показать, как будет выглядеть аналог вышеприведённого примера для Boost.Signals2 Улыбающийся. Т.е. тот же самый набор сигналов и слотов в полном объеме и с наследованием. Сами сигналы публичные, подключиться и отключиться от них может кто угодно. Только чтобы вызов сигнала (emit) был protected и "снаружи" никто не мог вызвать сигнал. Чтобы поменьше писать ручками, шаблонизация 80-го уровня вполне приветствуется, а вот макросов желательно избегать. Хотя вариант с макросами тоже интересен для сравнения Улыбающийся.

Вариант на Boost.Signals2, естественно, только с использованием Boost и std. Без Qt, QObject, Q_OBJECT и т.п. Улыбающийся Так же возможна демонстрация подобной задачи с использованием других библиотек SignalSlot. Интересно посмотреть, как там такое реализовано.
Записан

Пока сам не сделаешь...
Racheengel
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2679


Я работал с дискетам 5.25 :(


Просмотр профиля
« Ответ #1 : Октябрь 15, 2015, 21:13 »

А если в ChildQtShape добавить, например,

Код:
protected:
  void EmitVisibleChanged(bool b)
  {
      emit visibleChanged(b);
  }
?

И если не секрет - для чего вообще такое понадобилось? Вообще-то нетипичное применение эмита, имхо...
Записан

What is the 11 in the C++11? It’s the number of feet they glued to C++ trying to obtain a better octopus.

COVID не волк, в лес не уйдёт
ViTech
Гипер активный житель
*****
Offline Offline

Сообщений: 858



Просмотр профиля
« Ответ #2 : Октябрь 16, 2015, 11:34 »

Код:
protected:
  void EmitVisibleChanged(bool b)
  {
      emit visibleChanged(b);
  }

Даже если такое написать, как это изменит тот факт, что любой, у кого есть ссылка на объект shape, может от его имени и без его ведома рассылать сигналы об изменении его состояния, которое на самом деле не менялось, с помощью:
Код:
    // Invalid usage. emit must be protected.
    shape.emit visibleChanged(true);

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

Пока сам не сделаешь...
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #3 : Октябрь 16, 2015, 12:24 »

Несколько причудливый запрос Улыбающийся Понял так: если "signals" не писать, то не будет "мокирования", а signals просто-напросто public.

Все же решение "вообще сменить схему слот-сигнал" выглядит легковесным. Обычно если за плечами уже работающий код, такой путь отвергается сразу. Может простенько спрятать сигнал в унаследованном классе ?
Записан
qate
Супер
******
Offline Offline

Сообщений: 1177


Просмотр профиля
« Ответ #4 : Октябрь 16, 2015, 12:57 »

В системе сигнал-слот нужно обеспечить защищённый вызов сигнала.

сверху написать комментарий: !!! не вызывать из других классов !!!
Записан
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4350



Просмотр профиля
« Ответ #5 : Октябрь 16, 2015, 13:01 »

Код:
    // Invalid usage. emit must be protected.
    shape.emit visibleChanged(true);
А вы это проверяли?
Насколько я помню, signals == protected.
Записан
Bepec
Гость
« Ответ #6 : Октябрь 16, 2015, 13:12 »

Система сигнал слотов в своей основе имеет утверждение - сигнал любого класса может быть вызван при наличии указателя на этот класс. Тем или иным способом. К примеру можно просто вызвать сигнал  QMetaObject::invokeMethod в случае с Qt.

В общем то, что вы хотите это очень сложно и противоречит самой идее Улыбающийся

Разве что написать свою систему сигнал-слотов, только закрытую.
Записан
ViTech
Гипер активный житель
*****
Offline Offline

Сообщений: 858



Просмотр профиля
« Ответ #7 : Октябрь 16, 2015, 13:37 »

А вы это проверяли?
Насколько я помню, signals == protected.

В том-то и дело, что сначала хотел написать: "Чтобы было как в Qt, emit - protected" Улыбающийся. Потом ради смеха набрал эту причудливую конструкцию, она мало того, что скомпилировалась, так и работает. Поведение поменялось в Qt 5.

Signals & Slots Qt 4.8:
Цитировать
Signals

Signals are emitted by an object when its internal state has changed in some way that might be interesting to the object's client or owner. Only the class that defines a signal and its subclasses can emit the signal.

Signals & Slots Qt 5:
Цитировать
Signals

Signals are emitted by an object when its internal state has changed in some way that might be interesting to the object's client or owner. Signals are public access functions and can be emitted from anywhere, but we recommend to only emit them from the class that defines the signal and its subclasses.

Система сигнал слотов в своей основе имеет утверждение - сигнал любого класса может быть вызван при наличии указателя на этот класс. Тем или иным способом. К примеру можно просто вызвать сигнал  QMetaObject::invokeMethod в случае с Qt.

В общем то, что вы хотите это очень сложно и противоречит самой идее Улыбающийся

Мне вот кажется, что всё совсем наоборот Улыбающийся. Может вы о слотах говорите?

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

Про "менять схему сигнал-слот" к теме не относится. Мне интересно, как это выглядит в Boost.
Записан

Пока сам не сделаешь...
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4350



Просмотр профиля
« Ответ #8 : Октябрь 16, 2015, 13:51 »

Мне интересно, как это выглядит в Boost.
Сигнал в бусте это объект. Если его убрать из публичной секции, то эмитеть никто не сможет, кроме самого объекта. Но нужно будет добавить публичный метод для организации подключения (connect).
Записан
ViTech
Гипер активный житель
*****
Offline Offline

Сообщений: 858



Просмотр профиля
« Ответ #9 : Октябрь 16, 2015, 14:02 »

Сигнал в бусте это объект. Если его убрать из публичной секции, то эмитеть никто не сможет, кроме самого объекта. Но нужно будет добавить публичный метод для организации подключения (connect).

И для отключения Улыбающийся. Я примерно представил, как это будет, выходит слишком громоздко. Поэтому и спрашиваю знающих людей, может есть способ покороче всё это описать. Можно с применением шаблонов, на крайний случай с макросами.
Записан

Пока сам не сделаешь...
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4350



Просмотр профиля
« Ответ #10 : Октябрь 16, 2015, 15:50 »

Ну вот что получилось по быстрому:
Код
C++ (Qt)
#include <iostream>
#include <boost/signals2.hpp>
 
using namespace std;
 
void func( int val1, int val2 )
{
cout << "Call func: " << val1 << " " << val2 << endl;
}
 
#define DEF_SIGNAL(name, type) \
public: \
template<typename Slot> \
void connect_##name( Slot slot ) \
{ \
m_##name.connect( slot ); \
} \
\
template<typename Slot> \
void disconnect_##name( Slot slot ) \
{ \
m_##name.disconnect( slot ); \
} \
private: \
boost::signals2::signal<type> m_##name; \
 
class Demo
{
public:
explicit Demo() {}
 
void emit( int val )
{
cout << "Emit signal" << endl;
m_signal( val, val );
}
 
DEF_SIGNAL(signal, void( int, int ) )
};
 
int main()
{
cout << "Start..." << endl;
 
Demo demo;
demo.connect_signal( &func );
demo.emit( 10 );
 
return 0;
}
 
Записан
ViTech
Гипер активный житель
*****
Offline Offline

Сообщений: 858



Просмотр профиля
« Ответ #11 : Октябрь 16, 2015, 17:06 »

Ну вот что получилось по быстрому:
...

Вариант с макросами понятен. На макросах можно много чего быстро наваять Улыбающийся.

По этому поводу интересно мнение: вы бы стали использовать такой вариант в production в большом проекте? Какие плюсы и минусы видите?

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

Минусы:
1. Мне никогда не нравились такие куски кода в макросах. (Пусть это будет личное, субъективное Улыбающийся )
2. Макрос меняет уровень доступа. В примере m_##name расположен в private, хотя надо бы protected, чтобы дочерние классы могли сигналить. Кто-нибудь может забыть после protected написать private, и разрешит защищённый доступ к приватным данным. Может и мелочь, но всё же.
3. Методы типа connect_xxx() нельзя назвать универсальными. Они плохо поддаются дальнейшей обработке макросами, специализациями шаблонов.
4. Если появится "хотелка", чтобы сигналы объявлялись в одном месте (интерфейс, абстрактный базовый класс), а фактически m_##name располагалась в другом классе (реализации). Как быть? (На этот пункт пока не стоит обращать особого внимания)
5. Остальные минусы, присущие макросам в целом.

Boost же ценится шаблонизацией 90-го уровня Улыбающийся. На шаблонах такое можно красиво сделать, с минимумом использования макросов?
Записан

Пока сам не сделаешь...
Racheengel
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2679


Я работал с дискетам 5.25 :(


Просмотр профиля
« Ответ #12 : Октябрь 16, 2015, 17:48 »

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

Просто чтобы понять... Вы хотите предотвратить взлом системы?

Цитировать
По этому поводу интересно мнение: вы бы стали использовать такой вариант в production в большом проекте?

Нет, ибо чукча - не всегда писатель, но иногда и читатель Улыбающийся
Записан

What is the 11 in the C++11? It’s the number of feet they glued to C++ trying to obtain a better octopus.

COVID не волк, в лес не уйдёт
m_ax
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2095



Просмотр профиля
« Ответ #13 : Октябрь 16, 2015, 17:51 »

Цитировать
На шаблонах такое можно красиво сделать, с минимумом использования макросов?

Вообще можно, но из-за двух методов connect/disconnect может и не стоит..

Но как вариант:
Код
C++ (Qt)
template <class, class>
class signal;
 
template <class Friend, class R, class... Args>
class signal<Friend, R(Args...)>
{
public:
   template <class Slot>
   void connect(Slot slot) { m_sig.connect(slot); }
 
  template <class Slot>
   void disconnect(Slot slot) { m_sig.disconnect(slot); }
 
protected:
   friend Friend;
   boost::signal2::signal<R(Args...)> m_sig;
 
   void emit(Args... args) { m_sig.emit(args...); }
};
 
 
class Demo
{
public:
   signal<Demo, void( int )> my_signal;
 
   void emit(int val) { my_signal.emit(val); }
};
 
 
void func( int val )
{
cout << "Call func: " << val <<  endl;
}
 
int main()
{
   Demo demo;
   demo.my_signal.connect(&func);
   demo.emit(10); // ok
   demo.my_signal.emit(10) // Wrong! my_signal.emit is protected!
 
 
   return 0;
}
 
« Последнее редактирование: Октябрь 16, 2015, 18:02 от m_ax » Записан

Над водой луна двурога. Сяду выпью за Ван Гога. Хорошо, что кот не пьет, Он и так меня поймет..

Arch Linux Plasma 5
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4350



Просмотр профиля
« Ответ #14 : Октябрь 16, 2015, 17:52 »

Нет, ибо чукча - не всегда писатель, но иногда и читатель Улыбающийся
Господи, а что плохо читается в этом? Улыбающийся
Код
C++ (Qt)
DEF_SIGNAL( update, void() )
 
Записан
Страниц: [1] 2 3 ... 6   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.146 секунд. Запросов: 23.