Come sostenere il confronto per gli oggetti QVariant che contengono un tipo personalizzato?

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

  •  01-10-2019
  •  | 
  •  

Domanda

In base alla documentazione Qt, QVariant::operator== non funziona come ci si potrebbe aspettare se la variante contiene un tipo personalizzato:

  

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

     

confronta questo QVariant con v e   restituisce true se sono uguali;   altrimenti restituisce false.

     

Nel caso di tipi personalizzati, la loro   operatori eguaglianza non sono chiamati.   Invece gli indirizzi dei valori sono   rispetto.

Come si fa a ottenere questo a comportarsi significato per i tipi personalizzati? Nel mio caso, ho la memorizzazione di un valore enumerato in un QVariant, per es.

In un colpo di testa:

enum MyEnum { Foo, Bar };

Q_DECLARE_METATYPE(MyEnum);

Da qualche parte in una funzione:

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

Di cosa ho bisogno per fare in modo diverso in modo che questa affermazione per essere vero?

Capisco perché non funziona - ogni variante memorizza una copia separata del valore enumerato, in modo da avere indirizzi diversi. Vorrei sapere come posso cambiare il mio approccio alla memorizzazione di questi valori nelle varianti in modo che o questo non è un problema, o in modo che fanno entrambi riferimento alla stessa variabile sottostante.

Non credo che sia possibile per me andare in giro che necessitano confronti di uguaglianza al lavoro. Il contesto è che sto usando questa enumerazione come l'UserData in voci di un QComboBox e voglio essere in grado di utilizzare QComboBox::findData per individuare l'indice della voce corrispondente ad un particolare valore enumerato.

È stato utile?

Soluzione

La risposta ovvia è di gettare i dati fuori con var1.value<MyEnum>() == var2.value<MyEnum>() per confrontarli, ma che richiede di conoscere il tipo quando si confrontano. Sembra che nel tuo caso questo potrebbe essere possibile.

Se sono solo utilizzando le enumerazioni, si potrebbe anche convertirlo in un int per la memorizzazione nella QVariant.

Modifica: Per chiarimenti sulla ricerca di un QComboBox , è utilizza il modello della casella combinata per trovare i dati . Specificamente, si utilizza il match() funzione della QAbstractItemModel per verificare l'uguaglianza. Per fortuna, questa funzione è virtuale in modo da poter sostituire in una sottoclasse.

Altri suggerimenti

Prova QVariant hack, definire la funzione da prototipo

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

e impostarlo gestore QVariant; Per lavorare con QVariant qt uso 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;
};

Questo esempio dimostra come incidere output di debug QVariant e convertire in stringa. Questo è molto semplice esempio, ed è necessario estenderlo per voi problema. "Identifier" è il mio tipo personalizzato.

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

};

È necessario creare la funzione e impostarla al gestore. Non dimenticare chiamata HackVariant::hackIt() in main.cpp prima dell'uso (var1 var2 ==).

Soluzione per Qt 5

Qt supporta questa out of the box a partire dalla versione 5.2. Vedere QVariant :: operator == e QMetaType :: registerComparators .

Soluzione per Qt 4

Se si sta ancora utilizzando Qt 4 e non può (o non vogliono) l'aggiornamento a Qt 5, puoi utilizzare il CustomVariantComparator classe che ho scritto per uno dei miei progetti.

Si può usare come segue. Diciamo che abbiamo una classe che implementa Foo operator== e deve essere utilizzato entro un QVariant:

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

Quindi, è sufficiente mettere il macro Q_DEFINE_COMPARATOR accanto alla realizzazione di Foo (cioè all'interno del file Foo.cpp, ma non all'interno del file Foo.h):

Q_DEFINE_COMPARATOR(Foo)

Avanti, dopo costruire l'istanza QApplication (o QCoreApplication), attivare il comparatore variante personalizzato (questo deve solo essere fatto una volta):

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

Ora, il seguente frammento di codice funzionerà come previsto (cioè invoke Foo::operator==).

QVariant::fromValue(Foo()) == QVariant::fromValue(Foo())
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top