Общий обработчик сигналов QObject

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

Мне нужно «подключиться» ( вероятно, нетиспользуя QObject::connectнапрямую) все сигналыот экземпляра (еще не известного) подкласса QObject в один единственный слотдругого QObject. Мне это нужно для отправки сигнала (с аргументами) по сети (для собственной системы RPC с поддержкой сигналов).

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

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

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

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

void handleSignal(QByteArray signalName, QVariantList arguments);

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


Теперь мое возможное решениевопроса, над которым я думаю:

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

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

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 с именем (который, как известно, является сигналом) объекта QObject с именем objвроде этого (который может быть сгенерирован с использованием какого-то сценария для всех поддерживаемых типов и количества аргументов... да... он становится грязным! ):

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

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

Существует ли совершенно другой подход к этой цели, который я упускаю из виду?


ОБНОВЛЕНИЕ:

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

7
задан leemes 30 May 2012 в 18:20
поделиться