Russian Qt Forum

Qt => Общие вопросы => Тема начата: Гурман от Октябрь 15, 2014, 21:32



Название: [РЕШЕНО] Массив сигналов...
Отправлено: Гурман от Октябрь 15, 2014, 21:32
Нужно собрать однотипные сигналы в массив и эмитировать их по номеру. Но в C++ запрещено получать адрес функции-члена класса для динамического объекта. А статический вариант не приемлем для сигналов, при emit используется адрес несуществующего объекта. Подключения сигналов могут динамически меняться, поэтому invokeMethod не годится. Можно, конечно, влоб длинный switch/case написать (на самом деле, оптимизатор такую колбасу в массив и загоняет), этих сигналов не так много. Но может есть какие-то подводные возможности... В описаниях всяких "мета" ничего подходящего не нашел. Хотя заглянул в исходники Qt - там внутри после входа в мета-функцию сигнала дальше производится обработка по номерам. Может есть какой легальный, но недокументированный способ для QObject эмитировать свой сигнал по его номеру?


Название: Re: Массив сигналов...
Отправлено: m_ax от Октябрь 15, 2014, 22:58
Цитировать
Но в C++ запрещено получать адрес функции-члена класса для динамического объекта.
Да ладно? O_o


Название: Re: Массив сигналов...
Отправлено: Гурман от Октябрь 15, 2014, 23:24
Цитировать
Но в C++ запрещено получать адрес функции-члена класса для динамического объекта.
Да ладно? O_o

Код:
typedef void ( *sox )( QVariant );

class CiControl : public QObject
{
...
signals:    
    void universalOut01(QVariant);
...
private:
    sox sigarray[32];
}


SEGINTERFACES* CiControl::getInterfaces()
{
...
    sigarray[0] = (sox) &universalOut01; // строка 93
...
}

Цитировать
I:\Qt\2010.05s\citypes\Documents\cincontrol-build-desktop\..\cicontrol\cicontrol.cpp:93: ошибка: ISO C++ forbids taking the address of an unqualified or parenthesized non-static member function to form a pointer to member function.  Say '&CiControl::universalOut01
- то есть, статически можно, и

Код:
SEGINTERFACES* CiControl::getInterfaces()
{
...
    sigarray[0] = (sox) &CiControl::universalOut01;
...
}

компилируется, но не работает, при попытке emit sigarray[0]( variant ); падает глубоко внутри Qt, поскольку this там содержит адрес вовсе не того же инстанса объекта, в котором делается emit

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

но вопрос был не об этом


Название: Re: Массив сигналов...
Отправлено: m_ax от Октябрь 15, 2014, 23:54
Если я правильно понял, то я бы сделал как то так:

Код
C++ (Qt)
class CiControl : public QObject
{
public:
   signal<QVariant> sigarray[32];    
 
}
 
Сигналы бы использовал либо бустовские, либо, например ssp::signal http://www.prog.org.ru/topic_16829_105.html (http://www.prog.org.ru/topic_16829_105.html) (https://www.gitorious.org/lightssp (https://www.gitorious.org/lightssp))

Пример для ssp::signal, это выглядело бы так:
Код
C++ (Qt)
#include <signal_slot.h>
 
class CiControl
{
public:
   ssp::signal<int> sigarray[32];
};
 
class A : public ssp::trackable
{
public:
   void slot1(int arg) { std::cout << "A::slot1(int arg); arg = " << arg << std::endl; }
   void slot2(int arg) { std::cout << "A::slot2(int arg); arg = " << arg << std::endl; }
};
 
int main()
{
   CiControl ci_control;
 
   A a1, a2;
 
   ci_control.sigarray[0].connect(&a1, &A::slot1);
   ci_control.sigarray[0].connect(&a1, &A::slot2);
   ci_control.sigarray[1].connect(&a2, &A::slot1);
   ci_control.sigarray[1].connect(&a2, &A::slot2);
 
   ci_control.sigarray[0](1005001);
   ci_control.sigarray[1](1005002);
 
   return 0;
}
 


