Comment soutenir des comparaisons pour les objets QVariant contenant un type personnalisé?

StackOverflow https://stackoverflow.com/questions/2868673

  •  01-10-2019
  •  | 
  •  

Question

Selon la documentation Qt, QVariant::operator== ne fonctionne pas comme on pourrait s'y attendre si la variante contient un type personnalisé:

  

bool QVariant :: opérateur == (const QVariant & v) const

     

Compares ce QVariant avec v et   renvoie true si elles sont égales;   sinon retourne false.

     

Dans le cas des types personnalisés, leur   les opérateurs equalness ne sont pas appelés.   Au lieu de cela les adresses des valeurs sont   par rapport.

Comment êtes-vous censé obtenir ce à se comporter de façon significative pour vos types personnalisés? Dans mon cas, je suis stocker une valeur énumérée dans un QVariant, par exemple.

Dans un en-tête:

enum MyEnum { Foo, Bar };

Q_DECLARE_METATYPE(MyEnum);

Quelque part dans une fonction:

QVariant var1 = QVariant::fromValue<MyEnum>(Foo);
QVariant var2 = QVariant::fromValue<MyEnum>(Foo);
assert(var1 == var2); // Fails!

Que dois-je faire différemment pour que cette affirmation pour être vrai?

Je comprends pourquoi il ne fonctionne pas - chaque variante stocke une copie distincte de la valeur énumérés, donc ils ont des adresses différentes. Je veux savoir comment je peux changer mon approche pour stocker ces valeurs dans des variantes afin que soit ce n'est pas un problème, ou si ce qu'ils font à la fois référence à la même variable sous-jacente.

Il ne pense pas qu'il soit possible pour moi de se déplacer besoin des comparaisons d'égalité au travail. Le contexte est que j'utilise cette énumération comme UserData articles dans un QComboBox et je veux être en mesure d'utiliser QComboBox::findData pour localiser l'index de l'élément correspondant à une valeur particulière recensée.

Était-ce utile?

La solution

La réponse évidente est de jeter les données sur avec var1.value<MyEnum>() == var2.value<MyEnum>() de les comparer, mais qui vous oblige à connaître le type lorsque l'on compare. Il semble que dans votre cas cela pourrait être possible.

Si vous utilisez juste énumérations, vous pouvez également le convertir en un entier pour le stockage dans le QVariant.

Edit: Pour obtenir des éclaircissements sur la recherche d'un QComboBox , il utilise le modèle de la zone de liste déroulante pour trouver les données . Plus précisément, il utilise le match() fonction du QAbstractItemModel pour vérifier l'égalité. Heureusement, cette fonction est virtuelle de sorte que vous pouvez le remplacer dans une sous-classe.

Autres conseils

Essayez QVariant hack, définir la fonction prototype par

typedef bool (*f_compare)(const Private *, const Private *);

et le mettre à QVariant gestionnaire; Pour travailler avec QVariant qt utilisation 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;
};

Cet exemple montre comment pirater la sortie de débogage QVariant et convertir en chaîne. Ceci est par exemple très simple, et vous avez besoin de l'étendre pour vous problème. "Identifier" est mon type personnalisé.

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);
     }

};

Vous devez créer la fonction et le mettre au gestionnaire. Ne pas oublier HackVariant::hackIt() d'appel dans main.cpp avant utilisation (var1 == var2).

Solution pour Qt 5

Qt prend en charge cette sortie de la boîte depuis la version 5.2. Voir QVariant :: opérateur == et QMetaType :: registerComparators .

Solution pour Qt 4

Si vous utilisez encore Qt 4 et ne peut pas (ou ne veulent pas) pour mettre à niveau encore Qt 5, vous pouvez utiliser le CustomVariantComparator classe que j'ai écrit pour un de mes projets.

Vous pouvez l'utiliser comme suit. Disons que nous avons un Foo de classe qui met en œuvre operator== et doivent être utilisés dans un QVariant:

class Foo {
public:
    bool operator==(const Foo &other) { return ...; }
};
Q_DECLARE_METATYPE(Foo)

Ensuite, mettez simplement la macro Q_DEFINE_COMPARATOR à côté de la mise en œuvre de Foo (à savoir dans le fichier Foo.cpp, mais pas dans le fichier Foo.h):

Q_DEFINE_COMPARATOR(Foo)

Ensuite, après la construction de votre QApplication (ou QCoreApplication) par exemple, activer le comparateur variant personnalisé (ce ne doit être fait une fois):

int main(int argc, char *argv[]) {
    QApplication app(argc, argv);
    CustomVariantComparator::setEnabled(true);
    // more code...
}

Maintenant, l'extrait de code suivant fonctionnera comme prévu (à savoir Invoke Foo::operator==).

QVariant::fromValue(Foo()) == QVariant::fromValue(Foo())
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top