QMetaType, C++ Forward Declaration

Q_DECLARE_OPAQUE_POINTER

Had huge problems with Apps using C++ Forward Declarations.

In my Entity / DTO – data models there are references between QObject* classes in both directions. To avoid cyclic references while importing C++ header, C++ Forward Declaration is used.

This works well in Qt 5.15, but fails in 6.6 with errors like:

Error Pointer Meta Types…must be declared with Q_DECLARE_OPAQUE_POINTER

In file included from moc_MixArticle.cpp:9:
In file included from ./../../_qt_ws6/thielen_eingang_Qt6/cpp/gen/MixArticle.hpp:4:
In file included from ../../_qt_sdks/sdk_6_6b2/6.6.0/android_arm64_v8a/include/QtCore/QObject:1:
In file included from ../../_qt_sdks/sdk_6_6b2/6.6.0/android_arm64_v8a/include/QtCore/qobject.h:18:
../../_qt_sdks/sdk_6_6b2/6.6.0/android_arm64_v8a/include/QtCore/qmetatype.h:1189:13: error: static_assert failed due to requirement 'is_complete<OrderItem, void>::value' "Pointer Meta Types must either point to fully-defined types or be declared with Q_DECLARE_OPAQUE_POINTER(T *)"
            static_assert(is_complete<Pointed, void>::value,
            ^             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

So I added the recommended Q_DECLARE_OPAQUE_POINTER:

#include <QMetaObject>
...
class OrderItem;
...
#ifndef OPAQUE_OrderItem
  #define OPAQUE_OrderItem
  Q_DECLARE_OPAQUE_POINTER(OrderItem*)
#endif
...
class MixArticle: public QObject {...

Yep, in this scenario it is working, but not always in my Apps.

It always works for bi-directional references using ForwardDeclaration only at one of the sides as in the example above.

Error explicit specialization

Now in some use-cases after adding Q_DECLARE_OPAQUE_POINTER I’m getting ‘explicit specialization‘ errors with Qt 6.6.

Found out that Qt 6.6 doesn’t like scenarios like this, where at both sides C++ ForwardDeclaration was used, so this won’t work:

In 5.15 the C++ Compiler was happy with this, but in 6.6 an Error occured:

error: explicit specialization of 'QtPrivate::IsPointerDeclaredOpaque<MyQObject *>' after instantiation Q_DECLARE_OPAQUE_POINTER(MyQObject*)

Workaround explicit specialization

My Workaround: Remove the ForwardDeclaration from one of the sides of your bi-directional references.

This is working in most of my (20) ported Apps.

Problem with recursive References

Unfortunately there are still situations where now C++ Compiler is complaining.

Per ex. this is a subset with 5 Entities, where 3 bi-directional references are used (blue marked).

Over all there are more then 20 Entities with 8 bi-directional references in this App, but the problem can be nailed down to these 5 ones.

The App is a very special App with much user comfort to jump around between all the (in-memory) data, where data is saved as JSON.

Following my workaround I removed the C++ Forward Declaration from one side of each bi-directional reference and added the opaque pointer to the other one.

No problems with Q_DECLARE_OPAQUE_POINTER, but now many errors occured from C++ like this:

In included file: main file cannot be included recursively when building a preamble
Serie.hpp:14:10: error occurred here

Hint: No problems appear with 5.15: C++ was happy with all the ForwardDeclarations in bi-directional references, where the headers were all included from DataManager class. The DataManager is the first class in code to deal with all my Entities, so I know if Entities are used later, the Type is fully defined before.

Tried to find a working scenario and changed the side of the bi-directional references, where the opaque pointer is declared, but always errors occured from C ++.

The only way to get it working was to decouple some relations, so I changed my data model to go on:

  • removed reference from Report -> Client
  • removed reference from ScheduleData -> Client
  • removed Schedule -> Client from bi-directional Schedule <-> Client
    • so only Client -> Schedule remains

Now C++ and Q_DECLARE_OPAQUE_POINTER are happy.

When there’s later time (after finishing all ports and also done the QMake->CMake – port) I’ll try to create a reproducable example App, because it’s not the best workaround to change the data model and change C++ and QML code because of OPAQUE POINTER problems 😉

For now have described my problems here: QTBUG-117613 – perhaps someone finds a solution ? Probably if Q_DECLARE_OPAQUE_POINTER can be used on both sides of bi-directional references as C++ can do this, all the other side-effects would be solved.

Back to ekke’s Checklist ?


Bugreports QMetaObject:

Got some help and worthfully infos from:

More about QMetaType and QVariant see Qt QMetaType 5-to-6 docs