Название: Re: Массив сигналов...
Отправлено: Igors от Октябрь 16, 2014, 09:23
В описаниях всяких "мета" ничего подходящего не нашел.
А примерчик в QMetaObject::methodCount ? Во всяком случае имена всех сигналов есть, типы аргументов тоже. Напр PythonQt такими вызовами занимается, так что дело рядовое (и документированное).  Что имелось ввиду под "номером" - хз, поэтому больше сказать нечего.


Название: Re: Массив сигналов...
Отправлено: Гурман от Октябрь 16, 2014, 10:23
В описаниях всяких "мета" ничего подходящего не нашел.
А примерчик в QMetaObject::methodCount ? Во всяком случае имена всех сигналов есть, типы аргументов тоже. Напр PythonQt такими вызовами занимается, так что дело рядовое (и документированное).  Что имелось ввиду под "номером" - хз, поэтому больше сказать нечего.

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

"по номеру" означает, по целочисленному индексу

Сигналы бы использовал либо бустовские, либо, например ssp

ни boost, ни ssp не используются по техническим условиям, только Qt

патамушта...



Название: Re: Массив сигналов...
Отправлено: Johnik от Октябрь 16, 2014, 10:42
methodCount просто возвращает количество методов, включая не-сигналы - пользы от него никакой
далее получаем информацию по методу:
Код
C++ (Qt)
QMetaMethod QMetaObject::method(int index) const
и QMetaMethod содержит:
Код
C++ (Qt)
MethodType QMetaMethod::methodType() const



Название: Re: Массив сигналов...
Отправлено: Igors от Октябрь 16, 2014, 10:45
methodCount просто возвращает количество методов, включая не-сигналы - пользы от него никакой

