Как поддерживать сравнения для QVariant объектов, содержащих пользовательский тип?
согласно документации Qt, QVariant::operator==
не работает, как можно было бы ожидать, если вариант содержит пользовательского типа:
bool QVariant:: operator== (const QVariant & v ) const
сравнивает этот QVariant с v и возвращает true, если они равны; в противном случае возвращает false.
в случае пользовательских типов, их операторы equalness не называются. Вместо этого адреса значений сравнимый.
как вы должны заставить это вести себя значимо для ваших пользовательских типов? В моем случае я храню перечисленное значение в QVariant, например
в заголовке:
enum MyEnum { Foo, Bar };
Q_DECLARE_METATYPE(MyEnum);
где-то в функции:
QVariant var1 = QVariant::fromValue<MyEnum>(Foo);
QVariant var2 = QVariant::fromValue<MyEnum>(Foo);
assert(var1 == var2); // Fails!
что мне нужно сделать по-другому, чтобы это утверждение было истинным?
Я понимаю почему он не работает -- каждый вариант хранит отдельную копию перечисленного значения, у них разные адреса. Я хочу знать, как я могу изменить свой подход к хранению этих значений в вариантах, чтобы это не было проблемой, или чтобы они оба ссылались на одну и ту же базовую переменную.
не думаю, что для меня возможно обойти необходимость сравнения равенства для работы. Контекст заключается в том, что я использую это перечисление как UserData в элементах в QComboBox
и я хочу иметь возможность использовать QComboBox::findData
чтобы найти индекс элемента, соответствующий a конкретное значение перечисления.
3 ответов
очевидный ответ-выбросить данные из var1.value<MyEnum>() == var2.value<MyEnum>()
чтобы сравнить их, но это требует, чтобы вы знали тип при сравнении. Кажется, в вашем случае это возможно.
если вы просто используете перечисления, вы также можете преобразовать его в int для хранения в QVariant.
Edit: для уточнения о поиске QComboBox
, это использует модель поля со списком для поиска данных. В частности, он использует match()
попробуйте взломать qvariant, определите функцию по прототипу
typedef bool (*f_compare)(const Private *, const Private *);
и установите его в обработчик qvariant; Для работы с qvariant Qt используйте обработчик:
struct Handler {
f_construct construct;
f_clear clear;
f_null isNull;
#ifndef QT_NO_DATASTREAM
f_load load;
f_save save;
#endif
f_compare compare;
f_convert convert;
f_canConvert canConvert;
f_debugStream debugStream;
};
в этом примере показано, как взломать QVariant debug output и преобразовать в string. Это очень простой пример, и вам нужно расширить его для вас проблема. "Идентификатор" - это мой пользовательский тип.
class HackVariant : private QVariant
{
public:
static void hackIt() {
origh = handler;
Handler* h = new Handler;
*h = *origh;
h->convert = convert;
h->debugStream = hackStreamDebug;
handler = h;
}
private:
static bool convert(const QVariant::Private *d, QVariant::Type t, void *result, bool *ok)
{
//qDebug() << Q_FUNC_INFO << "type:" << d->type;
if (d->type >= QVariant::UserType)
{
QString& str = *((QString*)result);
Identifier* ident = (Identifier*)(constData(d));
str = ident->toString();
}
else
return origh->convert(d, t, result, ok);
return true;
}
static void hackStreamDebug(QDebug dbg, const QVariant &v) {
if (v.canConvert<Identifier>())
dbg << v.value<Identifier>();
else
origh->debugStream(dbg, v);
}
static const Handler* origh;
static const void *constData(const QVariant::Private *d)
{
return d->is_shared ? d->data.shared->ptr : reinterpret_cast<const void *>(&d->data.ptr);
}
};
вы должны создать функцию и установить ее в handler. Не забудьте позвонить HackVariant::hackIt()
в Main.ЧГК перед использовать (переменная var1 == var2 будет).
решение для Qt 5
Qt поддерживает это из коробки с версии 5.2. См.QVariant:: operator== и QMetaType:: registerComparators.
решение для Qt 4
если вы все еще используете Qt 4 и не можете (или не хотите) перейти на Qt 5, Вы можете использовать CustomVariantComparator класс, который я написал для одного из моих проекты.
вы можете использовать его следующим образом. Допустим у нас есть класс Foo
, который реализует operator==
и должен использоваться в пределах QVariant
:
class Foo {
public:
bool operator==(const Foo &other) { return ...; }
};
Q_DECLARE_METATYPE(Foo)
тогда просто поставьте Q_DEFINE_COMPARATOR
макрос рядом с осуществлением Foo
(т. е. в пределах , но не ):
Q_DEFINE_COMPARATOR(Foo)
далее после строительство вашего QApplication
(или QCoreApplication
) экземпляр, включите пользовательский компаратор вариантов (это должно быть только сделано один раз):
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
CustomVariantComparator::setEnabled(true);
// more code...
}
теперь следующий фрагмент кода будет работать так, как ожидалось (т. е. invoke Foo::operator==
).
QVariant::fromValue(Foo()) == QVariant::fromValue(Foo())