Вопрос по c++ – QObject универсальный обработчик сигнала

7

(С «обработчиком сигнала» я имею в виду слоты, а не обработчики для сигналов POSIX.)

Мне нужно & quot; подключить & quot; (probably not с помощьюQObject::connect напрямую)all signals из экземпляра (еще не известного) подкласса QObject дляone single slot другого QObject. Мне это нужно для отправки сигнала (с аргументами) по сети (для собственной системы RPC с поддержкой сигналов).

(Под "еще не известным" я имею в виду, что мой код должен быть настолько универсальным, насколько это возможно. Поэтому он не может содержатьconnect оператор для каждого сигнала в каждом классе, который я использую с моей системой RPC, но что-то вродеRPC::connectAllSignals(QObject*);, который затем сканирует все сигналы во время выполнения и соединяет их.)

То, чего я хотел бы достичь, это: обрабатывать все сигналы и сериализовать их (имя сигнала + аргументы). Я уже могу сериализовать аргументы, но я не знаю, как получить имя сигнала. После поиска в Google, кажется, невозможно использовать что-то подобное, как естьsender() для экземпляра QObject. Поэтому мне нужно сделать что-то гораздо более сложное.

Моя текущая система типов для передачи аргументов целевой функции на удаленном конце в любом случае ограничена некоторыми типами. (Это потому что мне нужноqt_metacall, который исключает аргументы типаvoid* с & quot; правильными типами & quot; позади них. Моя система RPC использует QVariants только с несколькими типами внутри, и я конвертирую их вvoid* правильных типов с использованием пользовательских методов. Я слышал оQVariant::constData слишком поздно, чтобы использовать его, и он, вероятно, все равно не подойдет; поэтому я буду придерживаться своего преобразования типов, если нет недостатка.)

Целевой интервал, в который должны быть отображены все сигналы, должен выглядеть примерно так:

void handleSignal(QByteArray signalName, QVariantList arguments);

Было бы лучше, если бы решение поддерживалось C ++ 03, поэтому я хочу использовать шаблоны с переменным числом аргументов только в том случае, еслиbig недостаток не использовать их. В этом случае C ++ 11 в порядке, поэтому я также рад ответам, использующим C ++ 11.

Теперь мойpossible solution на вопрос, о котором я думаю:

Я мог сканировать все сигналы объекта, используя егоQMetaObject а затем созданиеQSignalMapper (или что-то подобное, что передает все аргументы) дляeach сигнал. Это легко, и мне не нужна помощь с этой стороны. Как упоминалось ранее, я уже ограничен некоторыми типами аргументов, и я также могу жить с ограничением количества аргументов.

Это звучит как грязный хак, но я мог бы использовать какой-то пользовательский, основанный на шаблонах преобразователь сигналов, как этот (в этом примере для трех аргументов):

template<class T1, class T2, class T3>
class MySignalMapper : public QObject
{
    Q_OBJECT
public:
    void setSignalName(QByteArray signalName)
    {
        this->signalName = signalName;
    }
signals:
    void mapped(QByteArray signalName, QVariantList arguments);
public slots:
    void map(T1 arg1, T2 arg2, T3 arg3)
    {
        QVariantList args;
        // QVariant myTypeConverter<T>(T) already implemented:
        args << myTypeConverter(arg1);
        args << myTypeConverter(arg2);
        args << myTypeConverter(arg3);
        emit mapped(signalName, args);
    }
private:
    QByteArray signalName;
};

