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

1.2k views Asked by At

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?

2

There are 2 answers

0
pptaszni On BEST ANSWER

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.

0
scopchanov On

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.

Never let a base class know anything about its subclasses. Ever.

No matter how tempting it might look like, do no do it (even if you find out how).

A class might have hundreds of subclasses. Take for example QObject. Should QObject (or could it possible) know about your reimplementation of QMainWindow?

So, whatever the reason for this design decision is, change it!

Possible solutions

If the process algorithm of the text files is the same, regardless of the subclass, create a base class method to do the processing and call it from the subclasses.

If the algorithm depends on the subclass, make the method abstract, i.e. virtual and withouth implementation (=0), and let the implementation to the subclasses.