2013-10-31 107 views
9

更新

我已经创建了qt bugticket希望文档将被扩展。与自己类型的QVariant比较工作?

原始的问题

相信一个Question from 2010Qt Documentation,该operator==()不与自定义类型的工作。

引用:

布尔的QVariant ::运算符==(常量的QVariant & v)的常量

比较此的QVariant与v并返回true它们是否相等;否则返回false

QVariant使用它包含的类型()的相等运算符来检查相等性。 QVariant将尝试convert()v如果它的类型与此变体的类型不同。有关可能的转换列表,请参阅canConvert()

警告:此功能不支持使用qRegisterMetaType()注册的自定义类型。

我试图重现从Stackoverflow Question from 2010 repro案件和比较工作对我没有任何问题。

我也走得更远,并尝试使用自己的类也完美比较比较。 要重现,把下面的代码到任何标题:

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) 

以下代码到任何功能:

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! 

我猜想,在较新的QT版本类型的大小是获得性时使用Q_DECLARE_METATYPE,因此QVariant可以按字节比较未知类型的值。

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

我可以知道,QVariant如何比较未知类型?我宁愿依靠规范而不是执行。

+0

你叫'qRegisterMetaType()''上和MyEnum''MyClass'? – Zaiborg

+0

不,我没有调用'qRegisterMetaType()'。 除了我上面发布的代码外,没有使用MyClass或MyEnum的代码行。 –

+0

您是否试图将这些值转换回其原始类型比较? ('QVariant :: value ()')也是什么例如'var1'的类型? (或者要清楚,变量是什么类型,它说的是什么类型) – Zaiborg

回答

15

恐怕你需要依赖代码(并且是行为,不能无中断地改变),而不是文档。虽然下面有一个惊喜。

下面是相关的代码。

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)为这些库中定义的类型(f.i. QColor)安装自定义处理程序。

handlerManageroperator[]将执行一些额外的魔法,也就是让每个模块的处理程序是给定类型的权利:

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

现在的类型当然是一个自定义类型,所以处理器这里返回是Unknown模块之一。这Handler将使用qvariant.cppcustomCompare功能,做到这一点:

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

...只有当该类型不是指针类型时,它似乎。不知道为什么那里有代码...


好消息!

从Qt 5.2开始,您可以使用QMetaType::registerComparator(请参阅here)使Qt在您的自定义类型上调用operator<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); 
+0

伟大和非常有用的答案!谢谢! –

+0

令人惊叹!我认为,如果Qt今天诞生了,它将会更广泛地使用模板。但编译器的标准遵从性并不像今天那样广泛(即信号/时隙机制)。 –