カスタム タイプを含む QVariant オブジェクトの比較をサポートするにはどうすればよいですか?
質問
Qt のドキュメントによると、 QVariant::operator==
バリアントにカスタム タイプが含まれている場合、期待どおりに機能しません。
bool QVariant::operator== ( const QVariant & v ) const
このQVariantをVと比較し、それらが等しい場合はtrueを返します。それ以外の場合は false を返します。
カスタムタイプの場合、それらの均等性演算子は呼び出されません。代わりに、値のアドレスが比較されます。
これをカスタム型に対して有意義に動作させるにはどうすればよいでしょうか?私の場合、列挙値を QVariant に保存しています。
ヘッダー内:
enum MyEnum { Foo, Bar };
Q_DECLARE_METATYPE(MyEnum);
関数のどこか:
QVariant var1 = QVariant::fromValue<MyEnum>(Foo);
QVariant var2 = QVariant::fromValue<MyEnum>(Foo);
assert(var1 == var2); // Fails!
この主張が真実であるためには、何をどうする必要があるでしょうか?
わかりました なぜ それは機能しません。各バリアントは列挙値の個別のコピーを保存しているため、異なるアドレスを持ちます。これらの値をバリアントに保存するアプローチを変更して、これが問題にならないようにするか、両方が同じ基になる変数を参照できるようにする方法を知りたいです。
等価比較を機能させる必要を回避することは私には不可能だと思います。コンテキストとしては、この列挙をアイテム内の UserData として使用しているということです。 QComboBox
そして使えるようになりたい QComboBox::findData
特定の列挙値に対応する項目インデックスを見つけます。
解決
明らかな答えは、からデータをキャストすることです var1.value<MyEnum>() == var2.value<MyEnum>()
それらを比較するには、比較するときにタイプを知る必要があります。あなたの場合、これが可能かもしれないようです。
列挙のみを使用している場合は、QVariantのストレージ用にINTに変換することもできます。
編集:検索に関する明確化のため QComboBox
, 、 それ コンボボックスのモデルを使用してデータを見つけます. 。具体的には、を使用します match()
の関数 QAbstractItemModel
平等を確認する。幸いなことに、この関数は仮想であるため、サブクラスでオーバーライドできます。
他のヒント
qvariant をハックしてみて、プロトタイプで関数を定義してください
typedef bool (*f_compare)(const Private *, const Private *);
そしてそれを qvariant ハンドラーに設定します。qvariant qt を操作するには、ハンドラーを使用します。
struct Handler {
f_construct construct;
f_clear clear;
f_null isNull;
#ifndef QT_NO_DATASTREAM
f_load load;
f_save save;
#endif
f_compare compare;
f_convert convert;
f_canConvert canConvert;
f_debugStream debugStream;
};
この例では、qvariant デバッグ出力をハッキングして文字列に変換する方法を示します。これは非常に単純な例なので、問題に合わせて拡張する必要があります。「識別子」は私のカスタムタイプです。
class HackVariant : private QVariant
{
public:
static void hackIt() {
origh = handler;
Handler* h = new Handler;
*h = *origh;
h->convert = convert;
h->debugStream = hackStreamDebug;
handler = h;
}
private:
static bool convert(const QVariant::Private *d, QVariant::Type t, void *result, bool *ok)
{
//qDebug() << Q_FUNC_INFO << "type:" << d->type;
if (d->type >= QVariant::UserType)
{
QString& str = *((QString*)result);
Identifier* ident = (Identifier*)(constData(d));
str = ident->toString();
}
else
return origh->convert(d, t, result, ok);
return true;
}
static void hackStreamDebug(QDebug dbg, const QVariant &v) {
if (v.canConvert<Identifier>())
dbg << v.value<Identifier>();
else
origh->debugStream(dbg, v);
}
static const Handler* origh;
static const void *constData(const QVariant::Private *d)
{
return d->is_shared ? d->data.shared->ptr : reinterpret_cast<const void *>(&d->data.ptr);
}
};
関数を作成してハンドラーに設定する必要があります。電話を忘れないでください HackVariant::hackIt()
使用前に main.cpp に追加します (var1 == var2)。
QT 5のソリューション
QTは、バージョン5.2以降、これを箱から出してサポートしています。見る qvariant :: operator == と QMetatype :: RegisterComparators.
QT 4のソリューション
まだQT 4を使用していて、QT 5にアップグレードすることができない(または望んでいない)場合は、 CustomVariantComparator プロジェクトの1つに書いたクラス。
次のように使用できます。クラスがあるとしましょう Foo
実装します operator==
内で使用する必要があります QVariant
:
class Foo {
public:
bool operator==(const Foo &other) { return ...; }
};
Q_DECLARE_METATYPE(Foo)
次に、単に置きます Q_DEFINE_COMPARATOR
の実装の横にあるマクロ Foo
(すなわち内の Foo.cpp
ファイルですが、内部ではありません Foo.h
ファイル):
Q_DEFINE_COMPARATOR(Foo)
次、 後 あなたの構築 QApplication
(また QCoreApplication
)インスタンス、カスタムバリアントコンパレータを有効にします(これは1回だけ実行する必要があります):
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
CustomVariantComparator::setEnabled(true);
// more code...
}
これで、次のコードスニペットが期待どおりに機能します(つまり、呼び出します Foo::operator==
).
QVariant::fromValue(Foo()) == QVariant::fromValue(Foo())