Solved moc cpp generation fails with "No relevant classes found. No output generated."
-
I have a class D11 that inherits from class B1 that inherits QObject. B1 has declared Q_OBJECT, and so does D11. (Well, D11 tries to). But I'm running into the infamous
'undefined reference to vtable'
error in the constructor and destructor of D11.This issue has popped up many times before on these forums, and I have tried all the usual recommendations:
- The failing class is indeed in separate files (D11.h and D11.cpp).
- I have tried Build > Run qmake from QtCreator.
- Deleted the build dir, run qmake from QtCreator, and run build again.
The moc build step to generate moc_D11.cpp results in the following:
D11.h:0: Note: No relevant classes found. No output generated.
and indeed, moc_D11.cpp is an empty file.I have many other files in my project with the exact same hierarchies:
QObject <-- B1 <-- D11
QObject <-- B1 <-- D12
QObject <-- B1 <-- D13
QObject <-- B2 <-- D21None of these have any problems. In particular, D12 and D13 that derive from B1 and are (obviously) fairly similar to D11 are fine and have their moc_D12.cpp and moc_D13.cpp generated just fine.
This is being cross-compiled to an RPi, but not sure that that should matter. The moc command is fairly long, but I have checked it meticulously, switch-by-switch, between the versions that work (D12, D13) vs D11, which doesn't work.
Unfortunately the project is both large as well as proprietary, so I cannot post it - and of course a toy example is not going to show this (and/or would be easy to resolve). Still, if there is anything else I can try, I will welcome suggestions.
-
@vikramg Can you show your D11 class?
-
Not sure if it will help, but here is an obfuscated version. D11.h:
#ifndef D11_H #define D11_H #include "b1.h" class B2; class D11 : public B1 { Q_OBJECT // <-- this is causing "undefined reference to vtable for D11" WHY!?? public: D11(const B2& s); ~D11() override; void processRawData(const QByteArray& etba) override; protected: void initFSMs() override; void setLock(byte prefix) override; void dropLock() override; void updateFsm1(byte prefix) override; void updateFsm2(byte prefix) override; bool updateFsm3(byte dataByte) override; bool extractVals(byte dataByte) override; void signExtend(int& val) override; signals: void signal1(); void signal2(); private: const int CONST1 = 0b1000'0000; const int CONST2 = 0b0100'0000; const int MASK = 0b0010'0000; // About 15-20 int and bool private members here... enum class Fsm1State { ...names here... }; enum class Fsm2State { ...names here... }; enum class Fsm3State { ...names here... }; Fsm1State mFsm1State; Fsm2State mFsm2State; Fsm3State mFsm3State; bool fn1(byte dataByte); bool fn2(byte dataByte); bool fn3(byte dataByte); bool fn4(byte dataByte); }; #endif // D11_H
D11.cpp:
#include "d11.h" #include "B2dir/b2.h" D11::D11(const B2& s) : B1(s) { initFSMs(); } D11::~D11() { qDebug("D11 dtor"); } void D11::initFSMs() { mFsm1State = Fsm1State::NAME1; mFsm2State = Fsm2State::NAME1; } void D11::processRawData(const QByteArray &etba) { for (byte dataByte : etba) { . . . } emit mBase2.b2signal(); } // Other vanilla member function definitions here...
B1.h:
#ifndef B1_H #define B1_H #include <QObject> ...other QIncludes here, like QByteArray etc... class B2; class B1 : public QObject { Q_OBJECT public: explicit B1(const B2& s, QObject *parent = nullptr); virtual ~B1(); using byte = unsigned char; signals: public slots: public: virtual void processRawData(const QByteArray& etba) = 0; protected: // some int/bool members here const B2& mBase2; virtual void initFSMs() = 0; virtual void setLock(byte dataByte) = 0; virtual void dropLock() = 0; virtual void updateFsm1(byte dataByte) = 0; virtual void updateFsm2(byte dataByte) = 0; virtual bool updateFsm3(byte dataByte) = 0; virtual bool extractVals(byte dataByte) = 0; virtual void signExtend(int& val) = 0; }; #endif // B1_H
B1.cpp:
#include "b1.h" #include "B2dir/b2.h" B1::B1(const B2& s, QObject *parent) : QObject(parent), mBase2(s) { qDebug("B1 abstract base class ctor"); } B1::~B1() { qDebug("B1 abstract base class dtor"); }
I'm happy to provide any specific additional information. I can comment out the
Q_OBJECT
in D11 and of course it builds just fine. -
Hi,
Did you re-run qmake after adding the Q_OBJECT macro ?
-
@sgaist yes, see my first post. In particular, I have tried deleting the build directory, rerunning qmake and rerunning the entire build after that.
-
Is there a verbose mode to the moc command that will elaborate on the
No relevant classes found
? -
Quite incredibly, it turned out that the moc error was because of moc parsing not being able to comprehend C++14 digit separators in the constants I had in the private section of my class! Removing the digit separators enabled the moc compilation to go through without errors! So this doesn't work:
private: const int CONST1 = 0b1000'0000;
This works:
private: const int CONST1 = 0b10000000;
I have found C++14 digit separator support to be abysmal within the Qt ecosystem, from Qt Creator to now moc. I will personally just stop using it till Qt 6 at least; hopefully it will improve by then :P
-
Opening a feature request will be a better idea, it will make the issue known to moc's developers.
-
@sgaist How/where can I do that?
-
Hi @vikramg,
Please post a link to the report here too, so others can follow later. Thanks!
-
Remove (no clean) build directory and build project.
-
@SGaist @aha_1980
Bug report here: https://bugreports.qt.io/browse/QTBUG-77421I also found other bugs filed against moc that one would need to watch out for:
- moc does not support raw string literals: https://bugreports.qt.io/browse/QTBUG-62632
- moc does not support (certain?) unicode files: https://bugreports.qt.io/browse/QTBUG-26843
These will also result in the
No relevant classes found
error. -
Thanks for sharing your additional findings !