"по номеру" означает, по целочисленному индексу
Впечатление что Вы раздражены, типа "ну чего всякую ерунду пердлагаете" :) А может это не ерунда? Вот кусок кода выдрал наобум из PythonQt
Код
C++ (Qt)
   const QMetaObject* meta = decoratorProvider->metaObject();
   int numMethods = meta->methodCount();
   int startFrom = QObject::staticMetaObject.methodCount();
   for (int i = startFrom; i < numMethods; i++) {
     QMetaMethod m = meta->method(i);
     if ((m.methodType() == QMetaMethod::Method ||
          m.methodType() == QMetaMethod::Slot) && m.access() == QMetaMethod::Public) {
 


Название: Re: Массив сигналов...
Отправлено: m_ax от Октябрь 16, 2014, 11:05
ни boost, ни ssp не используются по техническим условиям, только Qt

патамушта...

Ну так такое поведение можно реализовать и с Qt-шными сигналами..


Название: Re: Массив сигналов...
Отправлено: Гурман от Октябрь 16, 2014, 11:11
methodCount просто возвращает количество методов, включая не-сигналы - пользы от него никакой

"по номеру" означает, по целочисленному индексу
Впечатление что Вы раздражены, типа "ну чего всякую ерунду пердлагаете" :) А может это не ерунда? Вот кусок кода выдрал наобум из PythonQt
Код
C++ (Qt)
   const QMetaObject* meta = decoratorProvider->metaObject();
   int numMethods = meta->methodCount();
   int startFrom = QObject::staticMetaObject.methodCount();
   for (int i = startFrom; i < numMethods; i++) {
     QMetaMethod m = meta->method(i);
     if ((m.methodType() == QMetaMethod::Method ||
          m.methodType() == QMetaMethod::Slot) && m.access() == QMetaMethod::Public) {
 

И вызывать этот мета-метод через invoke()?... Вай. Ну может быть, так и можно, хотя выглядит шибко громоздко. Возможно это вариант решения. Пока наваял switch/case и он работает, причем наверняка быстрее, чем такие макароны. И выглядит даже изящнее.

Код:
            switch( i )
            {
            case 0: emit universalOut01( container ); break;
            case 1: emit universalOut02( container ); break;
            ....
            }


Название: Re: Массив сигналов...
Отправлено: Igors от Октябрь 16, 2014, 11:31
И вызывать этот мета-метод через invoke()?... Вай. Ну может быть, так и можно, хотя выглядит шибко громоздко. Возможно это вариант решения. Пока наваял switch/case и он работает, причем наверняка быстрее, чем такие макароны. И выглядит даже изящнее.
Ну чтобы так наваять не нужно создавать тему на форуме  :)


Название: Re: Массив сигналов...
Отправлено: Гурман от Октябрь 16, 2014, 11:36
И вызывать этот мета-метод через invoke()?... Вай. Ну может быть, так и можно, хотя выглядит шибко громоздко. Возможно это вариант решения. Пока наваял switch/case и он работает, причем наверняка быстрее, чем такие макароны. И выглядит даже изящнее.
Ну чтобы так наваять не нужно создавать тему на форуме  :)

Ситуация может поменяться. Для этого объекта пока хватило такого варианта, для другого уже может быть не пригодно. Или для этого может.

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


Название: Re: Массив сигналов...
Отправлено: m_ax от Октябрь 16, 2014, 12:05
Да просто заверните сигнал в свой объект и создавайте потом из них хоть массив, хоть что (псевдокод):

Код
C++ (Qt)
class signal_wrapper : public QObject
{
...
signals:
   void signal(QVariant);
 
};
 
class CiControl : public QObject
{
public:
   signal_wrapper sigarray[32];    
 
}
 


Название: Re: Массив сигналов...
Отправлено: Гурман от Октябрь 16, 2014, 12:11
Да просто заверните сигнал в свой объект и создавайте потом из них хоть массив, хоть что (псевдокод):

Код
C++ (Qt)
class signal_wrapper : public QObject
{
...
signals:
   void signal(QVariant);
 
};
 
class CiControl : public QObject
{
public:
   signal_wrapper sigarray[32];    
 
}
 

Не годится. Подключать (connect) придется не тот объект, который де-факто является источником сигналов. А это по задаче существенно. Получатель определяет, от кого пришел сигнал с помощью QSignalMaper. Да и не только по этой причине.



Название: Re: Массив сигналов...
Отправлено: Old от Октябрь 16, 2014, 13:01
Код
C++ (Qt)
#ifndef OBJECT_H
#define OBJECT_H
 
#include <QObject>
#include <QVector>
 
class Object : public QObject
{
Q_OBJECT
public:
explicit Object( QObject *parent = 0 );
 
void emitNum( int index, const QVariant &val );
 
signals:
void sig0( const QVariant &val );
void sig1( const QVariant &val );
void sig2( const QVariant &val );
void sig3( const QVariant &val );
void sig4( const QVariant &val );
 
private:
typedef void (Object::*signal_ptr)( const QVariant &val );
QVector<signal_ptr> m_sigs;
};
 
#endif // OBJECT_H
 

Код
C++ (Qt)
#include "object.h"
 
Object::Object( QObject *parent ) :
QObject( parent ),
m_sigs( 5 )
{
m_sigs[ 0 ] = &Object::sig0;
m_sigs[ 1 ] = &Object::sig1;
m_sigs[ 2 ] = &Object::sig2;
m_sigs[ 3 ] = &Object::sig3;
m_sigs[ 4 ] = &Object::sig4;
}
 
void Object::emitNum( int index , const QVariant &val )
{
Q_ASSERT( index >= 0 && index < m_sigs.size() );
signal_ptr func = m_sigs[ index ];
(this->*func)( val );
}
 


Название: Re: Массив сигналов...
Отправлено: Гурман от Октябрь 16, 2014, 15:39
Код
C++ (Qt)
#include "object.h"
 
Object::Object( QObject *parent ) :
QObject( parent ),
m_sigs( 5 )
{
m_sigs[ 0 ] = &Object::sig0;
 
 

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

m_sigs[ 0 ] = &Object::sig0;

присваивается адрес метода некоего статического объекта, то и внутри этот метод получит указатель this на свой объект, а вовсе не на тот, в котором делается вызов (this->*func)( val );

кстати, moc колбасит для вызовов сигналов вот такой вот код:

Код:
int CiControl::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
{
    _id = QObject::qt_metacall(_c, _id, _a);
    if (_c == QMetaObject::InvokeMetaMethod) {
        switch (_id) {
        case 0: localMenuEdit((*reinterpret_cast< QString(*)>(_a[1]))); break;
        case 1: localMenuDebug((*reinterpret_cast< QString(*)>(_a[1]))); break;
        case 2: localMenuRun((*reinterpret_cast< QString(*)>(_a[1])),(*reinterpret_cast< bool(*)>(_a[2]))); break;
        case 3: busyPlugin((*reinterpret_cast< QString(*)>(_a[1])),(*reinterpret_cast< QString(*)>(_a[2]))); break;
        case 4: universalOut01((*reinterpret_cast< QVariant(*)>(_a[1]))); break;
        .....

один в один то, что я написал...  :D


Название: Re: Массив сигналов...
Отправлено: Old от Октябрь 16, 2014, 16:27
Подозреваю, что не будет работать по той же причине, что и пример в конце моего второго поста. Поскольку тут
Не правильно подозреваете. Это реальные файлы из тестового проекта, который работает.


Название: Re: Массив сигналов...
Отправлено: Гурман от Октябрь 16, 2014, 16:33
Подозреваю, что не будет работать по той же причине, что и пример в конце моего второго поста. Поскольку тут
Не правильно подозреваете. Это реальные файлы из тестового проекта, который работает.

Хм. Тогда это весьма элегантное решение. Попробую у себя потом.


Название: Re: Массив сигналов...
Отправлено: Old от Октябрь 16, 2014, 16:48
Хм. Тогда это весьма элегантное решение. Попробую у себя потом.
Ловите тогда тестовый проект целиком.


Название: Re: Массив сигналов...
Отправлено: Гурман от Октябрь 16, 2014, 16:54
Хм. Тогда это весьма элегантное решение. Попробую у себя потом.
Ловите тогда тестовый проект целиком.


Спасибо, но мне вполне достаточно того, что реплике видно.  ;D
Может кому-нибудь пригодится потом.


Название: Re: Массив сигналов...
Отправлено: Igors от Октябрь 16, 2014, 17:54
присваивается адрес метода некоего статического объекта, то и внутри этот метод получит указатель this на свой объект, а вовсе не на тот, в котором делается вызов (this->*func)( val );
Присваивается адрес не экземпляра, а типа (точнее метода), сохраненный адрес будет корректно вызываться для любого экземпляра такого типа или порожденного. Но не для др типа.


Название: Re: Массив сигналов...
Отправлено: Гурман от Октябрь 16, 2014, 18:04
присваивается адрес метода некоего статического объекта, то и внутри этот метод получит указатель this на свой объект, а вовсе не на тот, в котором делается вызов (this->*func)( val );
Присваивается адрес не экземпляра, а типа (точнее метода), сохраненный адрес будет корректно вызываться для любого экземпляра такого типа или порожденного. Но не для др типа.

Я не говорил, что присваивается адрес экземпляра. Вызываться он будет, разумеется, но важно не это, а то, на какой экземпляр будет ему передаваться указатель this. В случае стандартного emit передается вовсе не указатель на объект, который сделал этот emit, а на объект, который был создан при присвоении. Но при прямом вызове, очевидно передается таки свой this.


Название: Re: Массив сигналов...
Отправлено: m_ax от Октябрь 16, 2014, 18:20
Цитировать
но важно не это, а то, на какой экземпляр будет ему передаваться указатель this. В случае стандартного emit передается вовсе не указатель на объект, который сделал этот emit, а на объект, который был создан при присвоении. Но при прямом вызове, очевидно передается таки свой this.
Что то какой то несвязанный поток слов пошёл.. Ничего не понял, что хотели сказать..


Название: Re: Массив сигналов...
Отправлено: Igors от Октябрь 16, 2014, 18:56
Я не говорил, что присваивается адрес экземпляра. Вызываться он будет, разумеется, но важно не это, а то, на какой экземпляр будет ему передаваться указатель this. В случае стандартного emit передается вовсе не указатель на объект, который сделал этот emit, а на объект, который был создан при присвоении.
При каком присвоении ??? С точки зрения языка moc ничего не меняет. Макро emit пустое, чисто для понту. Сигнал - самый обычный метод, moc только создает тело. В этот метод передается this как и во все остальные. Др дело что пока дойдет до слота этого this уже не будет.

Я так и не понял что Вы хотели.


Название: Re: Массив сигналов...
Отправлено: Гурман от Октябрь 16, 2014, 20:20
При присвоении адреса метода элементу массива.

Я делал так

Код:
typedef void ( *sox )( QVariant );

class CiControl : public QObject
{
...
private:
    sox sigarray[32];
...
signals:     
    void universalOut01(QVariant);
...
}

SEGINTERFACES* CiControl::getInterfaces()
{
...
    sigarray[0] = (sox) &CiControl::universalOut01;
...
}

int CiControl::universalSend( unsigned int i, Qvariant container )
{
...
    emit sigarray[ i ]( container );
...
}

падает глубоко внутри при входе в sigarray[ i ]( container ), то есть, в universalOut01(QVariant), и в отладчике при входе в эту функцию видно, что указатель this содержит адрес совсем не того объекта, который сделал emit, и да, того объекта уже нет, отладчик не показывает значения его свойств

вот почему при вызове sigarray[ i ]( container ) передается this не того объекта, который делает этот вызов, я не совсем понял


Название: Re: Массив сигналов...
Отправлено: Old от Октябрь 16, 2014, 20:24
Это
Код
C++ (Qt)
typedef void ( *sox )( QVariant );
совсем не указатель на метод класса.

И так присваивать нельзя.
Код
C++ (Qt)
sigarray[0] = (sox) &CiControl::universalOut01;

Не нужно в c++ использовать сишные касты. Вот вы получили за это по рукам.


Название: Re: Массив сигналов...
Отправлено: Гурман от Октябрь 16, 2014, 20:36
Это
Код
C++ (Qt)
typedef void ( *sox )( QVariant );
совсем не указатель на метод класса.


А! Точно. Это моя ошибка. Надо было typedef внутри класса делать, с указанием его имени. Тогда заработает.


Название: Re: Массив сигналов...
Отправлено: Old от Октябрь 16, 2014, 20:38
Надо было typedef внутри класса делать, с указанием его имени.
Не обязательно внутри класса, но обязательно с именем класса.


Название: (РЕШЕНО) Re: Массив сигналов...
Отправлено: Гурман от Октябрь 16, 2014, 20:44
Надо было typedef внутри класса делать, с указанием его имени.
Не обязательно внутри класса, но обязательно с именем класса.

имя становится известно внутри класса, иначе надо отдельно класс-пустышку объявлять, так что лучше всего внутри ;)

только что проверил:

Код:
Код:

class CiControl : public QObject
{
...
private:
    typedef void (CiControl::*sox)( QVariant );
    sox sigarray[32];
...
signals:    
    void universalOut01(QVariant);
...
}

SEGINTERFACES* CiControl::getInterfaces()
{
...
    sigarray[0] = &CiControl::universalOut01;
...
}

int CiControl::universalSend( unsigned int i, QVariant container )
{
...
    emit (this->*sigarray[ i ])( container );
...
}

Работает. Тему можно закрыть.

ЗЫ: Вот это еще бы как-то более человечно сделать:

Код:
    sigarray[0] = &CiControl::universalOut01;
    sigarray[1] = &CiControl::universalOut02;
    sigarray[2] = &CiControl::universalOut03;
    .........
    .........
    .........

надо завтра попробовать через QMetaMethod