QVariant比较与自己的类型工作? [英] QVariant comparison with own types working?

查看:296
本文介绍了QVariant比较与自己的类型工作?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

更新



我已创建 qt bugticket 希望文档将被延长。



原始问题



a href =http://stackoverflow.com/questions/2868673/how-to-support-comparisons-for-q-variant-objects-containing-a-custom-type> 2010年问题和 Qt Documentation operator ==()不适用于自定义类型。



报价:


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



比较此QVariant如果它们相等,则返回 v 并返回 true 否则返回 false



QVariant 的类型()它包含检查是否相等。 QVariant 将尝试 convert() v 与此变体的类型不同。



警告:此功能可用于查看可能的转换列表。 不支持使用 qRegisterMetaType()注册的自定义类型。


ve尝试从重新生成仿真案例问题从2010年和比较工作没有任何问题,对我来说。



我还走了一步,尝试比较使用自己的类,也工作完美。
要重现,请将以下代码放入任何标题:

 枚举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库(比如QtGui)为这些库中定义的类型安装自定义处理程序 c $> c $> code>将执行一些额外的魔法,即获得正确的每个模块处理程序给定的类型:

  QModulesPrivate :: moduleForType(typeId)]; 

现在类型当然是一个自定义类型,所以这里返回的Handler是未知模块。 处理程序将使用 qvariant.cpp 中的 customCompare ,这样做:

  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 (请参阅在这里)让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:

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

Compares this QVariant with v and returns true if they are equal; otherwise returns false.

QVariant uses the equality operator of the type() it contains to check for equality. QVariant will try to convert() v if its type is not the same as this variant's type. See canConvert() for a list of possible conversions.

Warning: This function doesn't support custom types registered with qRegisterMetaType().

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 reason for having this array of global objects is a bit complicated -- it's for allowing other Qt libraries (say, QtGui) to install custom handlers for the types defined in those libs (f.i. QColor).

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.

... only if the type is not a pointer type, it seems. Wonder why there's that code there...


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比较与自己的类型工作?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