Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Qt MetaType and Circular Dependency



  • Hi!
    i have two qt classes which i intend to use in templates using the Grantlee library, the problem is the two classes use each other like in the code bellow that was working untill using grantlee if you are not familiar with grantlee it just fill template from qt objects like Django,jinja

    /file a.h
    class B;
    Q_DECLARE_METATYPE(QList<B*>)
    class A : public QOBject{
    QOBJECT
        Q_PROPERTY(QList<B*> bList...)
    
    };
    
    // file b.h
    class A;
    class B : public QOBject{
    QOBJECT
        Q_PROPERTY(A* a...)
    
    };
    

    the core problem is that to properly use 'a' and 'bList' in grantLee the Classes A&B need to be fully declared so i get this error QMetaProperty::read: Unable to handle unregistered datatype 'QList' for property 'A::bList'

    i tried using Q_DECLARE_METATYPE(QList) and same for 'a' but Q_DECLARE_METATYPE require the class to be fully described so i tried with Q_DECLARE_OPAQUE_POINTER(B) i get this error grantlee.customtype: Don't know how to handle metatype B

    is there a way to go around this circular dependency + qt properties problem ? or a change in the architecture is a must


  • Qt Champions 2017

    @yassser said in Qt MetaType and Circular Dependency:

    Hi!

    Hi.

    the core problem is that to properly use 'a' and 'bList' in grantLee the Classes A&B need to be fully declared so i get this error QMetaProperty::read: Unable to handle unregistered datatype 'QList' for property 'A::bList'

    Where is QMetaProperty::read called? Where do you get the error?

    i tried using Q_DECLARE_METATYPE(QList) and same for 'a' but Q_DECLARE_METATYPE require the class to be fully described so i tried with Q_DECLARE_OPAQUE_POINTER(B) i get this error grantlee.customtype: Don't know how to handle metatype B

    Q_DECLARE_METATYPE is used with the actual type, QList by itself isn't a "type" in the sense it's a template. To be compiled instantiantion needs to be performed, for which the template parameter must be known. Can you do something like this:

    typedef QList<B *> BList;
    Q_DECLARE_METATYPE(BList)
    

    Does this produce an error?

    is there a way to go around this circular dependency + qt properties problem ?

    To me it's not exactly clear where the problem lies, so let's take it a step at a time.



  • @kshegunov

    Where is QMetaProperty::read called? Where do you get the error?

    considering an instance 'aObj' given to grantlee engine to be rendered
    and we want to get the property 'a' of the first Bobject in the blist
    the error happened here {{ aObject.blist.0.a }} //0:first element
    in normal circumstances it would work providing that you use #include 'b.h' in A and
    #include 'a.h' in B to be able to render "aObject.blist.0" and "0.a"
    but that would cause circular dependency
    so is there some macro that register the type without having to include the file ?

    typedef QList<B *> BList;

    Q_DECLARE_METATYPE(BList)
    Does this produce an error?

    C2338: Type argument of Q_DECLARE_METATYPE(T*) must be fully defined
    this is the error that it produces, note that the error dosnt show up when you include the header files instead of predefining the class. even when you use Q_DECLARE_METATYPE(QList<B*>)



  • If B is a QObject, QList<QObject*> will work out of the box so you don't need to declare it as a metatype at all. Can you explain why you have this need?



  • @VRonin
    you are right ,when you are using just qt to access the property(getters/setters...) it would work whithout having to declare it, but grantlee require you to register the type to QVariant so it can use it in templates, like to access the first element and inspect it's properties {{ blist.0.propertyofb }}


  • Qt Champions 2017

    @yassser said in Qt MetaType and Circular Dependency:

    you are right ,when you are using just qt to access the property(getters/setters...) it would work whithout having to declare it, but grantlee require you to register the type to QVariant so it can use it in templates, like to access the first element and inspect it's properties {{ blist.0.propertyofb }}

    You can't register QObjects as metatypes and can't put QObjects in QVariant because they are can't be copied. By definition (a.k.a. metatype requirements) a QObject derived class isn't and can't be a meta-type. The only thing you could possibly do is to have a QObject * put into a QVariant, but that's rather different.
    That said, I still don't understand the problem, though, I have no idea what this library expects nor where (and by "where" I mean a concrete piece of c++ code) this error occurs.



  • @kshegunov
    Hi!

    You can't register QObjects as metatypes and can't put QObjects in QVariant because they are can't be copied. By definition (a.k.a. metatype requirements)

    correct me if i'm wrong but does the copying part mean that you cant copy the properties to variant using QVariant::fromValue(QObject*) because it is working some QObject Derived classes in the Project , or you mean add the QObject subclass to the Qt Standard Types as in this exemple CustomTypes there is 3 requirement

    Before we begin, we need to ensure that the custom type we are creating meets all the requirements imposed by QMetaType. In other words, it must provide:

    a public default constructor,

    a public copy constructor, and

    a public destructor.or the

    as for declaring QList<B*> i will switch to the typedef solution as it is more safe like you said although the other one(declar_type(QList<B*>)) is working somehow

    but the core problem i'm facing is the requirement of Q_DECLARE_METATYPE(B*) that B should be fully defined(defining the class using #include'b.h' instead of 'class B') as i cant include the header files because it will cause circular dependency because A it self require the same process


  • Qt Champions 2017

    @yassser said in Qt MetaType and Circular Dependency:

    correct me if i'm wrong but does the copying part mean that you cant copy the properties to variant using QVariant::fromValue(QObject*) because it is working some QObject Derived classes in the Project , or you mean add the QObject subclass to the Qt Standard Types as in this exemple CustomTypes there is 3 requirement

    Both. When adding a QObject * to a QVariant you copy nothing, you just transfer a reference, and if that reference disappears in the mean time, well that's cause for concern.

    @yassser said in Qt MetaType and Circular Dependency:

    as for declaring QList<B*> i will switch to the typedef solution as it is more safe like you said although the other one(declar_type(QList<B*>)) is working somehow

    For what it's worth, declaration of metatypes should happen where the type is defined, not somewhere else. So even ideologically:

    class B;
    Q_DECLARE_METATYPE(QList<B*>)
    

    is wrong. That metatype declaration should've gone where B is defined, aside from it being needed or not.

    but the core problem i'm facing is the requirement of Q_DECLARE_METATYPE(B*) that B should be fully defined(defining the class using #include'b.h' instead of 'class B')

    Still talking hypotheticals. I don't see any concrete error that one gets from the compiler, nor a specific piece of cpp code that's the cause of said error.

    as i cant include the header files because it will cause circular dependency because A it self require the same process

    Where does it require that and why? Please post a snippet, I'm getting rather frustrated here. Consider this in the meantime:

    class B;
    typedef QList<B *> BList;
    
    class A
    {
    public:
        void setBList(const BList &); //< This doesn't require a definition of BList, and by extension a definition of B isn't required
        BList bList() const; //< Nor does this
    };