QVariant与自有类型的比较工作?

时间:2013-10-31 10:21:42

标签: c++ qt qt5 qvariant

更新

我创建了一个qt bugticket,希望文档能够扩展。

原始问题

相信Question from 2010Qt Documentationoperator==()不适用于自定义类型。

引用:

  

bool QVariant :: operator ==(const QVariant& v)const

     

将此QVariant与v进行比较,如果它们相等则返回true;否则返回false

     

QVariant使用它包含的类型()的相等运算符来检查是否相等。如果QVariant的类型与此变体的类型不同,convert()会尝试v canConvert()。请参阅qRegisterMetaType()以获取可能的转化列表。

     

警告:此功能不支持使用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) 注册的自定义类型。

我试图从Stackoverflow Question from 2010重现repro案例,并且比较对我没有任何问题。

我还更进一步,尝试使用自己的类进行比较,这也很完美。 要重现,请将以下代码放入任何标题中:

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!

以下代码进入任何函数:

Q_DECLARE_METATYPE

我猜想在较新的qt版本中,当使用{{1}}时,会获取类型的大小,因此QVariant可以按字节顺序比较未知类型的值。

但这只是一个猜测而且我不想通过猜测qt做什么而不是依赖文档来冒险应用程序的稳定性。

我可以找出QVariant如何比较未知类型吗?我更愿意依赖规范而不是实现。

1 个答案:

答案 0 :(得分:16)

我担心你需要依赖代码(而且,作为行为,它不能在没有破坏的情况下改变),而不是文档。不过,下面有一个惊喜。

以下是相关代码。

对于具有未注册运算符的类型,

QVariant::operator==将仅使用memcmp。相关片段(在5.1中)是这样的:

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是一个全局对象,用于执行类型感知操作。它包含一组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库(比如QtGui)为这些库中定义的类型安装自定义处理程序(fi QColor)。

operator[]上的handlerManager将执行一些额外的魔法,即在给定类型的情况下获取正确的每模块处理程序:

return Handlers[QModulesPrivate::moduleForType(typeId)];

现在类型当然是自定义类型,因此这里返回的Handler是Unknown模块。 Handler customCompare将使用qvariant.cpp中的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)); } 函数执行此操作:

memcmp

除了以特殊方式检查和处理共享和null变体的错误之外,在内容上使用QMetaType::registerComparator

...只有当类型不是指针类型时才会出现。不知道为什么那里有代码...


好消息!

从Qt 5.2开始,您可以使用operator<(请参阅here)让Qt在您的自定义类型上调用operator==main。只需添加到qRegisterMetaType<MyClass>(); QMetaType::registerComparators<MyClass>();

即可
QVariant::cmp

瞧,你会在你的平等算子中击中断言。 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); 现在是:

{{1}}