QObject generic handler sygnału

(W przypadku „obsługi sygnału” mam na myśli gniazda, a nie procedury obsługi sygnałów POSIX).

Muszę „połączyć się” (prawdopodobnie nie za pomocąQObject::connect bezpośrednio)wszystkie sygnały z instancji (jeszcze nieznanej) podklasy QObject dojedno gniazdo innego QObject. Potrzebuję tego, aby wysłać sygnał (z argumentami) przez sieć (dla własnego systemu RPC z obsługą sygnałów).

(Z „jeszcze nieznanym” mam na myśli, że mój kod powinien być tak ogólny, jak to możliwe. Nie powinien więc zawieraćconnect instrukcja dla każdego sygnału w każdej klasie używam z moim systemem RPC, ale dostarczam coś takiegoRPC::connectAllSignals(QObject*);, który następnie skanuje wszystkie sygnały podczas działania i łączy je.)

Chciałbym osiągnąć następujące cele: obsługiwać wszystkie sygnały i serializować je (nazwa sygnału + argumenty). Już mogę serializować argumenty, ale nie wiem, jak uzyskać nazwę sygnału. Po googlu nie można użyć czegoś podobnegosender() dla instancji QObject. Muszę więc zrobić coś znacznie bardziej skomplikowanego.

Mój obecny system typów do przekazywania argumentów do funkcji docelowej na zdalnym końcu i tak jest ograniczony do niektórych typów. (To dlatego, że potrzebujęqt_metacall, z wyjątkiem argumentów typuvoid* z „poprawnymi typami” za nimi. Mój system RPC używa QVariants tylko z kilkoma typami wewnętrznie i konwertuję je navoid* prawidłowych typów przy użyciu niestandardowych metod. słyszałem oQVariant::constData za późno, aby go użyć, i prawdopodobnie i tak nie będzie pasować; więc pozostanę przy mojej konwersji typu, jeśli nie ma żadnej wady.)

Docelowy slot, do którego powinny być mapowane wszystkie sygnały, powinien wyglądać podobnie do tego:

void handleSignal(QByteArray signalName, QVariantList arguments);

Najlepiej byłoby, gdyby rozwiązanie było wspierane przez C ++ 03, więc chcę używać szablonów różnicowych, jeśli jest toduży wada, aby ich nie używać. W tym przypadku C ++ 11 jest w porządku, więc jestem również zadowolony z odpowiedzi przy użyciu C ++ 11.

Teraz mojemożliwe rozwiązanie na pytanie, o którym myślę:

Mogłem skanować wszystkie sygnały obiektu za pomocą jegoQMetaObject a następnie tworzenieQSignalMapper (lub coś podobnego, które przechodzi wszystkie argumenty)każdy sygnał. To jest łatwe i nie potrzebuję pomocy w tej części. Jak wspomniano wcześniej, jestem już ograniczony do niektórych typów argumentów i mogę również żyć z ograniczeniem liczby argumentów.

Brzmi jak brudny hack, ale mogłem użyć pewnego rodzaju niestandardowych maperów sygnału opartych na szablonach, takich jak ten (w tym przykładzie dla trzech argumentów):

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

Wtedy mógłbym połączyć QMetaMethod o nazwiemethod (który jest znany jako sygnał) QObject o nazwieobj w ten sposób (który może zostać wygenerowany przy użyciu jakiegoś skryptu dla wszystkich obsługiwanych typów i argumentów ... tak ...robi się brudno!):

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

Jak tomoże działać, to naprawdę brudne rozwiązanie. Potrzebowałbym dużo makr, aby pokryć wszystkie kombinacje typów, co najwyżejN argumenty (gdzieN jest około 3 do 5, jeszcze nie znany), lub prosty skrypt generujący kod dla wszystkich przypadków. Problem polega na tym, że będzie tolos przypadków, ponieważ obsługuję około 70 różnych typów na argument (10 typów pierwotnych + listy zagnieżdżone i mapy z głębokością 2 dlakażdy typ ich). Tak więc dla limitu liczby argumentówN tam sąN ^ 70 przypadków do pokrycia!

Czy istnieje zupełnie inne podejście do tego celu, którego nie rozumiem?

AKTUALIZACJA:

Sam rozwiązałem problem (patrz odpowiedź). Jeśli interesuje Cię pełny kod źródłowy, zobacz moje repozytorium na bitbucket mojego systemu RPC, który właśnie opublikowałem: bitbucket.org/leemes/qtsimplerpc

questionAnswers(3)

yourAnswerToTheQuestion