EDIT: I have heavily edited this post to strip the project down to its essentials. I have also added a Github repository , including the files which are not referenced in this post.
I have a Qt Creator project (qmake, Qt 5.2.0, Creator 3.0.0) that uses the subdirs
template. There are three subprojects:
TEMPLATE = lib
and CONFIG += staticlib
. TEMPLATE = lib
and CONFIG += staticlib
and uses the Field
library. I'm building this application on both Windows 8.1 (MSVC2012) and Linux (gcc 4.8.1). It works without issue on Windows , but the Linux build behaves strangely.
The errors I get look like this:
undefined reference to 'vtable for Stadium::Engine'
I've rewritten this project to a set of bare files that displays the error. You can find it on Github here: Football . Feel free to clone it and see all the errors for yourself. The 661441c
commit solves the problem, and the 09836f9
commit contains the errors.
The Stadium Engine.h file is an abstract class. It looks like this:
#ifndef STADIUM_ENGINE_H
#define STADIUM_ENGINE_H
#include <QObject>
namespace Stadium {
class Engine : public QObject
{
Q_OBJECT
public slots:
virtual void executeCommand() = 0;
};
} // namespace Stadium
#endif // STADIUM_ENGINE_H
Here is the Football Engine.h file, which inherits from the Stadium Engine.h file above:
#ifndef FOOTBALL_ENGINE_H
#define FOOTBALL_ENGINE_H
#include <QObject>
#include "../Stadium/Engine.h"
namespace Football
{
class Engine : public Stadium::Engine
{
Q_OBJECT
public:
Engine();
~Engine() {}
public slots:
void executeCommand();
};
} // namespace Football
#endif // FOOTBALL_ENGINE_H
And the Football Engine.cpp file:
#include "Engine.h"
#include <QDebug>
Football::Engine::Engine()
{
qDebug() << "[Football::Engine] Created.";
}
void Football::Engine::executeCommand()
{
qDebug() << "[Football::Engine] The command was executed.";
}
If I move the constructor definition from the cpp to the header file, it builds without error.
Below is the the Server.pro file. It's indicative of all my other pro files, in that the static linking descriptions (auto-generated by Qt Creator) look the same.
QT += core
QT -= gui
TARGET = Server
CONFIG += console
CONFIG -= app_bundle
TEMPLATE = app
SOURCES += main.cpp
win32:CONFIG(release, debug|release): LIBS += -L$$OUT_PWD/../Stadium/release/ -lStadium
else:win32:CONFIG(debug, debug|release): LIBS += -L$$OUT_PWD/../Stadium/debug/ -lStadium
else:unix: LIBS += -L$$OUT_PWD/../Stadium/ -lStadium
INCLUDEPATH += $$PWD/../Stadium
DEPENDPATH += $$PWD/../Stadium
win32-g++:CONFIG(release, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../Stadium/release/libStadium.a
else:win32-g++:CONFIG(debug, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../Stadium/debug/libStadium.a
else:win32:!win32-g++:CONFIG(release, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../Stadium/release/Stadium.lib
else:win32:!win32-g++:CONFIG(debug, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../Stadium/debug/Stadium.lib
else:unix: PRE_TARGETDEPS += $$OUT_PWD/../Stadium/libStadium.a
win32:CONFIG(release, debug|release): LIBS += -L$$OUT_PWD/../Football/release/ -lFootball
else:win32:CONFIG(debug, debug|release): LIBS += -L$$OUT_PWD/../Football/debug/ -lFootball
else:unix: LIBS += -L$$OUT_PWD/../Football/ -lFootball
INCLUDEPATH += $$PWD/../Football
DEPENDPATH += $$PWD/../Football
win32-g++:CONFIG(release, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../Football/release/libFootball.a
else:win32-g++:CONFIG(debug, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../Football/debug/libFootball.a
else:win32:!win32-g++:CONFIG(release, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../Football/release/Football.lib
else:win32:!win32-g++:CONFIG(debug, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../Football/debug/Football.lib
else:unix: PRE_TARGETDEPS += $$OUT_PWD/../Football/libFootball.a
I've tried cleaning, re-running qmake, removing the build directory, and rebuilding. The only way to get this project build in Linux is to remove the CONFIG += staticlib
line in the .pro file of the Stadium library (and the corresponding else:unix: PRE_TARGETDEPS += $$OUT_PWD/../stadium/libstadium.a
line in the Game.pro too, of course). This builds the project successfully and runs without issue. But I just don't understand why. I also don't understand why it matters where the constructor definition is defined.
Any ideas?
The answer is disappointingly simple: the libraries were linked in the wrong order.
I looked at the command that invoked the linker (right above the linker error):
g++ [...] -lStadium [...] -lFootball
I also looked into the code: The Football subproject refers to the Stadium subproject, so the libraries are in the wrong order, see for example the accepted answer of GCC C++ Linker errors: Undefined reference to 'vtable for XXX', Undefined reference to 'ClassName::ClassName()' for explanation.
Indeed, if I swap the two libraries in the Server.pro file (derived from commit 09836f9
, irrelevant win32 specific details deleted for brevity):
[...]
SOURCES += main.cpp
LIBS += -L$$OUT_PWD/../Football/ -lFootball
INCLUDEPATH += $$PWD/../Football
DEPENDPATH += $$PWD/../Football
PRE_TARGETDEPS += $$OUT_PWD/../Football/libFootball.a
LIBS += -L$$OUT_PWD/../Stadium/ -lStadium
INCLUDEPATH += $$PWD/../Stadium
DEPENDPATH += $$PWD/../Stadium
PRE_TARGETDEPS += $$OUT_PWD/../Stadium/libStadium.a
Now the command line looks like:
g++ [...] -lFootball [...] -lStadium
It compiles and runs just fine on my Linux machine.
You have inlined the virtual destructor.
This can sometimes lead to problems.
Try to implement the destructor in a .cpp file. I also would remove the = 0
from the declaration of the destructor.
You can take a look at this other questions:
I tried compiling your STADIUM_ENGINE code as an static lib and then linking from an app, and I've just got the error when NO virtual pure destructor is defined (as expected). If you don't have a virtual destructor defined you won't be able to instantiate any derived class.
Anyhow, your class inherits from QObject, which already declares and implements a non pure virtual destructor. Is it useful a pure virtual destructor?
Okay, I figured out the solution. I had three distinct issues that when changed, cleared the vtable errors. Unfortunatly, I don't have any idea why the second two changes are necessary.
The class which inherited the Stadium::Engine class above, had an extra Q_OBJECT inside. When I removed the second Q_OBJECT in the derived class, one of the vtable errors went away.
I do not understand why, but when the derived class defined the constructor in the CPP file, it gives vtable
errors. When defined in the header (inside the class description), it works fine. There is nothing in the constructor ( DerivedEngine() {}
). I can't figure out why this would matter.
It is required that both the constructor and destructor are defined in the pure abstract class. I do not understand why. I added these lines in the header, outside the class definition:
inline Stadium::Engine::Engine() {}
inline Stadium::Engine::~Engine() {}
This still really bothers me. Why are these changes necessary? Why does this only happen with gcc/Linux? Surely this is a Qt bug, right?
I do not see where you run the moc compiler. moc creates a file that resolves additional things for your QObject derived classes.
If moc is running, your namespace may be the problem. moc is not known to work well with namespaces. I like namespaces, but Qt is been there before people started using them everywhere.
Removing the Q_OBJECT and derivation from QObject is another solution, if that's not absolutely required for that class.
Another possibility is that your makefiles are out of date. In that case you'd want to force a run of qmake to make sure they are refreshed properly.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.