问题描述
更新
我已创建希望文档将被延长。
原始问题
a href =http://stackoverflow.com/questions/2868673/how-to-support-comparisons-for-q-variant-objects-containing-a-custom-type> 2010年问题和, operator ==()不适用于自定义类型。
报价:
ve尝试从和比较工作没有任何问题,对我来说。
我还走了一步,尝试比较使用自己的类,也工作完美。
要重现,请将以下代码放入任何标题:
枚举MyEnum {Foo,Bar};
Q_DECLARE_METATYPE(MyEnum)
class MyClass
{
int value;
public:
MyClass():value(0)
{
}
MyClass(int a):value {
}
bool operator ==(const MyClass&)const
{
Q_ASSERT(false); //这个方法似乎不是叫做
return false;
}
bool operator!=(const MyClass&)const
{
Q_ASSERT(false); //这个方法似乎没有被调用
return true;
}
};
Q_DECLARE_METATYPE(MyClass)
并将以下代码插入任何函数: / p>
QVariant var1 = QVariant :: fromValue< MyEnum>(Foo);
QVariant var2 = QVariant :: fromValue< MyEnum>(Foo);
Q_ASSERT(var1 == var2); //成功!
var1 = QVariant :: fromValue< MyEnum>(Foo);
var2 = QVariant :: fromValue< MyEnum>(Bar);
Q_ASSERT(var1!= var2); //成功!
QVariant obj1 = QVariant :: fromValue< MyClass>(MyClass(42));
QVariant obj2 = QVariant :: fromValue< MyClass>(MyClass(42));
Q_ASSERT(obj1 == obj2); //成功!
obj1 = QVariant :: fromValue< MyClass>(MyClass(42));
obj2 = QVariant :: fromValue< MyClass>(MyClass(23));
Q_ASSERT(obj1!= obj2); //成功!
我猜想在较新的qt版本中,当 Q_DECLARE_METATYPE ,因此QVariant可以比较未知类型的值。
但这只是一个猜测,我不想通过猜测什么是qt而不是依赖文档来风险我的应用程序的稳定性。
我可以找出QVariant如何比较未知类型吗?
恐怕你需要依赖代码,是行为,它不能改变而不破坏),而不是文档。下面是一个惊喜。
这里是相关的代码。
QVariant :: operator == 对于具有未注册操作符的类型,只需使用 memcmp 。相关片段(在5.1中)为:
bool QVariant :: cmp(const QVariant& v)const
{
QVariant v1 = * this;
QVariant v2 = v;
if(d.type!= v2.d.type)
//处理转换...
return handlerManager [v1.d.type] - > compare (& v1.d,& v2.d);
}
handlerManager 全局对象,用于执行类型感知操作。它包含一个 QVariant :: Handler 对象的数组;每个这样的对象包含指针以对它们知道如何处理的类型执行某些操作:
struct Handler {
f_construct construct;
f_clear clear;
f_null isNull;
f_load load;
f_save save;
f_compare compare;
f_convert convert;
f_canConvert canConvert;
f_debugStream debugStream;
};
每个成员实际上是一个指向函数的指针。
好消息!
从Qt 5.2开始,您可以使用 QMetaType :: registerComparator (请参阅)让Qt调用运算符< 和运算符== 您的自定义类型。只需添加到主:
qRegisterMetaType< MyClass>();
QMetaType :: registerComparators< MyClass>();
并且voilà,你会在你的相等运算符中打断assert。 QVariant :: cmp 现在是:
QVariant v1 = * this;
QVariant v2 = v;
if(d.type!= v2.d.type)
//处理转换,像之前一样
// *新的重要代码*
if(v1 .d.type> = QMetaType :: User){
//非内置类型(MyClass,MyEnum ...)
int result;
//将调用v1的类型的比较器,如果已注册
if(QMetaType :: compare(QT_PREPEND_NAMESPACE(constData(v1.d)),QT_PREPEND_NAMESPACE(constData(v2.d)),v1。 d.type,& result))
return result == 0;
}
// as before
return handlerManager [v1.d.type] - > compare(& v1.d,& v2.d);
Update
I have created an qt bugticket hoping the documentation will be extended.
Original Question
Believing an Question from 2010 and the Qt Documentation, the operator==() doesn't work with custom types.
Quote:
I've tried to reproduce the repro case from the Stackoverflow Question from 2010 and the comparison worked without any problems for me.
I also went a step further and tried comparisons using an own class which also worked perfectly.To reproduce, put the following code into any header:
enum MyEnum { Foo, Bar }; Q_DECLARE_METATYPE(MyEnum) class MyClass { int value; public: MyClass() : value(0) { } MyClass(int a) : value(a) { } bool operator==(const MyClass &) const { Q_ASSERT(false); // This method seems not to be called return false; } bool operator!=(const MyClass &) const { Q_ASSERT(false); // This method seems not to be called return true; } }; Q_DECLARE_METATYPE(MyClass)
And the following code into any function:
QVariant var1 = QVariant::fromValue<MyEnum>(Foo); QVariant var2 = QVariant::fromValue<MyEnum>(Foo); Q_ASSERT(var1 == var2); // Succeeds! var1 = QVariant::fromValue<MyEnum>(Foo); var2 = QVariant::fromValue<MyEnum>(Bar); Q_ASSERT(var1 != var2); // Succeeds! QVariant obj1 = QVariant::fromValue<MyClass>(MyClass(42)); QVariant obj2 = QVariant::fromValue<MyClass>(MyClass(42)); Q_ASSERT(obj1 == obj2); // Succeeds! obj1 = QVariant::fromValue<MyClass>(MyClass(42)); obj2 = QVariant::fromValue<MyClass>(MyClass(23)); Q_ASSERT(obj1 != obj2); // Succeeds!
I would guess that in newer qt versions the size of a type is aquired when the Q_DECLARE_METATYPE is used so the QVariant can compare values of unknown types bytewise.
But that's only a guess and I don't want to risk the stability of my application by guessing what qt does instead of relying on the documentation.
Can I find out, how the QVariant compares unknown types? I would prefer relying on specification than on implementation.
I'm afraid you'll need to rely on the code (and, being behaviour, it can't be changed without breaking), and not documentation. There's a surprise just below, though.
Here's the relevant code.
QVariant::operator== for types with unregistered operators will just use memcmp. The relevant snippet (in 5.1) is this:
bool QVariant::cmp(const QVariant &v) const { QVariant v1 = *this; QVariant v2 = v; if (d.type != v2.d.type) // handle conversions.... return handlerManager[v1.d.type]->compare(&v1.d, &v2.d); }
handlerManager is a global object that gets used to perform type-aware manipulations. It contains an array of QVariant::Handler objects; each of such objects contains pointers to perform certain operations on the types they know how to handle:
struct Handler { f_construct construct; f_clear clear; f_null isNull; f_load load; f_save save; f_compare compare; f_convert convert; f_canConvert canConvert; f_debugStream debugStream; };
Each and every of those members is actually a pointer to a function.
The operator[] on the handlerManager will perform some extra magic, namely get the right per-module handler given the type:
return Handlers[QModulesPrivate::moduleForType(typeId)];
Now the type is of course a custom type, so the Handler returned here is the one the Unknown module. That Handler will use the customCompare function in qvariant.cpp, which does this:
static bool customCompare(const QVariant::Private *a, const QVariant::Private *b) { const char *const typeName = QMetaType::typeName(a->type); if (Q_UNLIKELY(!typeName) && Q_LIKELY(!QMetaType::isRegistered(a->type))) qFatal("QVariant::compare: type %d unknown to QVariant.", a->type); const void *a_ptr = a->is_shared ? a->data.shared->ptr : &(a->data.ptr); const void *b_ptr = b->is_shared ? b->data.shared->ptr : &(b->data.ptr); uint typeNameLen = qstrlen(typeName); if (typeNameLen > 0 && typeName[typeNameLen - 1] == '*') return *static_cast<void *const *>(a_ptr) == *static_cast<void *const *>(b_ptr); if (a->is_null && b->is_null) return true; return !memcmp(a_ptr, b_ptr, QMetaType::sizeOf(a->type)); }
Which, apart from a bit of error checking and handling shared and null variants in a special way, uses memcmp on the contents.
Good news!
Starting with Qt 5.2, you can use QMetaType::registerComparator (see here) to make Qt invoke operator< and operator== on your custom type. Just add to your main:
qRegisterMetaType<MyClass>(); QMetaType::registerComparators<MyClass>();
And voilà, you'll hit the assert in your equality operator. QVariant::cmp now is:
QVariant v1 = *this; QVariant v2 = v; if (d.type != v2.d.type) // handle conversions, like before // *NEW IMPORTANT CODE* if (v1.d.type >= QMetaType::User) { // non-builtin types (MyClass, MyEnum...) int result; // will invoke the comparator for v1's type, if ever registered if (QMetaType::compare(QT_PREPEND_NAMESPACE(constData(v1.d)), QT_PREPEND_NAMESPACE(constData(v2.d)), v1.d.type, &result)) return result == 0; } // as before return handlerManager[v1.d.type]->compare(&v1.d, &v2.d);
这篇关于QVariant比较与自己的类型工作?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!