(「シグナルハンドラー」とは、POSIX シグナルのハンドラーではなく、スロットを意味します。)
QObjectの(まだ知られていない)サブクラスのインスタンスからのすべてのシグナルを別のQObjectの1つのスロットに「接続」する(おそらくQObject::connect
直接使用しない)必要があります。これは、ネットワーク経由でシグナル (引数付き) を送信するために必要です (シグナルをサポートする独自の RPC システムの場合)。
(「まだ知られていない」とは、私のコードが可能な限り一般的であるべきであることを意味します。したがってconnect
、RPC システムで使用している各クラスの各シグナルのステートメントを含める必要はありませんが、次のようなものを提供しますRPC::connectAllSignals(QObject*);
。ランタイム中にすべてのシグナルを送信し、それらを接続します。)
私が達成したいのは、すべてのシグナルを処理し、それらをシリアル化することです(シグナル名+引数)。すでに引数をシリアル化できますが、シグナル名を取得する方法がわかりません。sender()
グーグルで調べたところ、QObject インスタンスのようなものを使用することは不可能のようです。だから私はもっと複雑なことをする必要があります。
とにかく、リモート エンドのターゲット関数に引数を渡すための現在の型システムは、いくつかの型に制限されています。(これqt_metacall
は、引数がvoid*
背後にある「正しい型」を持つ型であることを除いて、 が必要だからです。私の RPC システムは、内部でいくつかの型のみを持つ QVariant を使用し、void*
カスタム メソッドを使用してそれらを正しい型に変換します。聞いたことがあります。使用するにはQVariant::constData
遅すぎるし、とにかく適合しない可能性があるため、欠点がなければ型変換に固執します。)
すべての信号がマップされるターゲット スロットは、次のようになります。
void handleSignal(QByteArray signalName, QVariantList arguments);
ソリューションが C++03 でサポートされているのがベストなので、使用しないことが大きな欠点である場合にのみ、可変個引数テンプレートを使用したいと考えています。今回はC++11でOKなので、C++11での回答も嬉しいです。
今私が考えている質問に対する私の可能な解決策:
そのオブジェクトを使用してオブジェクトのすべてのシグナルをスキャンし、各シグナルに対して (またはすべての引数を渡す同様のもの) をQMetaObject
作成できます。これは簡単で、この部分については助けを必要としません。前に述べたように、私はすでにいくつかの引数の型に制限されており、引数の数に制限を加えることもできます。QSignalMapper
汚いハックのように聞こえますが、次のようなカスタムのテンプレートベースのシグナルマッパーを使用できます (この例では 3 つの引数):
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;
};
次に、このように呼び出された QObject の QMetaMethod と呼ばれるmethod
(シグナルとして知られている) を接続できます (これは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 のマップ)をサポートしているため、これが多くのケースになることです。したがって、引数の数の制限に対して、^ 70 のケースをカバーする必要があります。N
N
私が見落としている、この目的のためのまったく異なるアプローチはありますか?
アップデート:
私は自分で問題を解決しました(回答を参照)。完全なソース コードに興味がある場合は、公開したばかりの RPC システムの bitbucket にあるリポジトリを参照してください: bitbucket.org/leemes/qtsimplerpc