1

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?)

Krupip
  • 4,404
  • 2
  • 32
  • 54
  • 2
    I'm pretty sure this is just a Qt Moc thing, where it doesn't properly understand macros. You might be able to run the preprocessor over the code before handing it over to moc, though – Justin Jul 19 '18 at 21:14
  • 1
    It's really hard to decipher that mess, but will there be any unimplemented virtual functions? That's typically the reason behind that error. – Some programmer dude Jul 19 '18 at 21:14
  • @Someprogrammerdude There's only one class, and it only inherits from QObject, unless you need something other than Q_OBJECT to inherit from QObject, then I think it should be fine, but I could be wrong. Again however, using the second class definition I gave everything works perfectly, it appears only when QT comes in-contact with boost pp do things get like this, which would hint that I'm doing something wrong with the `TUPLE_ADD_Q_PROPERTY` macro. – Krupip Jul 19 '18 at 21:29
  • @Justin How would I do something like that? – Krupip Jul 19 '18 at 21:31
  • 2
    So you had a problem, and you said "I know, I will use the preprocessor!". Now you have two problems... – n. m. could be an AI Jul 19 '18 at 21:32
  • @snb Thinking about it more, I don't think it's really possible. You'd need the preprocessor to stop whenever it expanded out to a qt macro, so basically you'd have to write your own preprocessor. These are the options I can think of: generate source code as a build step in cmake, use another implementation of moc which supports this (you may even need to implement it yourself), just use the standard moc and don't do this. – Justin Jul 19 '18 at 21:38
  • @Justin I'm just confused as to what Boost is doing which prevents QT from doing things if all it is doing is delegating to a macro that works on its own with QT... Supposedly this shouldn't be a problem according to troll tech. – Krupip Jul 20 '18 at 03:25
  • 1
    @snb Basically, moc isn't a C++ compiler. It doesn't expand the macros. Thus, it doesn't see the qt macros in the class – Justin Jul 20 '18 at 03:31
  • @Justin but it does expand macros in qt5, and indeed this can be seen with the second class working. – Krupip Jul 20 '18 at 12:37
  • No moc doesn't expand macros. The documentation says so. Qt macros act as moc keywods. The C++ compiler runs on moc output and expand these macros. – n. m. could be an AI Jul 21 '18 at 09:47
  • @n.m. So I'm confused then, why does `ADD_Q_PROPERTY(double, testvalue)` work perfectly fine? – Krupip Jul 21 '18 at 15:05
  • 1
    Sorry my bad, moc didn't expand macros before qt5, now it does. My experience is a bit outdated. But it looks like it is using its own custom preprocessor which gets confused by Boost. Verify this by calling `moc -E testheader.h`. You will see that some Boost macros are still in the output, unexpanded. I think this may be an issue in moc. – n. m. could be an AI Jul 21 '18 at 19:35
  • From my experience, moc sometimes has problem even if you only include some boost headers. I wouldn't count on it parsing such macros. – Jaa-c Jul 22 '18 at 17:19
  • @n.m. Thanks for checking that out, I'd still like to know how to fix this eventually, but for now I've taken your advice and for my **real project** I've stuck to not using macros for this. – Krupip Jul 23 '18 at 13:36

0 Answers0