Qt Forum

    • Login
    • Search
    • Categories
    • Recent
    • Tags
    • Popular
    • Users
    • Groups
    • Search
    • Unsolved

    Call for Presentations - Qt World Summit

    Solved Meta-type id of an object

    General and Desktop
    qmetaobject qmetatype
    4
    13
    7037
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • kshegunov
      kshegunov Moderators last edited by kshegunov

      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 of QObject, but I've made them gadgets with Q_GADGET. While QObject provides QObject::metaObject there's no such straight-forward way to retrieve the meta-object for gadgets. I suppose I could get it from staticMetaObject, 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.

      Read and abide by the Qt Code of Conduct

      1 Reply Last reply Reply Quote 0
      • SGaist
        SGaist Lifetime Qt Champion last edited by

        Hi,

        Isn't that what QMetaType provides ? With qMetaTypeId to get the id of your type ?

        Interested in AI ? www.idiap.ch
        Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

        kshegunov 1 Reply Last reply Reply Quote 1
        • kshegunov
          kshegunov Moderators @SGaist last edited by kshegunov

          @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 with Q_DECLARE_METATYPE should work as well, and most probably is the better way, as these objects of type QMpiMessage (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:

          1. The user creates a message object (derived from QMpiMessage).
          2. He/she passes it to the QMpiNode (QObject subclass that manages communication with a computational node) for sending. (I have void QMpiNode::send(const QMpiMessage *, bool synchronous = false) for that purpose)
          3. The message object is serialized in a QByteArray
          4. 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 the virtual const QMetaObject * QObject::metaObject() const exactly for such reasons, at least I assume that's the reason, but my base class is not a QObject. The question boils down to:
          How can I retrieve the meta-type id at runtime, provided I don't inherit from QObject?

          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.

          Read and abide by the Qt Code of Conduct

          1 Reply Last reply Reply Quote 0
          • ?
            A Former User last edited by A Former User

            Hi! Your function void QMpiNode::send(const QMpiMessage * message, bool synchronous) seems to be the place where you want to serialize message into payload, right? Why can't message do it by itself? like QByteArray QMpiMessage::serialize() const?

            1 Reply Last reply Reply Quote 1
            • Chris Kawa
              Chris Kawa Moderators last edited by Chris Kawa

              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.

              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"
              
              1 Reply Last reply Reply Quote 1
              • kshegunov
                kshegunov Moderators last edited by kshegunov

                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.

                @Chris-Kawa

                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 the send() 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.

                Read and abide by the Qt Code of Conduct

                1 Reply Last reply Reply Quote 0
                • ?
                  A Former User last edited by

                  @kshegunov said:

                  #define MPI_MESSAGE(ClassName)
                  virtual int metaTypeId() const { return qMetaTypeId<ClassName>(); }

                  This won't work because you can't use qMetaTypeId<>() before Q_DECLARE_METATYPE<>. Thus you need to use the runtime check QMetaType::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).

                  kshegunov 1 Reply Last reply Reply Quote 1
                  • kshegunov
                    kshegunov Moderators @Guest last edited by kshegunov

                    @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 the QMpiNode::send() function). So I don't see a reason why a function call would be made before the Q_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.

                    Read and abide by the Qt Code of Conduct

                    1 Reply Last reply Reply Quote 0
                    • ?
                      A Former User last edited by

                      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;
                      }
                      
                      kshegunov 1 Reply Last reply Reply Quote 1
                      • kshegunov
                        kshegunov Moderators @Guest last edited by kshegunov

                        @Wieland
                        Damn! You're obviously right and I think the Q_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 ...

                        Read and abide by the Qt Code of Conduct

                        1 Reply Last reply Reply Quote 0
                        • ?
                          A Former User last edited by

                          @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. ;-)

                          kshegunov 1 Reply Last reply Reply Quote 1
                          • kshegunov
                            kshegunov Moderators @Guest last edited by

                            @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

                            Read and abide by the Qt Code of Conduct

                            kshegunov 1 Reply Last reply Reply Quote 0
                            • kshegunov
                              kshegunov Moderators @kshegunov last edited by

                              Well, just letting you know that Chris' suggestion seems the most painless way to do the thing I need, so I'm going with it.
                              Thanks all!

                              Read and abide by the Qt Code of Conduct

                              1 Reply Last reply Reply Quote 0
                              • First post
                                Last post