Тогда я мог бы подключить QMetaMethod под названиемmethod (который известен как сигнал) объекта QObject, называемогоobj как это (который может быть сгенерирован с использованием некоторого вида сценария для всех поддерживаемых типов и количества аргументов ... да ...it's getting dirty!):

    // ...
}
else if(type1 == "int" && type2 == "char" && type3 == "bool")
{
    MySignalMapper<int,char,bool> *sm = new MySignalMapper<int,char,bool>(this);
    QByteArray signalName = method.signature();
    signalName = signalName.left(signalName.indexOf('(')); // remove parameters
    sm->setMember(signalName);

    // prepend "2", like Qt's SIGNAL() macro does:
    QByteArray signalName = QByteArray("2") + method.signature();

    // connect the mapper:
    connect(obj, signalName.constData(),
            sm, SLOT(map(int,char,bool)));
    connect(sm, SIGNAL(mapped(int,char,bool)),
            this, SLOT(handleSignal(const char*,QVariantList)));
}
else if(type1 == ...)
{
    // ...

Как этоmay workЭто действительно грязное решение. Мне нужно либо много макросов, чтобы охватить все комбинации типов максимумN аргументы (гдеN примерно от 3 до 5 (пока не известно) или простой скрипт, генерирующий код для всех случаев. Проблема в том, что это будетlot случаев, когда я поддерживаю около 70 различных типов на аргумент (10 примитивных типов + вложенные списки и карты с глубиной 2 дляevery типа их). Таким образом, для ограничения количества аргументовN имеютсяN ^ 70 случаев, чтобы покрыть!

Is there a completely different approach for this objective, which I'm overlooking?

UPDATE:

Я решил проблему самостоятельно (см. Ответ). Если вы заинтересованы в полном исходном коде, посмотрите мой репозиторий на bitbucket моей системы RPC, который я только что опубликовал: bitbucket.org/leemes/qtsimplerpc

Error: User Rate Limit Exceeded leemes
Error: User Rate Limit ExceededQ_OBJECTError: User Rate Limit ExceededtemplateError: User Rate Limit Exceeded Lol4t0
Error: User Rate Limit Exceeded leemes
Error: User Rate Limit ExceededQSignalSpyError: User Rate Limit Exceededqt-apps.org/content/show.php/… HostileFork
Error: User Rate Limit Exceeded leemes

Ваш Ответ

3   ответа
1

Error: User Rate Limit Exceeded

Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded leemes
Error: User Rate Limit Exceeded leemes
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded leemes
1

Error: User Rate Limit ExceededError: User Rate Limit ExceededError: User Rate Limit Exceeded

Error: User Rate Limit Exceededqt_static_metacallError: User Rate Limit Exceededqt_metacallError: User Rate Limit Exceeded

class QRpcService : public QRpcServiceBase
{
public:
    explicit QRpcService(QTcpServer* server, QObject *parent = 0);
    virtual ~QRpcService();

    virtual int qt_metacall(QMetaObject::Call, int, void**);
private:
    static int s_id_handleRegisteredObjectSignal;
};

Error: User Rate Limit Exceededvoid handleRegisteredObjectSignal()Error: User Rate Limit Exceededstatic intError: User Rate Limit Exceeded

Error: User Rate Limit Exceededvoid**Error: User Rate Limit Exceeded

int QRpcService::qt_metacall(QMetaObject::Call c, int id, void **a)
{
    // only handle calls to handleRegisteredObjectSignal
    // let parent qt_metacall do the rest
    if (id != QRpcService::s_id_handleRegisteredObjectSignal)
        return QRpcServiceBase::qt_metacall(c, id, a);

    // inspect sender and signal
    QObject* o = sender();
    QMetaMethod signal = o->metaObject()->method(senderSignalIndex());
    QString signal_name(signal.name());

    // convert signal args to QVariantList
    QVariantList args;
    for (int i = 0; i < signal.parameterCount(); ++i)
        args << QVariant(signal.parameterType(i), a[i+1]);

    // ...
    // do whatever you want with the signal name and arguments
    // (inspect, send via RPC, push to scripting environment, etc.)
    // ...

    return -1;
}

Error: User Rate Limit Exceeded

Error: User Rate Limit ExceededError: User Rate Limit Exceeded.

5

Error: User Rate Limit ExceededError: User Rate Limit ExceededError: User Rate Limit Exceeded

Error: User Rate Limit Exceededqt_static_metacallError: User Rate Limit ExceededmocError: User Rate Limit Exceeded

Error: User Rate Limit ExceededexampleA(int)Error: User Rate Limit ExceededexampleB(bool)Error: User Rate Limit Exceeded

void ClassName::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
{
    if (_c == QMetaObject::InvokeMetaMethod) {
        Q_ASSERT(staticMetaObject.cast(_o));
        ClassName *_t = static_cast<ClassName *>(_o);
        switch (_id) {
        case 0: _t->exampleA((*reinterpret_cast< int(*)>(_a[1]))); break;
        case 1: _t->exampleB((*reinterpret_cast< bool(*)>(_a[1]))); break;
        default: ;
        }
    }
}

Error: User Rate Limit Exceeded

Error: User Rate Limit Exceeded

class GenericSignalMapper : public QObject
{
    Q_OBJECT
public:
    explicit GenericSignalMapper(QMetaMethod mappedMethod, QObject *parent = 0);
signals:
    void mapped(QObject *sender, QMetaMethod signal, QVariantList arguments);
public slots:
    void map();
private:
    void internalSignalHandler(void **arguments);
    QMetaMethod method;
};

Error: User Rate Limit Exceededmap()Error: User Rate Limit Exceededqt_static_metacallError: User Rate Limit Exceededcase 1):

void GenericSignalMapper::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
{
    if (_c == QMetaObject::InvokeMetaMethod) {
        Q_ASSERT(staticMetaObject.cast(_o));
        GenericSignalMapper *_t = static_cast<GenericSignalMapper *>(_o);
        switch (_id) {
        case 0: _t->mapped((*reinterpret_cast< QObject*(*)>(_a[1])),(*reinterpret_cast< QMetaMethod(*)>(_a[2])),(*reinterpret_cast< QVariantList(*)>(_a[3]))); break;
        case 1: _t->internalSignalHandler(_a); break;
        default: ;
        }
    }
}

Error: User Rate Limit Exceeded

void GenericSignalMapper::internalSignalHandler(void **_a)
{
    QVariantList args;
    int i = 0;
    foreach(QByteArray typeName, method.parameterTypes())
    {
        int type = QMetaType::type(typeName.constData());

        QVariant arg(type, _a[++i]); // preincrement: start with 1
                                     // (_a[0] is return value)
        args << arg;
    }
    emit mapped(sender(), method, args);
}

Error: User Rate Limit ExceededmappedError: User Rate Limit Exceeded

Error: User Rate Limit Exceeded

I still have problems converting the void* arguments to QVariants.Error: User Rate Limit Exceeded_aError: User Rate Limit Exceeded0Error: User Rate Limit Exceeded1.


Example:

Error: User Rate Limit Exceeded

Error: User Rate Limit Exceeded

class Test : public QObject
{
    Q_OBJECT
public:
    explicit Test(QObject *parent = 0);

    void emitTestSignal() {
        emit test(1, 'x');
    }

signals:
    void test(int, char);
};

Error: User Rate Limit Exceeded

class CommonHandler : public QObject
{
    Q_OBJECT
public:
    explicit CommonHandler(QObject *parent = 0);

signals:

public slots:
    void handleSignal(QObject *sender, QMetaMethod signal, QVariantList arguments)
    {
        qDebug() << "Signal emitted:";
        qDebug() << "  sender:" << sender;
        qDebug() << "  signal:" << signal.signature();
        qDebug() << "  arguments:" << arguments;
    }
};

Error: User Rate Limit Exceeded

CommonHandler handler;

// In my scenario, it is easy to get the meta objects since I loop over them.
// Here, 4 is the index of SIGNAL(test(int,char))
QMetaMethod signal = Test::staticMetaObject.method(4);

Test test1;
test1.setObjectName("test1");
Test test2;
test2.setObjectName("test2");

GenericSignalMapper mapper1(signal);
QObject::connect(&test1, SIGNAL(test(int,char)), &mapper1, SLOT(map()));
QObject::connect(&mapper1, SIGNAL(mapped(QObject*,QMetaMethod,QVariantList)), &handler, SLOT(handleSignal(QObject*,QMetaMethod,QVariantList)));

GenericSignalMapper mapper2(signal);
QObject::connect(&test2, SIGNAL(test(int,char)), &mapper2, SLOT(map()));
QObject::connect(&mapper2, SIGNAL(mapped(QObject*,QMetaMethod,QVariantList)), &handler, SLOT(handleSignal(QObject*,QMetaMethod,QVariantList)));

test1.emitTestSignal();
test2.emitTestSignal();

Error: User Rate Limit Exceeded

Signal emitted: 
  sender: Test(0xbf955d70, name = "test1") 
  signal: test(int,char) 
  arguments: (QVariant(int, 1) ,  QVariant(char, ) )  
Signal emitted: 
  sender: Test(0xbf955d68, name = "test2") 
  signal: test(int,char) 
  arguments: (QVariant(int, 1) ,  QVariant(char, ) ) 

Error: User Rate Limit ExceededcharError: User Rate Limit Exceeded

Error: User Rate Limit Exceededbitbucket.org/leemes/qtsimplerpc leemes

Похожие вопросы