I'm trying to generate QT5 macros from Boost PP Tuples and sequences. This is my code (note I'm using C++14, and I'm using QT5.10.1):
//CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
project(helloqt)
# AUTO MOC GEN DEPENDENCIES
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTOUIC ON)
#find_package(Qt5Widgets REQUIRED)
find_package(Qt5 COMPONENTS Core Widgets gui OpenGL Network)
include_directories(boost/)
#Pull needed header files generated by QT first
set(QT_GENERATED qtGenerated)
add_custom_target(${QT_GENERATED})
#Get all header files in the directory
file(GLOB QT_GEN_HEADERS ./cmake-build-debug/qtclionproject_autogen/include/*.h)
#Copy them to the project dir
foreach (QT_GEN_HEADERS ${QT_GEN_HEADERS})
message("QT Generated header ${QT_GEN_HEADERS}")
add_custom_command(TARGET ${QT_GENERATED} PRE_BUILD COMMAND
${CMAKE_COMMAND} -E copy_if_different
${QT_GEN_HEADERS} ${CMAKE_SOURCE_DIR})
endforeach ()
add_executable(${PROJECT_NAME} main.cpp testheader.h)
target_link_libraries(helloqt Qt5::Widgets)
//testheader.h
#ifndef HELLOQT_TESTHEADER_H
#define HELLOQT_TESTHEADER_H
#include <boost/preprocessor/seq/for_each_product.hpp>
#include <boost/preprocessor/seq/for_each.hpp>
#include <boost/preprocessor/seq/to_tuple.hpp>
#include <boost/preprocessor/tuple/to_seq.hpp>
#include <boost/preprocessor/variadic/to_seq.hpp>
#include <boost/preprocessor/seq/transform.hpp>
#include <boost/preprocessor/seq/enum.hpp>
#include <QObject>
////// start property changed event creation required macros
#define CAT_SIGNATURE_IMPL2(A) \
A ## _changed
#define CAT_SIGNATURE_IMPL(A) \
CAT_SIGNATURE_IMPL2(A)
#define ADD_PROPERTY_SIGNAL_IMPL(TYPE, NAME) \
Q_SIGNALS: \
void CAT_SIGNATURE_IMPL(NAME)(TYPE new_value);
#define ADD_Q_PROPERTY_SIGNAL(TYPE, NAME) \
public: \
ADD_PROPERTY_SIGNAL_IMPL(TYPE, NAME) \
public:
////
#define ADD_Q_PROPERTY_IMPL(TYPE, NAME) \
public: \
Q_PROPERTY(TYPE NAME MEMBER m_##NAME NOTIFY NAME##_changed ); \
\
public:
#define ADD_Q_PROPERTY(TYPE, NAME) \
ADD_Q_PROPERTY_IMPL(TYPE, NAME) \
ADD_Q_PROPERTY_SIGNAL(TYPE, NAME)
#define TUPLE_ADD_Q_PROPERTY(_1, _2, TUPLE_X) \
ADD_Q_PROPERTY(BOOST_PP_TUPLE_ELEM(0, TUPLE_X), \
BOOST_PP_TUPLE_ELEM(1, TUPLE_X) \
)
////Q string list of properties required macros
#define GET_NAME_LIST_FROM_PAIRS(...) \
BOOST_PP_SEQ_ENUM( \
BOOST_PP_SEQ_TRANSFORM( \
UNZIP_NAME, \
0, \
BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__) \
) \
)
#define STRING_NAME_(NAME)\
#NAME
#define STRING_NAME(NAME) \
STRING_NAME_(NAME)
#define UNZIP_NAME(_0, _1, elem) \
STRING_NAME(BOOST_PP_TUPLE_ELEM(2, 1, elem))
#define ADD_ELEMENT_PROPERTIES(...)\
QStringList elementProperties() const { \
return {GET_NAME_LIST_FROM_PAIRS(__VA_ARGS__)}; \
}
////
class TestQObject : public QObject {
Q_OBJECT
double m_testvalue;
public:
TUPLE_ADD_Q_PROPERTY(,,(double, testvalue))
ADD_ELEMENT_PROPERTIES((double, testvalue))
};
#endif //HELLOQT_TESTHEADER_H
//main.cpp
#include "testheader.h"
int main() {
auto x = new TestQObject();
return 0;
}
And here is the error message:
"...\cmake.exe" --build ...\cmake-build-debug --target helloqt -- -j 2
[ 25%] Automatic MOC and UIC for target helloqt
[ 25%] Built target helloqt_autogen
Scanning dependencies of target helloqt
[ 50%] Building CXX object CMakeFiles/helloqt.dir/main.cpp.obj
[ 75%] Building CXX object CMakeFiles/helloqt.dir/helloqt_autogen/mocs_compilation.cpp.obj
[100%] Linking CXX executable helloqt.exe
CMakeFiles\helloqt.dir/objects.a(main.cpp.obj):main.cpp:(.rdata$.refptr._ZTV11TestQObject[.refptr._ZTV11TestQObject]+0x0): undefined reference to `vtable for TestQObject'
collect2.exe: error: ld returned 1 exit status
mingw32-make.exe[3]: *** [CMakeFiles\helloqt.dir\build.make:127: helloqt.exe] Error 1
mingw32-make.exe[2]: *** [CMakeFiles\Makefile2:99: CMakeFiles/helloqt.dir/all] Error 2
mingw32-make.exe[1]: *** [CMakeFiles\Makefile2:111: CMakeFiles/helloqt.dir/rule] Error 2
mingw32-make.exe: *** [Makefile:130: helloqt] Error 2
And In case this is an X Y problem, I was trying to create a minimal verifiable example for a different problem when I happened upon this, basically, I was trying to get the following macro to work (which compiled, but failed at runtime where apparently the property was never added according to QT).
#define ADD_PROPERTIES_WITH_ELEMENTS(...) \
BOOST_PP_SEQ_FOR_EACH(TUPLE_ADD_Q_PROPERTY, 0, BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__)) \
QStringList elementProperties() const override{ \
return {GET_NAME_LIST_FROM_PAIRS(__VA_ARGS__)}; \
}
I went and tried TUPLE_ADD_Q_PROPERTY
just in case to figure out what the issue was, and this is what seems to give the error (but going one level down with adding ADD_Q_PROPERTY
works fine, but doesn't use BOOST_PP macros and QT macros together)
From this answer my guess is that when I do this the MOC isn't updating the associated generated CPP file with my class, I have no clue how to actually force it to do that however if this is the case, my cmake file clearly set automoc on... However, when I look at the associated moc_testheader.cpp
, it is completely empty. There's simply nothing there. If i switch to the correctly compiled version of the class:
class TestQObject : public QObject {
Q_OBJECT
double m_testvalue;
public:
ADD_Q_PROPERTY(double, testvalue)
ADD_ELEMENT_PROPERTIES((double, m_testvalue))
};
Everything works fine, and moc_testheader.cpp
is not blank, but when I switch back to the other version, the compilation fails again, and the entire file turns blank (not sure if this is expected?)