c++ - expositivos - ¿Cómo se admiten las comparaciones para objetos QVariant que contienen un tipo personalizado?
comparar variables en c++ (3)
Según la documentación de Qt, QVariant::operator==
no funciona como cabría esperar si la variante contiene un tipo personalizado:
bool QVariant :: operator == (const QVariant & v) const
Compara este QVariant con v y devuelve verdadero si son iguales; de lo contrario, devuelve falso.
En el caso de los tipos personalizados, sus operadores de igualdad no son llamados. En cambio, las direcciones de los valores se comparan.
¿Cómo se supone que debes lograr que esto se comporte de manera significativa para tus tipos personalizados? En mi caso, estoy almacenando un valor enumerado en un QVariant, por ejemplo
En un encabezado:
enum MyEnum { Foo, Bar };
Q_DECLARE_METATYPE(MyEnum);
En algún lugar de una función:
QVariant var1 = QVariant::fromValue<MyEnum>(Foo);
QVariant var2 = QVariant::fromValue<MyEnum>(Foo);
assert(var1 == var2); // Fails!
¿Qué debo hacer de manera diferente para que esta afirmación sea verdadera?
Entiendo por qué no funciona: cada variante almacena una copia separada del valor enumerado, por lo que tienen diferentes direcciones. Quiero saber cómo puedo cambiar mi enfoque para almacenar estos valores en variantes para que esto no sea un problema, o para que ambos hagan referencia a la misma variable subyacente.
No creo que sea posible para mí tener que hacer comparaciones de igualdad para trabajar. El contexto es que estoy usando esta enumeración como el UserData en elementos en un QComboBox
y quiero poder usar QComboBox::findData
para ubicar el índice del artículo correspondiente a un valor enumerado particular.
Solución para Qt 5
Qt lo admite de fábrica desde la versión 5.2. Ver QVariant::operator== y QMetaType::registerComparators .
Solución para Qt 4
Si todavía está usando Qt 4 y no puede (o no quiere) actualizar a Qt 5 aún, puede usar la clase CustomVariantComparator que he escrito para uno de mis proyectos.
Puedes usarlo de la siguiente manera. Digamos que tenemos una clase Foo
que implementa operator==
y debe usarse dentro de QVariant
:
class Foo {
public:
bool operator==(const Foo &other) { return ...; }
};
Q_DECLARE_METATYPE(Foo)
Luego, simplemente coloque la macro Q_DEFINE_COMPARATOR
junto a la implementación de Foo
(es decir, dentro del archivo Foo.cpp
, pero no dentro del archivo Foo.h
):
Q_DEFINE_COMPARATOR(Foo)
A continuación, después de construir su QApplication
(o QCoreApplication
), habilite el comparador de variantes personalizado (esto solo debe hacerse una vez):
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
CustomVariantComparator::setEnabled(true);
// more code...
}
Ahora, el siguiente fragmento de código funcionará como se espera (es decir, invocar Foo::operator==
).
QVariant::fromValue(Foo()) == QVariant::fromValue(Foo())
La respuesta obvia es descartar los datos con var1.value<MyEnum>() == var2.value<MyEnum>()
para compararlos, pero eso requiere que sepas el tipo al comparar. Parece que en tu caso esto podría ser posible.
Si solo está utilizando enumeraciones, también puede convertirlo a int para su almacenamiento en QVariant.
Editar: para aclarar sobre la búsqueda de un QComboBox
, utiliza el modelo del cuadro combinado para buscar los datos . Específicamente, usa la función match()
del QAbstractItemModel
para verificar la igualdad. Afortunadamente, esta función es virtual, por lo que puede anularla en una subclase.
Prueba hack qvariant, define la función por prototipo
typedef bool (*f_compare)(const Private *, const Private *);
y configurarlo en el controlador qvariant; Para trabajar con qvariant qt use Handler:
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;
};
Este ejemplo muestra cómo hackear la salida de depuración qvariant y convertirla en cadena. Este es un ejemplo muy simple, y necesita ampliarlo para su problema. "Identificador" es mi tipo personalizado.
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);
}
};
Tienes que crear una función y configurarla en el controlador. No olvide llamar a HackVariant::hackIt()
en main.cpp antes de usar (var1 == var2).