c++qtqt5qmetaobjectqmetatype

Get object instance class name with Qt's meta-object system


I have 3 classes:

class Being : public QObject {
    Q_OBJECT
public:
    explicit Being(QObject *parent = nullptr);
};

class Animal : public Being {
    Q_OBJECT
public:
    explicit Animal(QObject *parent = nullptr);
};

class Dog : public Animal {
    Q_OBJECT
public:
    explicit Dog(QObject *parent = nullptr);
};

Being's implementation is the following:

Being::Being(QObject *parent) : QObject(parent) {
    qDebug() << staticMetaObject.className();
}

I have the following code:

Being *pBeing = new Being();
Being *pAnimal = new Animal();
Being *pDog = new Dog();

Question is: is there a way to implement Being's constructor so that I can have access to the actual instance that points to my Being base class so that the log of the above code will be:

Being
Animal
Dog

?

EDIT: My intention is that I want to be able to have a Animal.txt, Being.txt and Doc.txt that will be processed inside the Being base class. And I need to know, based on the instance's type, whether I need to parse Animal.txt, Being.txt or Doc.txt to get more information.

Does Qt support this? Is there a mechanism I can use to achieve this using C++/Qt? If not, could there be any elegant compromise solution for this?


Solution

  • It is not possible in the constructor of Being because the enclosing object is not constructed yet, therefore metadata about it is not available. However, you can write the initialize method that should be called after the object construction, e.g. with print function:

    class Being : public QObject {
        Q_OBJECT
    public:
        explicit Being(QObject *parent = nullptr) { qDebug() << this->metaObject()->className(); }  // this will always print "Being"
        void initialize() { qDebug() << this->metaObject()->className(); }  // this will print the actual class name
    };
    
    class Animal : public Being {
        Q_OBJECT
    public:
        explicit Animal(QObject *parent = nullptr) { initialize(); }  // you can already use this method in the constructor
    };
    
    TEST(xxx, yyy)
    {
        Being* being = new Being();
        Being* animal = new Animal();
        being->initialize();
        animal->initialize();  // or you can call it later
    }
    

    In case initialize method is not good solution, you can always hack it through Being constructor: explicit Being(QString name, QObject* parent = nullptr; and then explicit Animal(QObject *parent = nullptr): Being("Animal") {}, but I think it is less elegant.