(Под «обработчиком сигналов» я имею в виду слоты, а не обработчики сигналов 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