Qt 5 cmake fails with undefined reference to vtable on hello world with inc & src as subdirs

2 min read 07-10-2024
Qt 5 cmake fails with undefined reference to vtable on hello world with inc & src as subdirs


Qt 5 CMake: Conquering the "Undefined Reference to Vtable" in a Hello World with Subdirectories

Let's dive into a common CMake issue encountered when building a simple "Hello World" Qt application with separate source (src) and include (inc) directories. This scenario often results in a frustrating error: "undefined reference to vtable."

The Setup and the Frustrating Error

Imagine you're embarking on a Qt project and, for organization's sake, you've structured your code with separate inc and src directories. Your project might look like this:

myproject/
├── src/
│   └── main.cpp
└── inc/
    └── mywidget.h

Your main.cpp file includes mywidget.h, which defines a simple MyWidget class:

// src/main.cpp
#include <QApplication>
#include "mywidget.h" // Include from the 'inc' directory

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    MyWidget widget;
    widget.show();
    return app.exec();
}

And mywidget.h looks like this:

// inc/mywidget.h
#ifndef MYWIDGET_H
#define MYWIDGET_H

#include <QWidget>

class MyWidget : public QWidget
{
    Q_OBJECT
public:
    explicit MyWidget(QWidget *parent = nullptr);
};

#endif // MYWIDGET_H

Now, you create a CMakeLists.txt to build your project:

cmake_minimum_required(VERSION 3.10)
project(myproject)

set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTORCC ON)

find_package(Qt5Widgets REQUIRED)

include_directories(inc)

add_executable(myproject src/main.cpp)
target_link_libraries(myproject Qt5::Widgets)

After running cmake and make, you get the dreaded error:

undefined reference to `vtable for MyWidget'

What's going on? Why is the linker complaining about a missing vtable?

Understanding the "Undefined Reference to Vtable"

The error "undefined reference to vtable for MyWidget" signifies that the linker cannot find the necessary information about the MyWidget class's virtual functions. This information, known as the "vtable," is crucial for dynamic polymorphism in C++ – the ability to call virtual functions through pointers or references.

The Missing Link: Properly Including Qt's Header Files

The culprit in this scenario is the way we're handling Qt header files. While we've included our own mywidget.h, we haven't explicitly included Qt's header files responsible for providing the necessary vtable definitions.

Solution:

In the CMakeLists.txt, we need to include the Qt header files explicitly, ensuring they're accessible to the compiler:

cmake_minimum_required(VERSION 3.10)
project(myproject)

set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTORCC ON)

find_package(Qt5Widgets REQUIRED)

# **Explicitly include Qt header files**
include_directories(${Qt5Widgets_INCLUDE_DIRS})

include_directories(inc)

add_executable(myproject src/main.cpp)
target_link_libraries(myproject Qt5::Widgets)

By adding include_directories(${Qt5Widgets_INCLUDE_DIRS}), we're telling CMake to include the Qt header files that are located in the Qt5Widgets_INCLUDE_DIRS variable, which is automatically set by the find_package command.

The Importance of the Q_OBJECT Macro

The Q_OBJECT macro is a cornerstone of Qt's meta-object system. It enables features like signals and slots, dynamic property access, and the mechanism behind Qt's elegant event handling. When you include the Q_OBJECT macro in your classes, Qt generates a vtable that holds all the necessary information about your class's virtual functions.

In Summary

The "undefined reference to vtable" error when using Qt with CMake often stems from missing Qt header files. By ensuring you correctly include these files within your CMake project, you'll avoid the error and allow the linker to successfully resolve the necessary information for your Qt-based classes. Remember, always include the Q_OBJECT macro when working with classes that leverage Qt's meta-object system. This simple step is crucial for enabling the dynamic capabilities that make Qt such a powerful framework.