Projet

Général

Profil

Enregistrer un pointer dans un QVariant

Introduction

Il est parfois intéressant d'enregistrer un pointeur dans un QVariant car tous les QObject peuvent stocker des propriétés au format QVariant.
En effet, il est plus simple de stocker un pointeur sur un objet A dans un objet B si celui-ci en a besoin plus tard plutôt que de gérer un container de pointeur avec une référence sur A et sur B (une QMap par exemple).
Un container de plus à gérer, c'est un peu plus fastidieux (il faut "boucler" pour retrouver le bon).

Le problème, c'est que Qt ne nous offre pas la possibilité d'enregistrer des pointeurs dans un QVariant.
Si vous sauvegardez un pointeur quelconque dans un QVariant, il vous sera retourné sous la forme d'un booléen.
Il faut donc ruser.

Démonstration par l'exemple

Pour qu'un QObject stocke un QVariant, il faut passer par la méthode bool QObject::setProperty ( const char * name, const QVariant & value ).

Exemple :

#include <QCoreApplication>
#include <QObject>
#include <QDebug>
#include <QVariant>

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    QObject *myObject = new QObject();
    myObject->setProperty("Solid", true);
    qDebug() << myObject->property("Solid").toBool();

    return a.exec();
}

// N'oubliez pas d'inclure le moc, nous déclarons tout dans un seul fichier (si vous avez des erreurs vtable)
#include "main.moc" 

// Sortie Console
// true

Il faut bien sûr connaitre le type de votre propriété pour avoir le bon résultat en retour. Ici, j'ai utilisé la méthode toBool() sur mon QVariant pour que la transformation se fasse correctement.

On va donc créer un mécanisme interne à une classe qui va nous permettre de transformer un pointeur en QVariant et vice-versa.

Reprenons notre exemple du dessus avec la déclaration d'une simple classe Example afin de pouvoir stocker un pointeur de cette classe dans un autre QObject.

#include <QCoreApplication>
#include <QObject>
#include <QDebug>
#include <QVariant>

class Example : public QObject
{
    Q_OBJECT
public:
    explicit Example(){;}
};

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    Example *example = new Example();
    qDebug() << example;

    QObject *myObject = new QObject();
    // On essaye de ranger notre pointeur dans une "property" 
    myObject->setProperty("monPointeurExample", example);

    return a.exec();
}

#include "main.moc" 

Si vous testez ce code, votre compilateur va vous dire :
'QVariant::QVariant(void)' is private*
Et oui, je vous ai dit que Qt ne nous permet pas de stocker des pointeurs... Vous en avez la preuve.

Pour contourner le problème, il faut "sérialiser" l'enregistrement du pointeur dans un QVariant mais aussi la récupération d'un QVariant en pointeur.
Nous allons donc définir une autre classe au-dessus de notre classe Example qui se chargera du travail de "sérialisation".

#include <QCoreApplication>
#include <QObject>
#include <QDebug>
#include <QVariant>

// Voici nous classe qui transforme un pointer en QVariant, et vice-versa
template <class T> class ExamplePointer
{
public:

    /* Retourne le QVariant donné en paramètre sous la forme d'un pointeur */
    static T* asPtr(QVariant v)
    {
        return  (T *) v.value<void *>();
    }

    /* Retourne le pointeur donné en paramètre sous la forme d'un QVariant */
    static QVariant asQVariant(T* ptr)
    {
        return qVariantFromValue((void *) ptr);
    }
};

class Example : public QObject
{
    Q_OBJECT
public:
    explicit Example(){;}
};

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    Example *example = new Example();

    QObject *myObject = new QObject();

    // On enregistre notre pointeur en passant par la classe ExamplePointer
    myObject->setProperty("object", ExamplePointer<Example>::asQVariant(example));

    // Pour récupérer notre pointeur
    Example *p1 = ExamplePointer<Example>::asPtr(myObject->property("object"));

    qDebug() << example;
    qDebug() << p1 ;

    return a.exec();
}

#include "main.moc" 

Vous devriez voir à l'exécution que les pointeurs example et p1 sont égaux.
Donc, ils indiquent la même adresse =) !
Nous avons réussi à enregistrer un pointeur sous la forme d'un QVariant, et ainsi pu utiliser le mécanisme les "properties" des QObject pour stocker notre pointeur.

Et voilà.

(Source : http://blog.bigpixel.ro/2010/04/storing-pointer-in-qvariant/)

Redmine Appliance - Powered by TurnKey Linux