0

I have a problem using CMake and Qt5. So my project looks like this:

├── CMakeLists.txt
├── inc
│   └── qtbasicapp
│       ├── app
│       │   ├── Application.hpp
│       │   ├── fwd.hpp
│       │   └── IApplication.hpp
│       └── windows
│           ├── fwd.hpp
│           ├── IWindow.hpp
│           ├── main
│           │   └── MainWindow.hpp
│           └── Window.hpp
├── src
│   ├── app
│   │   ├── Application.cpp
│   │   └── CMakeLists.txt
│   ├── CMakeLists.txt
│   ├── main.cpp
│   └── windows
│       ├── CMakeLists.txt
│       ├── main
│       │   ├── CMakeLists.txt
│       │   └── MainWindow.cpp
│       └── Window.cpp

CMakeLists.txt:

cmake_minimum_required(VERSION 3.5)
 
project(BasicProject LANGUAGES CXX)
 
add_subdirectory(dependencies)
 
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
 
find_package(Qt5 COMPONENTS Widgets REQUIRED)
 
set(INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/inc)
set(DEPENDENCY_DIR ${CMAKE_CURRENT_SOURCE_DIR}/dependencies)
set(HEADERS_DIR ${INCLUDE_DIR})
 
add_subdirectory(src)
add_subdirectory(test)

src/CMakeLists.txt:

set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
 
add_subdirectory(app)
add_subdirectory(windows)
 
add_library(mainlib STATIC)
 
target_link_libraries(mainlib PUBLIC
    qtbasicapp_app_lib
    qtbasicapp_windows_lib
    qtbasicapp_windows_main_lib
    )
 
set(SOURCE_FILES
    ./main.cpp
)
 
add_executable(${PROJECT_NAME} ${SOURCE_FILES})
 
target_include_directories(${PROJECT_NAME}
    PUBLIC
    ${HEADERS_DIR}
    ${Qt5Widgets_INCLUDE_DIRS}
    )
 
target_link_libraries(${PROJECT_NAME}
    PUBLIC
    mainlib
    Qt5::Widgets
    )

src/windows/CMakeLists.txt:

set(LIB_NAME qtbasicapp_windows_lib)
 
add_library(${LIB_NAME} OBJECT)
 
target_sources(${LIB_NAME}
    PUBLIC
    ${INCLUDE_DIR}/qtbasicapp/windows/fwd.hpp
    ${INCLUDE_DIR}/qtbasicapp/windows/IWindow.hpp
    ${INCLUDE_DIR}/qtbasicapp/windows/Window.hpp
 
    PRIVATE
    ${CMAKE_CURRENT_SOURCE_DIR}/Window.cpp
    )
 
target_include_directories(${LIB_NAME}
    PUBLIC
    ${HEADERS_DIR}
    )
 
target_compile_options(${LIB_NAME} PUBLIC -Werror -Wall -Wextra)
 
add_subdirectory(main)

src/windows/main/CMakeLists.txt:

set(LIB_NAME qtbasicapp_windows_main_lib)
 
find_package(Qt5 COMPONENTS Widgets REQUIRED)
 
add_library(${LIB_NAME} OBJECT)
 
target_sources(${LIB_NAME}
        PUBLIC
        ${INCLUDE_DIR}/qtbasicapp/windows/main/MainWindow.hpp
 
        PRIVATE
        ${CMAKE_CURRENT_SOURCE_DIR}/MainWindow.cpp
        )
 
target_include_directories(${LIB_NAME}
        PUBLIC
        ${HEADERS_DIR}
        /usr/include/x86_64-linux-gnu/qt5/QtWidgets
        )
 
target_link_libraries(${LIB_NAME}
        PUBLIC
        Qt5::Widgets
        )
 
target_compile_options(${LIB_NAME} PUBLIC -Werror -Wall -Wextra)

In inc/qtbasicapp/windows/main/MainWindow.hpp file I'm including QMainWindow and getting an error:

Error:

/usr/bin/c++  -I/home/konrad/dev/cpp/qt_basic_application/repository/inc -g -Werror -Wall -Wextra -std=gnu++17 -MD -MT src/app/CMakeFiles/qtbasicapp_app_lib.dir/Application.cpp.o -MF src/app/CMakeFiles/qtbasicapp_app_lib.dir/Application.cpp.o.d -o src/app/CMakeFiles/qtbasicapp_app_lib.dir/Application.cpp.o -c /home/konrad/dev/cpp/qt_basic_application/repository/src/app/Application.cpp
In file included from /home/konrad/dev/cpp/qt_basic_application/repository/src/app/Application.cpp:4:
/home/konrad/dev/cpp/qt_basic_application/repository/inc/qtbasicapp/windows/main/MainWindow.hpp:8:10: fatal error: QMainWindow: No such file or directory
    8 | #include <QMainWindow>
      |          ^~~~~~~~~~~~~
compilation terminated.
[2/4] Building CXX object src/windows/main/CMakeFiles/qtbasicapp_windows_main_lib.dir/MainWindow.cpp.o
ninja: build stopped: subcommand failed.

