moc-generated code error at Qt6 when not at Qt5
-
I have code which errors with moc at Qt6 but went through fine at Qt5. I am not looking for a code change to make it work (I have that, see at end) but for the reason for the change.
Here are 2 pairs of files for an absolutely minimal repro:
bar.h
:#ifndef BAR_H #define BAR_H class Bar { public: Bar(); }; #endif // BAR_H
bar.cpp
#include "bar.h" Bar::Bar() {}
foo.h
:#ifndef FOO_H #define FOO_H #include <QList> #include <QObject> // I want it to work with the "incomplete" type of next line, rather than #include "bar.h" here ... class Bar; class Foo : public QObject { Q_OBJECT public: Foo(); public slots: void this_works(Bar *bar); void this_errors(QList<Bar *>bars); }; #endif // FOO_H
foo.cpp
:#include "foo.h" #include "bar.h" Foo::Foo() {} void Foo::this_works(Bar *bar) { Q_UNUSED(bar); } void Foo::this_errors(QList<Bar *> bars) { Q_UNUSED(bars); }
I am "parsimonious" in my use of including
.h
files in other.h
files where I don't want/feel I need to, partly for compilation speed, partly for code separation.Running build, moc complains with this long message:
g++ -c -pipe -g -std=gnu++1z -Wall -Wextra -fPIC -D_REENTRANT -DQT_QML_DEBUG -DQT_CORE_LIB -I../../../mocerror -I. -I/usr/include/x86_64-linux-gnu/qt6 -I/usr/include/x86_64-linux-gnu/qt6/QtCore -I. -I/usr/lib/x86_64-linux-gnu/qt6/mkspecs/linux-g++ -o moc_foo.o moc_foo.cpp In file included from /usr/include/x86_64-linux-gnu/qt6/QtCore/qobject.h:18, from /usr/include/x86_64-linux-gnu/qt6/QtCore/QObject:1, from ../../foo.h:5, from moc_foo.cpp:10: /usr/include/x86_64-linux-gnu/qt6/QtCore/qmetatype.h: In instantiation of ‘struct QtPrivate::IsPointerToTypeDerivedFromQObject<Bar*>’: /usr/include/x86_64-linux-gnu/qt6/QtCore/qmetatype.h:1102:60: required from ‘struct QMetaTypeId<Bar*>’ /usr/include/x86_64-linux-gnu/qt6/QtCore/qmetatype.h:1122:38: required from ‘struct QMetaTypeId2<Bar*>’ /usr/include/x86_64-linux-gnu/qt6/QtCore/qmetatype.h:1585:1: required from ‘struct QMetaTypeId<QList<Bar*> >’ /usr/include/x86_64-linux-gnu/qt6/QtCore/qmetatype.h:2364:42: recursively required by substitution of ‘template<class T> struct QtPrivate::BuiltinMetaType<T, typename std::enable_if<QMetaTypeId2<T>::IsBuiltIn, void>::type> [with T = QList<Bar*>]’ /usr/include/x86_64-linux-gnu/qt6/QtCore/qmetatype.h:2364:42: required from ‘constexpr const QtPrivate::QMetaTypeInterface QtPrivate::QMetaTypeInterfaceWrapper<QList<Bar*> >::metaType’ /usr/include/x86_64-linux-gnu/qt6/QtCore/qmetatype.h:2486:16: required from ‘constexpr const QtPrivate::QMetaTypeInterface* QtPrivate::qTryMetaTypeInterfaceForType() [with Unique = {anonymous}::qt_meta_stringdata_Foo_t; TypeCompletePair = TypeAndForceComplete<QList<Bar*>, std::integral_constant<bool, false> >]’ /usr/include/x86_64-linux-gnu/qt6/QtCore/qmetatype.h:2537:55: required from ‘constexpr const QtPrivate::QMetaTypeInterface* const qt_incomplete_metaTypeArray [5]<{anonymous}::qt_meta_stringdata_Foo_t, QtPrivate::TypeAndForceComplete<Foo, std::integral_constant<bool, true> >, QtPrivate::TypeAndForceComplete<void, std::integral_constant<bool, false> >, QtPrivate::TypeAndForceComplete<Bar*, std::integral_constant<bool, false> >, QtPrivate::TypeAndForceComplete<void, std::integral_constant<bool, false> >, QtPrivate::TypeAndForceComplete<QList<Bar*>, std::integral_constant<bool, false> > >’ moc_foo.cpp:95:5: required from here /usr/include/x86_64-linux-gnu/qt6/QtCore/qmetatype.h:842:23: error: invalid application of ‘sizeof’ to incomplete type ‘Bar’ 842 | static_assert(sizeof(T), "Type argument of Q_PROPERTY or Q_DECLARE_METATYPE(T*) must be fully defined"); | ^~~~~~~~~
Note that it complains on
void this_errors(QList<Bar *>bars);
but not onvoid Foo::this_works(Bar *bar)
(try commenting out each method declaration, or moving frompublic slots:
topublic:
). Why? Why does a function parameter ofQList<IncompleteType *>
need any more information aboutIncompleteType
than parameterIncompleteType *
withoutQList
does? And why any different at Qt6 than at Qt5?At the time the compiler meets the
Bar
in slot parameters infoo.h
it has only encounteredclass Bar;
declaration. Infoo.cpp
I actually#include "bar.h"
. I would like to keep it like that. But to keep Qt6 moc-generated code happy I have to move#include "bar.h"
intofoo.h
, so that it has the "full" definition ofclass Bar
when it sees the slot methods. But why, and why only forQList<Bar *>
and why only at Qt6? -
I am using gcc 13.3.0, moc 6.9.0.
The only difference in our CMake files is that I require 3.16, you require 3.14. -
Hi Jon,
could it be that the build directory is "polluted" by a previous build?
I have copied your code into a new project and it builds like this (Qt 6.7 and 6.8):19:04:18: Running steps for project JonB... 19:04:18: Starting: "/opt/Qt/Tools/CMake/bin/cmake" --build /home/axel/QtDev/JonB/build/Qt_dev-Debug --target all [1/6 0.6/sec] Automatic MOC and UIC for target JonB [2/6 0.2/sec] Building CXX object CMakeFiles/JonB.dir/bar.cpp.o [3/6 0.3/sec] Building CXX object CMakeFiles/JonB.dir/foo.cpp.o [4/6 0.4/sec] Building CXX object CMakeFiles/JonB.dir/main.cpp.o [5/6 0.5/sec] Building CXX object CMakeFiles/JonB.dir/JonB_autogen/mocs_compilation.cpp.o [6/6 0.6/sec] Linking CXX executable JonB 19:04:29: The process "/opt/Qt/Tools/CMake/bin/cmake" exited normally. 19:04:29: Elapsed time: 00:11.
The only thing I actually added on top, is a
main.cpp
, creating one instance of each,Foo
andBar
on the stack. -
@Axel-Spoerl
Hi Axel, thank you for testing.All is clean, and reproducible from scratch by me.
This is a compilation issue, so
main
and creating instances doesn't matter. I don't know why you don't see the same as I do. As I move across I am still using qmake; you use cmake, I cannot see why that would cause a difference but will try tomorrow. I will also get exact Qt versions. -
Hi Jon,
Let us see your .pro file.
We’ll fix this together! -
@Axel-Spoerl
I am looking at moving this test over to cmake to see whether it makes any difference/works like you showed. Will take me a while as I discover what that involves!Meanwhile the
.pro
file you asked for, which I am using when the compiler generates the error:QT = core CONFIG += c++17 cmdline # You can make your code fail to compile if it uses deprecated APIs. # In order to do so, uncomment the following line. #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 SOURCES += \ bar.cpp \ foo.cpp \ main.cpp # Default rules for deployment. qnx: target.path = /tmp/$${TARGET}/bin else: unix:!android: target.path = /opt/$${TARGET}/bin !isEmpty(target.path): INSTALLS += target HEADERS += \ bar.h \ foo.h
-
@Axel-Spoerl
Before I try to move to cmake, in case that makes any difference. With qmake if you refer to the error in my first post you see it happens onmoc_foo.cpp
. The relevant moc/compilation lines executed are:g++ -pipe -g -std=gnu++1z -Wall -Wextra -fPIC -dM -E -o moc_predefs.h /usr/lib/x86_64-linux-gnu/qt6/mkspecs/features/data/dummy.cpp /usr/lib/qt6/libexec/moc -DQT_QML_DEBUG -DQT_CORE_LIB --include /home/jon/QtTests/mocerror/build/Desktop-Debug/moc_predefs.h -I/usr/lib/x86_64-linux-gnu/qt6/mkspecs/linux-g++ -I/home/jon/QtTests/mocerror -I/usr/include/x86_64-linux-gnu/qt6 -I/usr/include/x86_64-linux-gnu/qt6/QtCore -I. -I/usr/include/c++/13 -I/usr/include/x86_64-linux-gnu/c++/13 -I/usr/include/c++/13/backward -I/usr/lib/gcc/x86_64-linux-gnu/13/include -I/usr/local/include -I/usr/include/x86_64-linux-gnu -I/usr/include ../../foo.h -o moc_foo.cpp g++ -c -pipe -g -std=gnu++1z -Wall -Wextra -fPIC -D_REENTRANT -DQT_QML_DEBUG -DQT_CORE_LIB -I../../../mocerror -I. -I/usr/include/x86_64-linux-gnu/qt6 -I/usr/include/x86_64-linux-gnu/qt6/QtCore -I. -I/usr/lib/x86_64-linux-gnu/qt6/mkspecs/linux-g++ -o moc_foo.o moc_foo.cpp
My generated
moc_foo.cpp
file from my pasted code is 151 lines long. The error message includesrequired from ‘struct QMetaTypeId<QList<Bar*> >’
/usr/include/x86_64-linux-gnu/qt6/QtCore/qmetatype.h:2364:42: recursively required by substitution of ‘template<class T> struct QtPrivate::BuiltinMetaType<T, typename std::enable_if<QMetaTypeId2<T>::IsBuiltIn, void>::type> [with T = QList<Bar*>]’
QtPrivate::TypeAndForceComplete<QList<Bar*>, std::integral_constant<bool, false>
moc_foo.cpp:95:5: required from here
There I see function:
Q_CONSTINIT const QMetaObject Foo::staticMetaObject = { { QMetaObject::SuperData::link<QObject::staticMetaObject>(), qt_meta_stringdata_Foo.offsetsAndSizes, qt_meta_data_Foo, qt_static_metacall, nullptr, qt_incomplete_metaTypeArray<qt_meta_stringdata_Foo_t, // Q_OBJECT / Q_GADGET QtPrivate::TypeAndForceComplete<Foo, std::true_type>, // method 'this_works' QtPrivate::TypeAndForceComplete<void, std::false_type>, QtPrivate::TypeAndForceComplete<Bar *, std::false_type>, // method 'this_errors' QtPrivate::TypeAndForceComplete<void, std::false_type>, QtPrivate::TypeAndForceComplete<QList<Bar*>, std::false_type> >, nullptr } };
The line generating the error is the
QtPrivate::TypeAndForceComplete<QList<Bar*>
Something in evaluating that is what is complaining that it does not have the "complete" definition of
Bar
class/type (because of how I wish to arrange my#include "bar.h"
). But see how there is a also a line forQtPrivate::TypeAndForceComplete<Bar *, std::false_type>
. That does not error. Hence the question: apart from why it has changed at Qt6, why does a slot parameter ofQList<Bar *>
need to know the full definition ofBar
when a slot parameter ofBar *
does not?Ultimately the error comes from the moc-generated code causing the compiler to try to evaluate
sizeof(Bar)
/whetherBar
has aQ_OBJECT
(I think)/whatever it is doing withQ_PROPERTY or Q_DECLARE_METATYPE(T*)
. Which of course fails on an incomplete definition ofBar
. I know about copying of signal/slot parameters (e.g. it certainly would complain if I tried to passBar
orQList<Bar>
) but I am passing pointers and I do not see whyQList<Bar *>
(fails) needs to know any more aboutBar
thanBar *
(succeeds) does.Do you not have similar in your generated
moc_foo.cpp
for your build using cmake? -
@Axel-Spoerl
I have now changed over to cmake (projectmocerror2
). FWIW myCMakeLists.txt
:cmake_minimum_required(VERSION 3.14) project(mocerror2 LANGUAGES CXX) set(CMAKE_AUTOUIC ON) set(CMAKE_AUTOMOC ON) set(CMAKE_AUTORCC ON) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Core) find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Core) add_executable(mocerror2 main.cpp bar.cpp bar.h foo.cpp foo.h ) target_link_libraries(mocerror2 Qt${QT_VERSION_MAJOR}::Core) include(GNUInstallDirs) install(TARGETS mocerror2 LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} )
As I expected it makes no difference and I get same error:
[ 14%] Automatic MOC and UIC for target mocerror2 [ 14%] Built target mocerror2_autogen [ 28%] Building CXX object CMakeFiles/mocerror2.dir/mocerror2_autogen/mocs_compilation.cpp.o In file included from /usr/include/x86_64-linux-gnu/qt6/QtCore/qobject.h:18, from /usr/include/x86_64-linux-gnu/qt6/QtCore/QObject:1, from /home/jon/QtTests/mocerror2/build/Desktop-Debug/mocerror2_autogen/EWIEGA46WW/../../../../foo.h:5, from /home/jon/QtTests/mocerror2/build/Desktop-Debug/mocerror2_autogen/EWIEGA46WW/moc_foo.cpp:10, from /home/jon/QtTests/mocerror2/build/Desktop-Debug/mocerror2_autogen/mocs_compilation.cpp:2: /usr/include/x86_64-linux-gnu/qt6/QtCore/qmetatype.h: In instantiation of ‘struct QtPrivate::IsPointerToTypeDerivedFromQObject<Bar*>’: /usr/include/x86_64-linux-gnu/qt6/QtCore/qmetatype.h:1102:60: required from ‘struct QMetaTypeId<Bar*>’ /usr/include/x86_64-linux-gnu/qt6/QtCore/qmetatype.h:1122:38: required from ‘struct QMetaTypeId2<Bar*>’ /usr/include/x86_64-linux-gnu/qt6/QtCore/qmetatype.h:1585:1: required from ‘struct QMetaTypeId<QList<Bar*> >’ /usr/include/x86_64-linux-gnu/qt6/QtCore/qmetatype.h:2364:42: recursively required by substitution of ‘template<class T> struct QtPrivate::BuiltinMetaType<T, typename std::enable_if<QMetaTypeId2<T>::IsBuiltIn, void>::type> [with T = QList<Bar*>]’ /usr/include/x86_64-linux-gnu/qt6/QtCore/qmetatype.h:2364:42: required from ‘constexpr const QtPrivate::QMetaTypeInterface QtPrivate::QMetaTypeInterfaceWrapper<QList<Bar*> >::metaType’ /usr/include/x86_64-linux-gnu/qt6/QtCore/qmetatype.h:2486:16: required from ‘constexpr const QtPrivate::QMetaTypeInterface* QtPrivate::qTryMetaTypeInterfaceForType() [with Unique = {anonymous}::qt_meta_stringdata_Foo_t; TypeCompletePair = TypeAndForceComplete<QList<Bar*>, std::integral_constant<bool, false> >]’ /usr/include/x86_64-linux-gnu/qt6/QtCore/qmetatype.h:2537:55: required from ‘constexpr const QtPrivate::QMetaTypeInterface* const qt_incomplete_metaTypeArray [5]<{anonymous}::qt_meta_stringdata_Foo_t, QtPrivate::TypeAndForceComplete<Foo, std::integral_constant<bool, true> >, QtPrivate::TypeAndForceComplete<void, std::integral_constant<bool, false> >, QtPrivate::TypeAndForceComplete<Bar*, std::integral_constant<bool, false> >, QtPrivate::TypeAndForceComplete<void, std::integral_constant<bool, false> >, QtPrivate::TypeAndForceComplete<QList<Bar*>, std::integral_constant<bool, false> > >’ /home/jon/QtTests/mocerror2/build/Desktop-Debug/mocerror2_autogen/EWIEGA46WW/moc_foo.cpp:95:5: required from here /usr/include/x86_64-linux-gnu/qt6/QtCore/qmetatype.h:842:23: error: invalid application of ‘sizeof’ to incomplete type ‘Bar’ 842 | static_assert(sizeof(T), "Type argument of Q_PROPERTY or Q_DECLARE_METATYPE(T*) must be fully defined"); | ^~~~~~~~~ gmake[2]: *** [CMakeFiles/mocerror2.dir/build.make:82: CMakeFiles/mocerror2.dir/mocerror2_autogen/mocs_compilation.cpp.o] Error 1 gmake[1]: *** [CMakeFiles/Makefile2:86: CMakeFiles/mocerror2.dir/all] Error 2 gmake: *** [Makefile:136: all] Error 2 10:38:41: The process "/usr/bin/cmake" exited with code 2.
I do not understand why you do not get same error as I do.
My gcc is 13.2.0. My moc is 6.4.2 as is my Qt6.
-
I am using gcc 13.3.0, moc 6.9.0.
The only difference in our CMake files is that I require 3.16, you require 3.14. -
@Axel-Spoerl
Thank you. So what would you like me to do about our difference in behaviour? :)
[I fetch all my Qt stuff fromapt
as supplied with Ubuntu 24.04 distro.] -
I actually remember something similar, early when QVector was nuked for QList to prosper.
I think if you change it from
QList<Bar *>bars
toQList<Bar *> *bars
it actually works. Axel is using a way later Qt6 version ( 6.8 vs 6.4.2) QList was probably touched again in between those versions, to fix this :D -
@J-Hilk said in moc-generated code error at Qt6 when not at Qt5:
I think if you change it from
QList<Bar *>bars
toQList<Bar *> *bars
it actually works.But those are quite different types! :) Are you just showing me that to say what it could cope with?
Axel is using a way later Qt6 version ( 6.8 vs 6.4.2) QList was probably touched again in between those versions, to fix this :D
Ah! Well in that case it's obviously not to be reported. Thanks for the heads-up.
With Qt5 I always had Qt 5.15 (or maybe 5.12) via
apt
under various Ubuntu releases over the years. And I have to say that was just great, I didn't encounter problems and I didn't need later versions. I suspected Qt6 would be problematic, looks like I get a quite older version and am going to have issues which really need a later version from TQtC :( Ho-hum.... -
- I have never even attempted to download from TQtC, having seen the endless problems with downloads/mirrors. Fills me with dread....
- ...As does when I see people who for whatever reason (e.g. maybe to do with configuration, whatever it happens) get an error during compilation of the whole of Qt....
- I don't have the disk space in VM for Qt sources + compilation outputs.
- I don't have the memory in VM to compile it. Not to mention the requirements for
QWebEngine
. - I share (development) with other people using Qt from Ubuntu distro. They are not going to want to fetch/compile/install or take from me.
Am I being difficult? ;-) It was all so simple with Qt5, everything worked. Now beginning to think of backing out of the whole of Qt6 and reverting to Qt5. But that would be a shame for my answering questions on this forum.
-