Название: Как обработать сигнал одним слотом из подключенных? Отправлено: Гурман от Сентябрь 01, 2014, 17:36 Есть несколько одинаковых объектов-приемников, у которых есть некий слот. Ко всем этим объектам подключен один сигнал одного объекта-источника, одновременно. В норме активация сигнала приводит к получению его всеми слотами и обработке всеми объектами этого класса. Точнее, всеми экземплярами объектов, как я сказал, их несколько. Но нужно, чтобы сигнал обработался только одним объектом, который получит сигнал первым, а остальные его проигнорировали. Проблема в том, что спецификатор static для создания разделяемого данного, и использования как семафора, не подходит - все объекты, это загруженные Qt-плагины. А в вениках, как известно, память данных у каждой DLL своя.
И отсюда вопрос - можно ли как-то внутри экземпляра объекта узнать, что он получил сигнал не первым? Вроде было такое где-то в QMeta???, но что-то не получается найти. Название: Re: Как обработать сигнал одним слотом из подключенных? Отправлено: Old от Сентябрь 01, 2014, 17:39 А зачем подключать слоты объектов к сигналу, если они его должны игнорировать?
Почему бы не подключать только один из объектов? Название: Re: Как обработать сигнал одним слотом из подключенных? Отправлено: Bepec от Сентябрь 01, 2014, 18:04 Т.к. слоты подключают сигнал в очередности их подключения (connect), то затея теряет смысл :) Всегда будет обрабатываться один и тот же слот.
Но если опустить эту досадную особенность, то без координации тут никак - объекты должны знать хотя бы 1 общий ресурс в котором и будет разруливаться логика. Название: Re: Как обработать сигнал одним слотом из подключенных? Отправлено: Гурман от Сентябрь 01, 2014, 18:48 А зачем подключать слоты объектов к сигналу, если они его должны игнорировать? Почему бы не подключать только один из объектов? Разные объекты в плагинах, какие-то могут не быть загружены (отстуствовать в поставке). Могут присутствовать несколько. А обработать сигнал должен только один. Название: Re: Как обработать сигнал одним слотом из подключенных? Отправлено: Гурман от Сентябрь 01, 2014, 18:53 Т.к. слоты подключают сигнал в очередности их подключения (connect), то затея теряет смысл :) Всегда будет обрабатываться один и тот же слот. Но если опустить эту досадную особенность, то без координации тут никак - объекты должны знать хотя бы 1 общий ресурс в котором и будет разруливаться логика. Что один и тот же - плевать, важно, чтобы сигнал не получили или проигнорировали остальные. С общим ресурсом хреново... его можно только в головном приложении выделить, но дюже муторно получается. Сигналы получают плагины, а они по идее, максимально независимы от головного приложения, обмениваются данными только через сигнал-слоты. Вроде должно получиться с помощью invokeMethod(), поскольку реестр плагинов уже и так есть, достаточно пробежать по нему и с помощью invokeMethod() найти первый подходящий и не занятый плагин - кроме подходящих, есть еще и не подходящие, а у подходящего нить может быть занята обработкой. Название: Re: Как обработать сигнал одним слотом из подключенных? Отправлено: Old от Сентябрь 01, 2014, 19:08 Можно проверять сигнал перед коннектом: если к нему уже кто-то подключен, то не подключаться.
bool QObject::isSignalConnected(const QMetaMethod & signal) const [protected] Название: Re: Как обработать сигнал одним слотом из подключенных? Отправлено: Гурман от Сентябрь 01, 2014, 19:29 Можно проверять сигнал перед коннектом: если к нему уже кто-то подключен, то не подключаться. bool QObject::isSignalConnected(const QMetaMethod & signal) const [protected] Не, нельзя... Задача сложнее - первый, кто получил сигнал, становится занят на неопределенное время, за которое может прийти следующий сигнал, значит надо, чтобы сигнал получил другой, и обработал, если свободен. Слишком сложная получается логика подключений-отключений. Пока без invokeMethod не понятно, как это можно иначе сделать. Название: Re: Как обработать сигнал одним слотом из подключенных? Отправлено: m_ax от Сентябрь 01, 2014, 22:01 Можно проверять сигнал перед коннектом: если к нему уже кто-то подключен, то не подключаться. bool QObject::isSignalConnected(const QMetaMethod & signal) const [protected] Не, нельзя... Задача сложнее - первый, кто получил сигнал, становится занят на неопределенное время, за которое может прийти следующий сигнал, значит надо, чтобы сигнал получил другой, и обработал, если свободен. Слишком сложная получается логика подключений-отключений. Пока без invokeMethod не понятно, как это можно иначе сделать. Сделайте промежуточное звено (менеджер сигналов).. Пусть ловит сигналы и уже после (исходя из вашей задачи) перенаправляет их кому нужно и как нужно.. Название: Re: Как обработать сигнал одним слотом из подключенных? Отправлено: __Heaven__ от Сентябрь 02, 2014, 06:59 А нельзя ли объявить глобальную переменную-семафор, а при первом вызове метода осуществить дисконнект от прочих слотов?
Название: Re: Как обработать сигнал одним слотом из подключенных? Отправлено: GreatSnake от Сентябрь 02, 2014, 07:32 А нельзя ли объявить глобальную переменную-семафор, а при первом вызове метода осуществить дисконнект от прочих слотов? Выстави QApplication::property().Название: Re: Как обработать сигнал одним слотом из подключенных? Отправлено: Igors от Сентябрь 02, 2014, 08:47 Самое простое наверное и будет самым лучшим - подать флажок "принято" в параметрах сигнала. Напр использовать boost::optional
Название: Re: Как обработать сигнал одним слотом из подключенных? Отправлено: Гурман от Сентябрь 02, 2014, 10:31 Сделайте промежуточное звено (менеджер сигналов).. Пусть ловит сигналы и уже после (исходя из вашей задачи) перенаправляет их кому нужно и как нужно.. В этом нет смысла, так менеджить сигналы можно было бы и в отправителе. Если бы можно было. Но такой вариант слишком усложняет реализацию. Название: Re: Как обработать сигнал одним слотом из подключенных? Отправлено: Гурман от Сентябрь 02, 2014, 10:43 Самое простое наверное и будет самым лучшим - подать флажок "принято" в параметрах сигнала. Напр использовать boost::optional boost не использую сигнал - не объект, где у него есть состояние "принято"? а насчет флажка в данных сигнала - я изначально считаю, что каждый слот, подключенный к одному сигналу, получает свою копию данных, то есть, флажок не является глобальным для принимающих объектов а разве нет? Название: Re: Как обработать сигнал одним слотом из подключенных? Отправлено: Bepec от Сентябрь 02, 2014, 11:32 Цитировать Выстави QApplication::property(). qApp один на всю программу. Соответственно и флажок будет одним.Название: Re: Как обработать сигнал одним слотом из подключенных? Отправлено: navrocky от Сентябрь 02, 2014, 11:39 В сигнале передай ссылку на структуру с флажком обработанности и полезными данными. В слоте будешь проверять, что сигнал уже обработали по этому флажку. При обработке флажок взводить.
Для больше безопасности её лучше завернуть в shared_ptr. Код
Название: Re: Как обработать сигнал одним слотом из подключенных? Отправлено: GreatSnake от Сентябрь 02, 2014, 11:43 Цитировать Выстави QApplication::property(). qApp один на всю программу. Соответственно и флажок будет одним.Ко всем этим объектам подключен один сигнал одного объекта-источника, одновременно. Если позволяет архитектура, такой флажок можно завести в объекте-источнике ( sender() ).Название: Re: Как обработать сигнал одним слотом из подключенных? Отправлено: Гурман от Сентябрь 02, 2014, 11:51 Уже сделал на invokeMethod(). Проще всего, ничего лишнего. Реестр плагинов имеется, нашел в нем первый подходящий и не занятый, и в нем вызвал метод. У всех подходящих плагинов имя этого метода одинаковое, поскольку наследуют от одного класса. Всё. Работает. Даже устанавливать соединения не требуется.
Название: Re: Как обработать сигнал одним слотом из подключенных? Отправлено: Гурман от Сентябрь 02, 2014, 11:55 Ко всем этим объектам подключен один сигнал одного объекта-источника, одновременно. Если позволяет архитектура, такой флажок можно завести в объекте-источнике ( sender() ). В архитектуре постулировано, что плагины с родительским приложением и наоборот общаются только через сигнал-слоты. А они при обычных соединениях значения не возвращают. Но вот invoikeMethod() позволяет получить возвращаемое значение, хотя является эквивалентом временного соединения сигнал-слот, поскольку работает на том же механизме. Название: Re: Как обработать сигнал одним слотом из подключенных? Отправлено: Igors от Сентябрь 02, 2014, 11:57 Уже сделал на invokeMethod(). Проще всего, ничего лишнего. Реестр плагинов имеется, нашел в нем первый подходящий и не занятый, и в нем вызвал метод. У всех плагинов имя этого метода одинаковое, поскольку наследуют от одного класса. Всё. Работает. Даже устанавливать соединения не требуется. Безыдейно (хотя и просто и работает)Название: Re: Как обработать сигнал одним слотом из подключенных? Отправлено: Гурман от Сентябрь 02, 2014, 11:59 Безыдейно (хотя и просто и работает) Принцип бритвы Оккама - самое простое решение является самым правильным. ;D Название: Re: Как обработать сигнал одним слотом из подключенных? Отправлено: GreatSnake от Сентябрь 02, 2014, 12:04 Безыдейно (хотя и просто и работает) Имхо в данном случае самое простое решение.И если бы автор изначально по-конкретнее поставил задачу, такого количества костылей ему бы не было предложено :) Название: Re: Как обработать сигнал одним слотом из подключенных? Отправлено: Гурман от Сентябрь 03, 2014, 14:21 Хе... Заметил попутно такой факт, не совсем в тему, но в принципе можно было бы использовать и тут - если класс хранения переменной в плагине статический (static, или глобально без модификатора, или extern), и плагин загружается из той же DLL повторно, то эта переменная будет общей для всех загруженных версий плагина (динамическая память и стек у каждого будут, разумеется, свои). Но если плагин загрузить с таким же кодом, но из DLL с другим именем (из копии файла DLL), то статически хранимые переменные будут для всех версий свои. По крайней мере, так в вениках. Как в Линухе еще не знаю, до сборки проекта в нем еще далеко.
По идее, все правильно, поскольку статически хранимые переменные размещаются в общем сегменте данных, который вместе с сегментом кода отображается в оперативную память процессора. Но неоднократно читал (делать такое не приходилось), что в вениках такие переменные у каждого экземпляра плагина свои - правда это могло иметь отношение к динамическим библиотекам и плагинам, создаваемым с помощью мелкомягкого компоновщика. Тут же GNUтое всё... Наверняка и в Линухе всё также будет работать, но на 100% в мультплатформенном приложении полагаться на такое размещение нельзя, могут быть проблемы. Название: Re: Как обработать сигнал одним слотом из подключенных? Отправлено: Igors от Сентябрь 03, 2014, 14:54 Замеченный Вами факт я использовал не один десяток лет - когда не было нормального способа API заставлял юзера цеплять один плагин в неск местах приложения, и дальше взаимодействие через общие переменные.
и плагин загружается из той же DLL повторно, Нет никакой "повторной загрузки" - просто увеличивается счетчик использования dll.(динамическая память и стек у каждого будут, разумеется, свои). Не свои а вызывающегоПо идее, все правильно, поскольку статически хранимые переменные размещаются в общем сегменте данных, который вместе с сегментом кода отображается в оперативную память процессора. Нет. Напр если 5 процессов загрузили одну dll все используют одну копию кода и 5 копий данных. В Вындоуз можно сделать данные общими, но надо сильно постараться. На никсовых ОС все то же самое, читайте dlopen и dlsym. Помню правил такой баг: загрузил либу - выгрузил - загрузил опять. Оба-на! Переменные остались с предыдущей загрузки :) Название: Re: Как обработать сигнал одним слотом из подключенных? Отправлено: Гурман от Сентябрь 03, 2014, 15:30 и плагин загружается из той же DLL повторно, Нет никакой "повторной загрузки" - просто увеличивается счетчик использования dll.Да, нашел уже описание. Странно, что раньше это не попадалось. (динамическая память и стек у каждого будут, разумеется, свои). Не свои а вызывающегоЯ имел в виду - разные. Это понятно, что у приложения куча одна и стек один. По идее, все правильно, поскольку статически хранимые переменные размещаются в общем сегменте данных, который вместе с сегментом кода отображается в оперативную память процессора. Нет. Напр если 5 процессов загрузили одну dll все используют одну копию кода и 5 копий данных. В Вындоуз можно сделать данные общими, но надо сильно постараться. Это если 5 разных процессов. Я имел в виду случай загрузки одной DLL несколько раз одним и тем же процессом. Статические данные вместе с кодом отобразились один раз, и как вы выше сказали, затем только увеличивается счетчик использования - не только сегмента кода, но и сегмента данных. На никсовых ОС все то же самое, читайте dlopen и dlsym. Помню правил такой баг: загрузил либу - выгрузил - загрузил опять. Оба-на! Переменные остались с предыдущей загрузки :) Вот это теперь для меня вполне обоснованно. Сижу теперь и думаю, что в реальной жизни будет лучше, если по условию для каждого плагина надо иметь собственные сегменты данных - плодить копии плагинов и грузить их как отдельные, либо переделывать плагины, чтобы у них не было статических данных, все только динамические. Первый вариант проще. Но это приведет к многократной загрузке и дублированию в памяти одинакового кода, хотя он и не страшно смертельного объема, около 800 КБ на плагин, но может вырасти до 2 МБ. Максимум в реальности будет загружаться 10-20 таких плагинов. В принципе, не смертельно. Но надо еще придумать, как и когда создавать копии. Второй вариант - это переделка всего такого плагина, а он... сложный. И давно написанный и хорошо отлаженный. И статические данные там неспроста появились. Название: Re: Как обработать сигнал одним слотом из подключенных? Отправлено: Igors от Сентябрь 03, 2014, 17:53 Сижу теперь и думаю, что в реальной жизни будет лучше, если по условию для каждого плагина надо иметь собственные сегменты данных - плодить копии плагинов и грузить их как отдельные, либо переделывать плагины, чтобы у них не было статических данных, все только динамические. Только динамические - необязательно, нередко что-то даже лучше иметь статичным. Но основные/рабочие параметры да, в куче и у каждого экземпляра свои. Для этого есть хороший критерий: multi-threading. Как только потребуется параллельное выполнение вся статика рухнет. Боязнь переделок понятна - плагинов-то совсем не один, могут быть писаны давно и хз как теперь компилить. Но все же чем раньше - тем лучше. Возможный подход: на фазе инициализации плагин выделяет блок памяти где размещает рабочие данные/параметры. Адрес этого блока возвращается хосту, а тот при каждом вызове плагина опять подает ему этот же адрес. Название: Re: Как обработать сигнал одним слотом из подключенных? Отправлено: Гурман от Сентябрь 03, 2014, 22:17 Именно о параллельном выполнении и речь. Переделывать плагины пока не стал, хотя оно все тут же, связано зависимостями с проектом, и можно менять. Но сделал пока простой вариант - автоматически при необходимости на носителе создаются копии плагина, которые загружаются. Если копия уже есть, то просто загружается, не создается. Просто надо другое делать, всего делать вообще дофига, а переделка задержит на неопределенное время. Там в плагинах сложно, около 15000 строк... В следующей версии можно будет и переделать, если понадобится, это как отдельная задача фактически.
|