Как поддерживать сравнения для QVariant объектов, содержащих пользовательский тип?
Вопрос
Согласно документации QT, QVariant::operator==
Не работает, так как можно ожидать, если вариант содержит пользовательский тип:
Bool QVariant :: Operator == (const Qvarant & v) const
Сравнивает этот QVariant с V и возвращает true, если они равны; в противном случае возвращает false.
В случае пользовательских типов их равных операторов не вызываются. Вместо этого адреса значений сравниваются.
Как вы должны сделать это, чтобы это вести себя значимым для ваших пользовательских типов? В моем случае я храним перечисленную стоимость в 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
Чтобы найти индекс элемента, соответствующий конкретному перечисленному значению.
Решение
Очевидный ответ - отложить данные из с var1.value<MyEnum>() == var2.value<MyEnum>()
Чтобы сравнить их, но это требует, чтобы вы знали тип при сравнении. Кажется, в вашем случае это может быть возможно.
Если вы просто используете enums, вы также можете преобразовать его в INT для хранения в QVariant.
Редактировать: для разъяснения о поиске QComboBox
, Это использует модель поле комбо, чтобы найти данные. Отказ В частности, он использует match()
Функция QAbstractItemModel
проверить на равенство. К счастью, эта функция является виртуальной, поэтому вы можете переопределить его в подклассе.
Другие советы
Попробуйте взломать QVarant, определите функцию прототипом
typedef bool (*f_compare)(const Private *, const Private *);
и установить его к QVaryt Handler; Работать с QVarant 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;
};
Этот пример демонстрирует, как взломать QVaryt Debug Exput и преобразовать в строку. Это очень простой пример, и вам нужно продлить его для вас. «Идентификатор» - мой пользовательский тип.
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);
}
};
Вы должны создать функцию и установить его в обработчик. Не забудьте вызов HackVariant::hackIt()
в Main.cpp перед использованием (var1 == var2).
Решение для Qt 5
Qt поддерживает это из коробки с версии 5.2. Видеть QVariant :: Оператор == а также Qmetatype :: РегистрационныеМаторы.
Решение для 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
(т.е. внутри Foo.cpp
файл, но не в пределах Foo.h
файл):
Q_DEFINE_COMPARATOR(Foo)
Следующий, после построение вашего QApplication
(или QCoreApplication
) Экземпляр, включить пользовательский вариант компаратора (это только должно быть сделано один раз):
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
CustomVariantComparator::setEnabled(true);
// more code...
}
Теперь следующий фрагмент кода будет работать, как ожидалось (то есть вызывает Foo::operator==
).
QVariant::fromValue(Foo()) == QVariant::fromValue(Foo())