简体   繁体   中英

Building Qt app with CONFIG += staticlib causes “undefined reference to vtable” errors

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:

  1. Stadium - library that is configured as TEMPLATE = lib and CONFIG += staticlib .
  2. Football - library that is configured as TEMPLATE = lib and CONFIG += staticlib and uses the Field library.
  3. Server - a QML application that uses both Stadium and Football libraries.

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.

1. Q_OBJECT in the derived class

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.

2. The Engine constructor

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.

3. Define constructor and virtual destructor

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM