Controlador de señal genérico QObject

(Con "manejador de señales" me refiero a las ranuras, no a los manejadores de señales POSIX).

Necesito "conectar" (probablemente no utilizandoQObject::connect directamente)todas las señales de una instancia de una subclase (aún no conocida) de QObject auna sola ranura de otro QObject. Necesito esto para enviar la señal (con argumentos) a través de la red (para un sistema RPC propio con soporte para señales).

(Con "aún no se conoce", quiero decir que mi código debería ser lo más genérico posible. Por lo tanto, no debería contener unaconnect declaración para cada señal en cada clase que estoy usando con mi sistema RPC, pero proporciona algo comoRPC::connectAllSignals(QObject*);, que luego analiza todas las señales durante el tiempo de ejecución y las conecta.)

Lo que me gustaría lograr es: manejar todas las señales y serializarlas (nombre de la señal + argumentos). Ya puedo serializar los argumentos, pero no sé cómo obtener el nombre de la señal. Después de buscar en Google, parece ser imposible usar algo similar, como si hubierasender() para la instancia de QObject. Así que necesito hacer algo mucho más complicado.

Mi sistema de tipos actual para pasar los argumentos a una función de destino en el extremo remoto está restringido a algunos tipos de todos modos. (Eso es porque necesitoqt_metacall, que exceptúa los argumentos por ser de tipovoid* con los "tipos correctos" detrás de ellos. Mi sistema RPC usa QVariants con solo un par de tipos internamente y los convierto avoid* de los tipos correctos utilizando métodos personalizados. Me enteré deQVariant::constData demasiado tarde para usarlo, y probablemente no encajará de todos modos; así que me quedo con mi conversión de tipos si no hay inconveniente.)

La ranura de destino, donde se deben asignar todas las señales, debe parecerse a esto:

void handleSignal(QByteArray signalName, QVariantList arguments);

Sería mejor si la solución es compatible con C ++ 03, por lo que solo quiero usar plantillas variadic si es unagrande inconveniente de no utilizarlos. En este caso, C ++ 11 está bien, así que también estoy contento con las respuestas que utilizan C ++ 11.

Ahora misolución posible a la pregunta que estoy pensando:

Podría escanear todas las señales del objeto utilizando suQMetaObject y luego creando unaQSignalMapper (o algo similar que pasa todos los argumentos) paracada señal. Esto es fácil, y no necesito ayuda en esta parte. Como se mencionó anteriormente, ya estoy restringido a algunos tipos de argumentos, y también puedo vivir con una restricción en el recuento de argumentos.

Suena como un truco sucio, pero podría usar algún tipo de mapeadores de señales basados ​​en plantillas como este (en este ejemplo para tres argumentos):

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;
};

Entonces pude conectar un QMetaMethod llamadomethod (que se sabe que es una señal) de un QObject llamadoobj de esta manera (que podría generarse utilizando algún tipo de script para todos los tipos admitidos y recuentos de argumentos ... sí ...se está ensuciando!):

    // ...
}
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 == ...)
{
    // ...

Como estapodría funcionar, realmente es una solución sucia. Necesitaría una gran cantidad de macros para cubrir todas las combinaciones de tipos, como máximoN argumentos (dondeN es de aproximadamente 3 a 5, aún no se conoce), o un script simple que genera el código para todos los casos. El problema es que esta será unamucho de casos, ya que estoy soportando aproximadamente 70 tipos diferentes por argumento (10 tipos primitivos + listas y mapas anidados con profundidad 2 paracada tipo de ellos). Así que para un límite de conteo de argumentos deN existenN ^ 70 cajas para cubrir!

¿Hay un enfoque completamente diferente para este objetivo, que estoy pasando por alto?

ACTUALIZAR:

Resolví el problema por mi cuenta (ver respuesta). Si está interesado en el código fuente completo, vea mi repositorio en bitbucket de mi sistema RPC, que acabo de publicar: bitbucket.org/leemes/qtsimplerpc

Respuestas a la pregunta(3)

Su respuesta a la pregunta