MainWindow.hpp:

#ifndef QTBASICAPP_WINDOWS_MAIN_MAINWINDOW_HPP_
#define QTBASICAPP_WINDOWS_MAIN_MAINWINDOW_HPP_

#include <qtbasicapp/windows/Window.hpp>

#include <qtbasicapp/windows/fwd.hpp>

#include <QMainWindow> // ERROR

namespace qtbasicapp
{
namespace windows
{
namespace main
{

class MainWindow : public Window, public QMainWindow
{
    Q_OBJECT

public:
    static IWindowPtr create(); //IWindowPtr from fwd.hpp

    void open() override;
    void close() override;

private:
    MainWindow();

    QWidget* mainWidget_;
};

} // namespace main
} // namespace windows
} // namespace qtbasicapp

#endif //QTBASICAPP_WINDOWS_MAIN_MAINWINDOW_HPP_

Application.hpp

#ifndef QTBASICAPP_APP_APPLICATION_HPP_
#define QTBASICAPP_APP_APPLICATION_HPP_

#include <qtbasicapp/app/IApplication.hpp>
#include <qtbasicapp/app/fwd.hpp>

#include <qtbasicapp/windows/fwd.hpp>

namespace qtbasicapp
{
namespace app
{
enum class ApplicationStatus
{
    NoError = 0,
    Warning = -1,
    Fatal = -2
};

class Application : public IApplication
{
public:
    static IApplicationPtr create();
    ~Application() override;

    void start() const override;
    void exit() const override;

private:
    Application();

    ApplicationStatus status_;
    windows::IWindowPtr window_;
};

}  // namespace app
}  // namespace qtbasicapp

#endif  // QTBASICAPP_APP_APPLICATION_HPP_

Application.cpp

#include <iostream>
#include <qtbasicapp/app/Application.hpp>

#include <qtbasicapp/windows/main/MainWindow.hpp> // ERROR

namespace qtbasicapp
{
namespace app
{

IApplicationPtr Application::create()
{
    return std::unique_ptr<Application>(new Application);
}

Application::Application()
: status_(ApplicationStatus::NoError)
, window_(qtbasicapp::windows::main::MainWindow::create())
{}

Application::~Application()
{
    exit();
}

void Application::start() const
{
    std::cout << "Starting application" << std::endl;


}

void Application::exit() const
{
    std::cout << "Closing application" << std::endl;
}

}  // namespace app
}  // namespace qtbasicapp

Edit: Adding includes and linking library in windows/CMakeLists.txt gives the same result.

If I add

target_link_libraries(${LIB_NAME}
    PUBLIC
    Qt5::Widgets
    )

to app/CMakeLists.txt I get: /home/konrad/dev/cpp/qt_basic_application/repository/src/windows/main/MainWindow.cpp:13: undefined reference to `vtable for qtbasicapp::windows::main::MainWindow'

Kabuuz
  • 1
  • 1
  • 2
    Can you edit the post with the .cpp file that gives rise to the error? We don't know which of your three CMake projects import the header file in question, but I can see at least one does not link to Qt5::Widgets. – Botje Nov 02 '21 at 15:23
  • Don't try to get the include directories right yourself. Your qt5 installation should come with cmake configuration scripts that describe everything you need to compile a qt application, including include directories. Assuming you link the target with `Qt5::Widgets`, the necessary include dirs should be available to the linking target. Note that the include should be `#include ` and not `#include `; If the former isn't working, something went wrong during `find_package(Qt5 ...)` or you forgot to link a `Qt5::Widgets` to one target where it's needed. – fabian Nov 02 '21 at 18:07
  • @Botje, hi, I edited my question. – Kabuuz Nov 02 '21 at 18:25
  • Please show the full cmake error output. We need to know which cpp file triggers the error, because *that* project is badly configured. – Botje Nov 02 '21 at 19:08
  • I hope that now I posted all necessary information – Kabuuz Nov 02 '21 at 19:41
  • You're not showing us everything. The error comes from Application.cpp which is compiled as part of the `qtbasicapp_app_lib` target. Presumably that's in `src/app/CMakeLists.txt`? – Botje Nov 03 '21 at 08:00
  • you forgot to add `target_link_libraries(${LIB_NAME} PUBLIC Qt5::Widgets )` in a file src/windows/CMakeLists.txt: – arutar Nov 03 '21 at 08:15
  • hi, @arutar I tried but error still exists – Kabuuz Nov 03 '21 at 15:37
  • @Kabuuz, it's been a long time, but did you find any solution on this? – willy Jul 21 '22 at 12:10
  • 1
    @michalis hi, if I remember correctly the problem was with MainWindow which inherits from Window and QMainWindow. If some class inherits from Qt classes or use qt macro (I don't remember which one) it means it has to have it's own vtable. My class didn't have any virtual function so vtable didn't exist. Creating virtual destructor would solve this problem. – Kabuuz Jul 22 '22 at 15:10

0 Answers0