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.