Q Вариант сравнения с собственными типами работает?

Я создал Qt Bugticket надеясь, что документация будет продлена.

Верить Вопрос с 2010 года и Qt Documentation, operator==() не работает с пользовательскими типами.

Цитата:

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

Сравнивает этот QVariant с v и возвращается true если они равны; в противном случае возвращается false,

QVariant использует оператор равенства типа (), который он содержит, чтобы проверить равенство. QVariant постараюсь convert() v если его тип не совпадает с типом этого варианта. Увидеть canConvert() для списка возможных преобразований.

Предупреждение: Эта функция не поддерживает пользовательские типы, зарегистрированные с qRegisterMetaType(),

Я пытался воспроизвести случай репро с Stackoverflow Вопрос от 2010 и сравнение работало без проблем для меня.

Я также пошел дальше и попробовал сравнения, используя собственный класс, который также работал отлично.
Чтобы воспроизвести, поместите следующий код в любой заголовок:

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 сравнивает неизвестные типы? Я бы предпочел полагаться на спецификацию, чем на реализацию.

9

Решение

Боюсь, вам нужно полагаться на код (и, будучи поведенческим, его нельзя изменить, не нарушая), а не на документацию. Впрочем, чуть ниже есть сюрприз.

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) устанавливать пользовательские обработчики для типов, определенных в этих библиотеках (например, QColor).

operator[] на handlerManager выполнит некоторую дополнительную магию, а именно получит правильный обработчик для модуля, учитывая тип:

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

Теперь тип, конечно, пользовательский тип, поэтому возвращаемый здесь обработчик 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 на содержание.

… кажется, только если тип не является указателем Интересно, почему там этот код …


Начиная с Qt 5.2, вы можете использовать QMetaType::registerComparator (увидеть Вот) чтобы вызвать Qt operator< а также operator== на ваш пользовательский тип. Просто добавьте в свой main:

qRegisterMetaType<MyClass>();
QMetaType::registerComparators<MyClass>();

И вуаля, вы нажмете на assert в вашем операторе равенства. 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);
16

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]