Meta-type id of an object
-
Hello,
For a project I'm working on I have to marshal some objects through a network stream. The most obvious approach seemed to serialize/deserialize them based on their meta-type id's at the entry/exit point of my application. So to do that I'd wish to extract the meta-type id for each of them. Note that my objects are not descendants ofQObject
, but I've made them gadgets withQ_GADGET
. WhileQObject
providesQObject::metaObject
there's no such straight-forward way to retrieve the meta-object for gadgets. I suppose I could get it fromstaticMetaObject
, and even if I did that is there a direct way to retrieve the meta-type id?The only way I could find was to retrieve the class name from the meta-object and then use
QMetaType::type
with it to find out what the id is corresponding to the class. Is there a better/more convenient method for doing this? Am I overcomplicating my problem?As a side question, can I invoke signals based on the type id I'm getting (if they are supposedly already declared in the C++ code)?
Edit: I'll post this as a separate thread when I face its implementation.Kind regards.
-
@SGaist
Hello Samuel,
While I appreciate you taking the time to answer, I don't think it's helping my case. Yes,QMetaType
is what provides the needed API, however I was expecting more of an advice than a reference to the docs. :)
Still, I may not have explained my dilemma well, so I'm going to expand a bit (directly using the classes' names from my lib):Suppose in a library I have a class that's called
QMpiMessage
, which is to be the base for the MPI messages that I'm going to marshal through the MPI infrastructure (a low-latency high-throughput network). The user (programmer) is to derive from that base and define his/hers own messages that could be used to communicate between the nodes. Now, I've made the base to be a gadget to put the meta-type information in that class (Noting here that only declaring it as meta-type withQ_DECLARE_METATYPE
should work as well, and most probably is the better way, as these objects of typeQMpiMessage
(and descendants) should hold the message data, so I probably will want switch to it at some point).So the use case for the send operation is as follows:
- The user creates a message object (derived from
QMpiMessage
). - He/she passes it to the
QMpiNode
(QObject
subclass that manages communication with a computational node) for sending. (I havevoid QMpiNode::send(const QMpiMessage *, bool synchronous = false)
for that purpose) - The message object is serialized in a
QByteArray
- Depending on whether the message is
synchronous
its generated event is either processed immediately and synchronously, or it's is scheduled through Qt's event system to be processed at a later time.
Right, so to have the serialization performed transparently I'm about to be using the meta-type system. However, since the type of the object referenced by the base class pointer is not known at compile-time, I have to retrieve the meta-type id from the
QMpiMessage *
- the base class' pointer.QObject
provides thevirtual const QMetaObject * QObject::metaObject() const
exactly for such reasons, at least I assume that's the reason, but my base class is not aQObject
. The question boils down to:
How can I retrieve the meta-type id at runtime, provided I don't inherit fromQObject
?Notice that if it's better/easier I could convert the base class to extend
QObject
if needed, but I'd wish to avoid it if possible. Since this is a "framework" in the making, I'm open to any other solution(s) that suit the end result I'm after.With qMetaTypeId to get the id of your type?
This ain't doing me any good, since I need to retrieve the meta-type ids at runtime (not at compile time) for an object belonging to an hierarchy (the base class is known). The derived class can't be known at compile time, because the base is from the library I'm working on and the subclasses are user-defined.
Kind regards,
Konstantin. - The user creates a message object (derived from
-
Hi! Your function
void QMpiNode::send(const QMpiMessage * message, bool synchronous)
seems to be the place where you want to serializemessage
intopayload
, right? Why can't message do it by itself? likeQByteArray QMpiMessage::serialize() const
? -
Q_DECLARE_METATYPE
only registers a type in meta type system. It's not enough to get reflection features out of a type this way (it does not create aQMetaObject
), so for the thing to be useful at all you need at leastQ_GADGET
in both base and derived classes.Assuming base and derived are Q_GADGETs you want to get a static member (staticMetaObject) of a derived class having only a pointer to the base class. The only way to go "up" in the inheritance chain in c++, when you don't know types at compile time, is via virtualization, and since Q_GADGET does not provide any, you need to provide it yourself (pretty much one of the things Q_OBJECT does).
For example:
#define FOO Q_GADGET \ public : \ virtual const QMetaObject* metaObject() { return &staticMetaObject; } class Base { FOO }; class Derived : public Base { FOO };
With this you can introspect both derived and base like this:
Base* ptr = new Derived(); qDebug() << ptr->metaObject()->className(); //prints "Derived" qDebug() << ptr->metaObject()->superClass()->className(); //prints "Base"
-
Hello,
I most certainly was not expecting such interest, based on my other questions, and so much good advice. I want to thank you in advance, before going in to respond in turn to each of you.@Wieland
Your function void QMpiNode::send(const QMpiMessage * message, bool synchronous) seems to be the place where you want to serialize message into payload, right?
This is exactly correct. I'm planing to serialize the message inside the
send
function.Why can't message do it by itself? like QByteArray QMpiMessage::serialize() const?
It will, and it should, since as far as I can see there's no other way to delegate the responsibility through the inherited classes, and naturally the base class couldn't possibly know how to serialize the derived classes' data. I actually think of implementing a "virtual stream operator" of sorts, something like this:
QDataStream & operator << (QDataStream & stream, QMpiMessage & message) { stream << messageTypeId; //< This is why I want to retrieve the meta-type id for the QMpiMessage derived classes. message.serialize(stream); }
where one'd have
virtual void QMpiMessage::serialize(QDataStream &) const = 0
in the base class.Q_DECLARE_METATYPE only registers a type in meta type system. It's not enough to get reflection features out of a type this way (it does not create a QMetaObject), so for the thing to be useful at all you need at least Q_GADGET in both base and derived classes.
You're right of course, it seems I wasn't much thinking when I wrote the post ... :|
Assuming base and derived are Q_GADGETs you want to get a static member (staticMetaObject) of a derived class having only a pointer to the base class. The only way to go "up" in the inheritance chain in c++, when you don't know types at compile time, is via virtualization, and since Q_GADGET does not provide any, you need to provide it yourself (pretty much one of the things Q_OBJECT does).
Right, so what about making my own macro instead that shouldn't even depend on
Q_GADGET
that'd do the virtual's overriding? Something like this:#define MPI_MESSAGE(ClassName) \ virtual int metaTypeId() const { return qMetaTypeId<ClassName>(); } class MyMpiMessage : public QMpiMessage { MPI_MESSAGE(MyMpiMessage) }
With the understanding that all of
QMpiMessage
descendants must be declared with the meta-type system? This would also allow me to not worry about the base class being passed as a pointer. Additionally, It will permit to change thesend()
prototype to:void QMpiNode::send(const QMpiMessage &, bool synchronous = false);
and leave the user to worry about memory management of the messages?
Thanks again guys for the input!
Kind regards. -
@kshegunov said:
#define MPI_MESSAGE(ClassName)
virtual int metaTypeId() const { return qMetaTypeId<ClassName>(); }This won't work because you can't use
qMetaTypeId<>()
beforeQ_DECLARE_METATYPE<>
. Thus you need to use the runtime checkQMetaType::type()
. The following should work:#define xstr(s) stringify(s) #define stringify(s) #s #define MPI_MESSAGE(ClassName) \ virtual int metaTypeId() const { return QMetaType::type( stringify(ClassName) ); }
Note that you also have to call
qRegisterMetaType<MyMpiMessage>();
before you use the function the first time (you'd usually do it in main.cpp). -
@Wieland
I don't understand. The macro will be expanded before compilation and will generate the virtual override for the method. Said override will not be called until the program is run and the meta-type id is actually needed (in theQMpiNode::send()
function). So I don't see a reason why a function call would be made before theQ_DECLARE_METATYPE
macro expansion.Note that you also have to call qRegisterMetaType<MyMpiMessage>(); before you use the function the first time (you'd usually do it in main.cpp).
I believe
qMetaTypeId<MyMpiMessage>()
automatically registers the template argument with the runtime, but I might be wrong. I'll have to check.Kind regards.
-
qMetaTypeId will break the compilation if used before Q_DECLARE_METATYPE. I tested it before I posted my reply to avoid talking bullsh.. :-)
qmpimessage.h
#ifndef QMPIMESSAGE_H #define QMPIMESSAGE_H #include <QString> #define xstr(s) stringify(s) #define stringify(s) #s #define MPI_MESSAGE(ClassName) \ virtual int metaTypeId() const { return QMetaType::type( stringify(ClassName) ); } class QMpiMessage { public: QMpiMessage(); virtual QString sayHi() const; }; #endif // QMPIMESSAGE_H
qpimessage.cpp
#include "qmpimessage.h" QMpiMessage::QMpiMessage() { } QString QMpiMessage::sayHi() const { return "base"; }
mympimessage.h
#ifndef MYMPIMESSAGE_H #define MYMPIMESSAGE_H #include <QMetaType> #include "qmpimessage.h" class MyMpiMessage : public QMpiMessage { public: MPI_MESSAGE(MyMpiMessage) MyMpiMessage(); QString sayHi() const; }; Q_DECLARE_METATYPE(MyMpiMessage) #endif // MYMPIMESSAGE_H
mympimessage.cpp
#include "mympimessage.h" MyMpiMessage::MyMpiMessage() : QMpiMessage() { } QString MyMpiMessage::sayHi() const { return "derived"; }
main.cpp
#include <QApplication> #include <QDebug> #include "mympimessage.h" int main(int argc, char *argv[]) { QApplication a(argc, argv); qRegisterMetaType<MyMpiMessage>(); MyMpiMessage mmm; qDebug() << mmm.sayHi(); qDebug() << mmm.metaTypeId(); qDebug() << qMetaTypeId<MyMpiMessage>(); return 0; }
-
@Wieland
Damn! You're obviously right and I think theQ_DECL_CONSTEXPR
is the reason for this somewhat curious behavior:template <typename T> inline Q_DECL_CONSTEXPR int qMetaTypeId();
Although, I think your macro could be simplified a bit (unless I'm completely clueless again):
#define MPI_MESSAGE(ClassName) \ virtual int metaTypeId() const { return QMetaType::type("##ClassName##"); }
Note that you also have to call qRegisterMetaType<MyMpiMessage>();
This also makes much more sense to me now ...
-
@kshegunov said:
Although, I think your macro could be simplified a bit
This might be true. To be honest, I have a cheat book for preprocessor magic because I find it too hard to make things right without. ;-)
-
@Wieland said:
This might be true. To be honest, I have a cheat book for preprocessor magic because I find it too hard to make things right without.
One of the reasons I hate macros in the first place, they expand and reexpand themselves endlessly it seems ... :D