Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 385
jmbeck Avatar asked Feb 02 '14 15:02

jmbeck


1 Answers

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.

like image 149
Ali Avatar answered Nov 11 '22 21:11

Ali