qt5 cmake在hello world上未定义引用vtable时失败,以inc & src作为subdirs

时间:2022-01-28 15:39:26

Update 2

After messing around a bit (and some editing of the generated Makefiles), it looks like what is happening is that moc is not properly processing MainWindow.h (included by main.cpp and MainWindow.cpp unless it is in the same folder as the source files which included it.

在对生成的makefile进行了一些编辑之后,看起来moc并没有正确地处理主窗口。h(主要包括。cpp和主窗口。cpp,除非它与包含它的源文件在同一个文件夹中。

Moc runs on MainWindow.cpp, doesn't process the include and thus doesn't see the Q_OBJECT macro so proceeds to produce an empty output file. I'm not sure whether moc usually processes includes or if it just scans the directory, but either way, headers that need mocing but are in other directories are not being processed!

商务部运行在主窗口。cpp不处理include,因此不会看到Q_OBJECT宏,因此会生成一个空输出文件。我不确定moc是否通常包含或只是扫描目录,但是无论哪种方式,需要移动但在其他目录中的头都不会被处理!

Update

The problem appears to be related to the output produced by moc. In the first case (the one that compiles), hello-world_automoc.cpp and moc_MainWindow.cpp are generated. hello-world_automoc.cpp looks like

这个问题似乎与moc生产的产品有关。在第一种情况(编译的那个)中,hello- world_automobile。cpp和moc_MainWindow。cpp生成。hello-world_automoc。cpp的样子

/* This file is autogenerated, do not edit*/
#include "moc_MainWindow.cpp"

In the second case, a hello-world_automoc.cpp is produced that looks like

在第二个例子中,是一个地狱-世界-汽车。cpp是生产出来的。

/* This file is autogenerated, do not edit*/
enum some_compilers { need_more_than_nothing };

and there is no moc_MainWindow.cpp at all. If I manually call moc in from cmake instead of using automoc in the broken case, I do get moc_MainWindow.cpp but it is empty.

没有moc_MainWindow。cpp。如果我从cmake手动调用moc,而不是在坏的情况下使用汽车,我会得到moc_MainWindow。但它是空的。

Original Status

Firstly, no, I haven't forgotten to set(CMAKE_AUTOMOC ON). Also note that MainWindow's destructor is declared and implemented.

首先,我没有忘记设置(cmake_automotive ON)。还要注意,主窗口的析构函数是被声明和实现的。

When my directory structure looks like:

当我的目录结构如下:

CMakeLists.txt
|__ main.cpp
|__ MainWindow.cpp
|__ MainWindow.h
|__ MainWindow.ui

compiling works just fine.

编译就可以了。

However, when it looks like:

然而,当它看起来像:

helloworld/
|__ CMakeLists.txt
|__ src/
|   |__ CMakeLists.txt
|   |__ main.cpp
|   |__ MainWindow.cpp
|
|__ inc/
|   |__ MainWindow.h
|
|__ gui/
    |__ MainWindow.ui

I get linking errors:

我得到连接错误:

Linking CXX executable hello-world
CMakeFiles/hello-world.dir/MainWindow.cpp.o: In function `MainWindow::MainWindow()':
MainWindow.cpp:(.text+0x3b): undefined reference to `vtable for MainWindow'
MainWindow.cpp:(.text+0x4d): undefined reference to `vtable for MainWindow'
CMakeFiles/hello-world.dir/MainWindow.cpp.o: In function `MainWindow::~MainWindow()':
MainWindow.cpp:(.text+0xaf): undefined reference to `vtable for MainWindow'
MainWindow.cpp:(.text+0xc1): undefined reference to `vtable for MainWindow'
collect2: error: ld returned 1 exit status
make[2]: *** [src/hello-world] Error 1
make[1]: *** [src/CMakeFiles/hello-world.dir/all] Error 2

I'd really like to have my sources and headers in proper subdirectories, but I'm not quite sure how to fix this.

我真的很想把源文件和头文件放在适当的子目录中,但是我不太确定如何修复它。

This is actually the simplest identifiable case of an error from a much larger project, so I'm really not all that keen to flatten the project directories just because I'm adding a Qt GUI to it.

这实际上是一个更大的项目中出现错误的最简单的可识别案例,因此我并不是那么热衷于简化项目目录,仅仅因为我在其中添加了Qt GUI。

2 个解决方案

#1


10  

As noted, moc is not processing MainWindow.h in your example. One way to force this to happen is to call qt_wrap_cpp() on it directly (instead of on MainWindow.cpp) and then include the resulting file in the call to add_executable().

如前所述,moc并不处理主窗口。在你的例子。强制执行此操作的一种方法是直接在其上调用qt_wrap_cpp()(而不是在MainWindow.cpp上),然后将结果文件包含到对add_execution()的调用中。

Your top level CMakeLists.txt might look like:

你的*CMakeLists。txt的样子:

cmake_minimum_required(VERSION 2.8.9)

#set(CMAKE_AUTOMOC ON)

set(CMAKE_PREFIX_PATH "/opt/Qt/5.1.1/gcc_64")
set(CMAKE_INCLUDE_CURRENT_DIR ON)

project(hello-world)

find_package(Qt5Widgets REQUIRED)

set(HW_HEADER_DIR ${CMAKE_CURRENT_SOURCE_DIR}/inc)
set(HW_GUI_DIR ${CMAKE_CURRENT_SOURCE_DIR}/gui)

include_directories(${HW_HEADER_DIR})

subdirs(src)

and your src level one like:

而你的src一级像:

qt5_wrap_cpp(hello-world_SRC ${HW_HEADER_DIR}/MainWindow.h)
qt5_wrap_ui(hello-world_UI ${HW_GUI_DIR}/MainWindow.ui)

add_executable(hello-world MainWindow.cpp main.cpp
               ${hello-world_UI} ${hello-world_SRC})
qt5_use_modules(hello-world Widgets)

Addendum:

附录:

  • This works in your example with and without AUTOMOC enabled. I don't know for sure if having it on will cause issues in the future. If you don't enable it you'll have to manually moc any other stuff... although it might all behave like MainWindow in which case you'll be manually mocing the headers regardless.
  • 在您的示例中,无论是否启用了汽车,这都是可行的。我不知道在未来是否会引起问题。如果您不启用它,您将不得不手动moc任何其他东西……尽管它可能都像主窗口一样运行,在这种情况下,无论如何您都要手动地移动头部。
  • You don't have to set the directory variables in the top level CMakeLists.txt but I find it cleaner than doing qt5_wrap_cpp(hello-world_SRC ../inc/MainWindow.h)
  • 您不必在顶层CMakeLists中设置目录变量。txt但我发现它比qt5_wrap_cpp(hello-world_SRC ../inc/MainWindow.h)更干净。
  • There might be a better way of doing this.
  • 也许有更好的办法。
  • For anybody else with similar issues, so far this solution has held up in the larger project that I initially encountered this in. I will update accordingly if it fails.
  • 对于任何有类似问题的人来说,到目前为止,这个解决方案在我最初遇到的更大的项目中起了作用。如果失败,我将相应地更新。

#2


2  

Well, maybe automoc does not work for you, I would guess it's because CMake does not find the corresponding files. Check the documentation here: http://www.cmake.org/cmake/help/v2.8.12/cmake.html#prop_tgt:AUTOMOC

好吧,也许汽车公司不适合你,我猜是因为CMake没有找到相应的文件。在这里查看文档:http://www.cmake.org/cmake/help/v2.8.12/cmake.html#prop_tgt: automotive

In this case, you can always call the moc command manually for them in your CMakeLists.txt:

在这种情况下,您可以在CMakeLists.txt中手动调用moc命令:

qt5_wrap_cpp(moc_sources src/MainWindow.cpp)
qt5_wrap_ui(uic_sources src/MainWindow.cpp)

list(APPEND library_sources ${moc_sources} ${uic_sources})

Note: you have to make sure you use the list command correctly yourself. This code example is from my project where I use a specific list of sources (library_sources).

注意:您必须确保自己正确地使用列表命令。这个代码示例来自我的项目,我使用一个特定的源列表(library_sources)。

It is just a guess but you should try without the automagic first to rule out one possible source of error.

这只是一个猜测,但是您应该尝试不使用自动机首先排除一个可能的错误来源。

Also make sure that you fully deleted the CMake cache after changing your directory structure.

还要确保在更改目录结构后完全删除了CMake缓存。

#1


10  

As noted, moc is not processing MainWindow.h in your example. One way to force this to happen is to call qt_wrap_cpp() on it directly (instead of on MainWindow.cpp) and then include the resulting file in the call to add_executable().

如前所述,moc并不处理主窗口。在你的例子。强制执行此操作的一种方法是直接在其上调用qt_wrap_cpp()(而不是在MainWindow.cpp上),然后将结果文件包含到对add_execution()的调用中。

Your top level CMakeLists.txt might look like:

你的*CMakeLists。txt的样子:

cmake_minimum_required(VERSION 2.8.9)

#set(CMAKE_AUTOMOC ON)

set(CMAKE_PREFIX_PATH "/opt/Qt/5.1.1/gcc_64")
set(CMAKE_INCLUDE_CURRENT_DIR ON)

project(hello-world)

find_package(Qt5Widgets REQUIRED)

set(HW_HEADER_DIR ${CMAKE_CURRENT_SOURCE_DIR}/inc)
set(HW_GUI_DIR ${CMAKE_CURRENT_SOURCE_DIR}/gui)

include_directories(${HW_HEADER_DIR})

subdirs(src)

and your src level one like:

而你的src一级像:

qt5_wrap_cpp(hello-world_SRC ${HW_HEADER_DIR}/MainWindow.h)
qt5_wrap_ui(hello-world_UI ${HW_GUI_DIR}/MainWindow.ui)

add_executable(hello-world MainWindow.cpp main.cpp
               ${hello-world_UI} ${hello-world_SRC})
qt5_use_modules(hello-world Widgets)

Addendum:

附录:

  • This works in your example with and without AUTOMOC enabled. I don't know for sure if having it on will cause issues in the future. If you don't enable it you'll have to manually moc any other stuff... although it might all behave like MainWindow in which case you'll be manually mocing the headers regardless.
  • 在您的示例中,无论是否启用了汽车,这都是可行的。我不知道在未来是否会引起问题。如果您不启用它,您将不得不手动moc任何其他东西……尽管它可能都像主窗口一样运行,在这种情况下,无论如何您都要手动地移动头部。
  • You don't have to set the directory variables in the top level CMakeLists.txt but I find it cleaner than doing qt5_wrap_cpp(hello-world_SRC ../inc/MainWindow.h)
  • 您不必在顶层CMakeLists中设置目录变量。txt但我发现它比qt5_wrap_cpp(hello-world_SRC ../inc/MainWindow.h)更干净。
  • There might be a better way of doing this.
  • 也许有更好的办法。
  • For anybody else with similar issues, so far this solution has held up in the larger project that I initially encountered this in. I will update accordingly if it fails.
  • 对于任何有类似问题的人来说,到目前为止,这个解决方案在我最初遇到的更大的项目中起了作用。如果失败,我将相应地更新。

#2


2  

Well, maybe automoc does not work for you, I would guess it's because CMake does not find the corresponding files. Check the documentation here: http://www.cmake.org/cmake/help/v2.8.12/cmake.html#prop_tgt:AUTOMOC

好吧,也许汽车公司不适合你,我猜是因为CMake没有找到相应的文件。在这里查看文档:http://www.cmake.org/cmake/help/v2.8.12/cmake.html#prop_tgt: automotive

In this case, you can always call the moc command manually for them in your CMakeLists.txt:

在这种情况下,您可以在CMakeLists.txt中手动调用moc命令:

qt5_wrap_cpp(moc_sources src/MainWindow.cpp)
qt5_wrap_ui(uic_sources src/MainWindow.cpp)

list(APPEND library_sources ${moc_sources} ${uic_sources})

Note: you have to make sure you use the list command correctly yourself. This code example is from my project where I use a specific list of sources (library_sources).

注意:您必须确保自己正确地使用列表命令。这个代码示例来自我的项目,我使用一个特定的源列表(library_sources)。

It is just a guess but you should try without the automagic first to rule out one possible source of error.

这只是一个猜测,但是您应该尝试不使用自动机首先排除一个可能的错误来源。

Also make sure that you fully deleted the CMake cache after changing your directory structure.

还要确保在更改目录结构后完全删除了CMake缓存。