App crashes using gadgets defined in a library
-
wrote on 17 May 2023, 19:49 last edited by
Given two CMake subprojects: foo and bar, I want to define Q_GADGET in bar and use it in foo, but the application crashes when accessing a QObject property of type Bar (defined in bar project).
foo CMakeLists, created by QtCreator, I just added SOURCES / HEADER and added
bar
totarget_link_libraries
:cmake_minimum_required(VERSION 3.16) project(foo VERSION 0.1 LANGUAGES CXX) set(CMAKE_CXX_STANDARD_REQUIRED ON) find_package(Qt6 6.5 REQUIRED COMPONENTS Quick) qt_standard_project_setup(REQUIRES 6.5) set(SOURCES # cmake-format: sort main.cpp foo.cpp fooobjectwithbarproperty.cpp ) set(HEADER # cmake-format: sort fooobjectwithbarproperty.h foo.h ) qt_add_executable(appfoo main.cpp ) qt_add_qml_module(appfoo URI foo VERSION 1.0 QML_FILES Main.qml SOURCES ${SOURCES} ${HEADER} ) set_target_properties(appfoo PROPERTIES MACOSX_BUNDLE_GUI_IDENTIFIER my.example.com MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION} MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR} MACOSX_BUNDLE TRUE WIN32_EXECUTABLE TRUE ) target_link_libraries(appfoo PUBLIC Qt6::Quick bar ) install(TARGETS appfoo BUNDLE DESTINATION . LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} )
bar CMakeLists:
cmake_minimum_required(VERSION 3.14) project(bar 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_library(bar SHARED bar_global.h bar.cpp bar.h ) target_link_libraries(bar PRIVATE Qt${QT_VERSION_MAJOR}::Core) target_compile_definitions(bar PRIVATE BAR_LIBRARY)
top-level CMakeLists:
cmake_minimum_required(VERSION 3.26.0) project( FooBar VERSION 0.0.1 DESCRIPTION "Demo for Qt issue" LANGUAGES CXX) set(CMAKE_CXX_STANDARD 20) add_subdirectory(foo) add_subdirectory(bar)
My QObject, defined in foo, looks like this:
class FooObjectWithBarProperty : public QObject { Q_OBJECT Q_PROPERTY(Bar bar READ getBar WRITE setBar NOTIFY barChanged) Q_PROPERTY(Foo foo READ getFoo WRITE setFoo NOTIFY fooChanged) public: const Bar getBar() const { return _bar; } void setBar(const Bar& bar) { _bar = bar; emit barChanged(_bar); } const Foo getFoo() const { return _foo; } void setFoo(const Foo& foo) { _foo = foo; emit fooChanged(_foo); } private: Bar _bar; Foo _foo; signals: void barChanged(const Bar&); void fooChanged(const Foo&); };
Foo is defined in foo, while Bar is defined in Bar, they both look exactly the same:
struct Foo { Q_GADGET Q_PROPERTY(double x MEMBER x) Q_PROPERTY(double y MEMBER y) public: double x, y; };
Given the following setup in main.cpp:
QQmlApplicationEngine engine; engine.rootContext()->setContextProperty("obj", new FooObjectWithBarProperty());
I added a Text item into my Main.qml that looks like this:
Text { anchors.centerIn: parent text: obj.foo.x }
This correctly displays 0. If I change the binding too obj.bar.x the app crashes:
Stacktrace here suggest the metatype for Bar is not correctly registered, but I don't know how to correctly register it. I've tried qRegisterMetaType<Bar>, Q_DECLARE_METATYPE(Bar) in both foo or bar, I experimented a bit with QML_FOREIGN, but the docs are very sparse on it. All to no avail.
What is the necessary change here, and why?
Repo for this is available at https://github.com/marcinjakubowski/foo_bar_qt_issue
-
Given two CMake subprojects: foo and bar, I want to define Q_GADGET in bar and use it in foo, but the application crashes when accessing a QObject property of type Bar (defined in bar project).
foo CMakeLists, created by QtCreator, I just added SOURCES / HEADER and added
bar
totarget_link_libraries
:cmake_minimum_required(VERSION 3.16) project(foo VERSION 0.1 LANGUAGES CXX) set(CMAKE_CXX_STANDARD_REQUIRED ON) find_package(Qt6 6.5 REQUIRED COMPONENTS Quick) qt_standard_project_setup(REQUIRES 6.5) set(SOURCES # cmake-format: sort main.cpp foo.cpp fooobjectwithbarproperty.cpp ) set(HEADER # cmake-format: sort fooobjectwithbarproperty.h foo.h ) qt_add_executable(appfoo main.cpp ) qt_add_qml_module(appfoo URI foo VERSION 1.0 QML_FILES Main.qml SOURCES ${SOURCES} ${HEADER} ) set_target_properties(appfoo PROPERTIES MACOSX_BUNDLE_GUI_IDENTIFIER my.example.com MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION} MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR} MACOSX_BUNDLE TRUE WIN32_EXECUTABLE TRUE ) target_link_libraries(appfoo PUBLIC Qt6::Quick bar ) install(TARGETS appfoo BUNDLE DESTINATION . LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} )
bar CMakeLists:
cmake_minimum_required(VERSION 3.14) project(bar 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_library(bar SHARED bar_global.h bar.cpp bar.h ) target_link_libraries(bar PRIVATE Qt${QT_VERSION_MAJOR}::Core) target_compile_definitions(bar PRIVATE BAR_LIBRARY)
top-level CMakeLists:
cmake_minimum_required(VERSION 3.26.0) project( FooBar VERSION 0.0.1 DESCRIPTION "Demo for Qt issue" LANGUAGES CXX) set(CMAKE_CXX_STANDARD 20) add_subdirectory(foo) add_subdirectory(bar)
My QObject, defined in foo, looks like this:
class FooObjectWithBarProperty : public QObject { Q_OBJECT Q_PROPERTY(Bar bar READ getBar WRITE setBar NOTIFY barChanged) Q_PROPERTY(Foo foo READ getFoo WRITE setFoo NOTIFY fooChanged) public: const Bar getBar() const { return _bar; } void setBar(const Bar& bar) { _bar = bar; emit barChanged(_bar); } const Foo getFoo() const { return _foo; } void setFoo(const Foo& foo) { _foo = foo; emit fooChanged(_foo); } private: Bar _bar; Foo _foo; signals: void barChanged(const Bar&); void fooChanged(const Foo&); };
Foo is defined in foo, while Bar is defined in Bar, they both look exactly the same:
struct Foo { Q_GADGET Q_PROPERTY(double x MEMBER x) Q_PROPERTY(double y MEMBER y) public: double x, y; };
Given the following setup in main.cpp:
QQmlApplicationEngine engine; engine.rootContext()->setContextProperty("obj", new FooObjectWithBarProperty());
I added a Text item into my Main.qml that looks like this:
Text { anchors.centerIn: parent text: obj.foo.x }
This correctly displays 0. If I change the binding too obj.bar.x the app crashes:
Stacktrace here suggest the metatype for Bar is not correctly registered, but I don't know how to correctly register it. I've tried qRegisterMetaType<Bar>, Q_DECLARE_METATYPE(Bar) in both foo or bar, I experimented a bit with QML_FOREIGN, but the docs are very sparse on it. All to no avail.
What is the necessary change here, and why?
Repo for this is available at https://github.com/marcinjakubowski/foo_bar_qt_issue
wrote on 18 May 2023, 20:11 last edited byAs it turns out, it's because of this line in bar's CMakeLists:
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Core)
It looks for Qt6 and Qt5, and, for some reason, picks Qt5 to generate moc files, as evidenced by the resulting moc_bar.cpp:
/**************************************************************************** ** Meta object code from reading C++ file 'bar.h' ** ** Created by: The Qt Meta Object Compiler version 67 (Qt 5.15.9)
Removing Qt5 from that find_package call and rebuilding the project makes it all work fine.
1/2