C++ / CMake / Conan.io - 包含标题时'Undefined Reference to Class::Method'



我在编译项目时遇到了一点麻烦,使用 Conan.io 和 CMake。

我正在构建一个基于OpenGL的小型项目。我使用MVC架构。我希望CMake产生两种不同的.exe

  • main.exe是一个具有简单OpenGL上下文的小GLFW窗口。它构建并运行良好,使用conan.io来管理所使用的库。
  • pentest.exe是一个简单的测试可执行文件,我想用它来测试我的模型中的一些基本功能。当我调用make命令时,不会编译这个。

这是我简化的项目架构

├───build
│   ├───.cmake
│   │
│   ├───bin
│   │   └─── // .exe files are here
│   │
│   ├───CMakeFiles
│   │   └─── // Cmake files are here
│   │
│   └─── // ...
│
├───include
│   ├───GL
│   │   └─── GLU.h
│   │
│   └───model
│       ├───Block.hpp
│       └───GameGrid.hpp
│   
├───src
│   ├───model
│   │   ├───Block.hpp
│   │   └───GameGrid.hpp
│   │
│   ├───main.cpp
│   └───pentest.cpp
│
├───CMakeLists.txt
└───conanfile.txt

请注意:

  • pentest.cpp不依赖于任何外部库。
  • 尽管我的GameGrid类是一个模板类,但我使标头在末尾包含实现文件(在此 StackOverflow 问题之后)。
  • 我对CMake非常非常糟糕。
  • CMake 命令运行良好,当make命令调用链接器进行pentest.exe时发生错误。

这是我CMakeLists.txt

cmake_minimum_required(VERSION 2.8.12)
project(TheEndless)

add_definitions("-std=c++17")

include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
conan_basic_setup()

include_directories(
${PROJECT_SOURCE_DIR}/include
${PROJECT_SOURCE_DIR}/src
${PROJECT_SOURCE_DIR}/src/model
)

link_directories(${CMAKE_SOURCE_DIR}/lib)

add_executable(main src/main.cpp)
add_executable(pentest src/pentest.cpp)
target_link_libraries(main ${CONAN_LIBS})
target_link_libraries(pentest ${CONAN_LIBS})

这是我pentest.cpp

#include <iostream>
#include <string>
#include "model/Block.hpp"
#include "model/GameGrid.hpp"

int main(int argc, char const *argv[]) {
theendless::model::Block b;
theendless::model::GameGrid<1, 1> g;
g(0, 0) = b;
std::string s(g(0, 0).get_name());
std::cout << s << std::endl;
return 0;
}

这是我model/Block.hpp

#ifndef THEENDLESS_MODEL_BLOCK_HPP
#define THEENDLESS_MODEL_BLOCK_HPP

#include <string>

namespace theendless::model {
class Block {
private:
std::string name;
public:
Block();
Block(std::string name);
std::string get_name() const;
void set_name(const std::string newName);
};
}

#endif

这是我model/Block.cpp

#include "model/Block.hpp"
#include <string>
namespace theendless::model {
Block::Block() : name("default_name") {}
Block::Block(std::string name) : name(name) {}

std::string Block::get_name() const { return this->name; }
void Block::set_name(const std::string newName) { this->name = newName; }
}

以下是make显示的错误:

PS C:projectsTheEndlessbuild> make
Scanning dependencies of target pentest
[ 75%] Building CXX object CMakeFiles/pentest.dir/src/pentest.cpp.obj
[100%] Linking CXX executable bin/pentest.exe
c:/mingw/bin/../lib/gcc/x86_64-w64-mingw32/9.2.0/../../../../x86_64-w64-mingw32/bin/ld.exe: CMakeFiles/pentest.dir/objects.a(pentest.cpp.obj): in function `main':
C:/projects/TheEndless/src/pentest.cpp:9: undefined reference to `theendless::model::Block::Block()'
c:/mingw/bin/../lib/gcc/x86_64-w64-mingw32/9.2.0/../../../../x86_64-w64-mingw32/bin/ld.exe: C:/projects/TheEndless/src/pentest.cpp:13: undefined reference to `theendless::model::Block::get_name[abi:cxx1c:/mingw/bin/../lib/gcc/x86_64-w64-mingw32/9.2.0/../../../../x86_64-w64-mingw32/bin/ld.exe: CMakeFiles/pentest.dir/objects.a(pentest.cpp.obj): in function `std::array<theendless::model::Block, 1ull>::array()':
c:/mingw/include/c++/9.2.0/array:94: undefined reference to `theendless::model::Block::Block()'
collect2.exe: error: ld returned 1 exit status
make[2]: *** [CMakeFiles/pentest.dir/build.make:107: bin/pentest.exe] Error 1
make[1]: *** [CMakeFiles/Makefile2:124: CMakeFiles/pentest.dir/all] Error 2
make: *** [Makefile:103: all] Error 2

请注意,包含model/Block.cpppentest.cpp会使问题消失,但我有点想使项目干净,所以我不想这样做。

附加信息 :

  • 我在窗口 10 上。
  • 我正在使用VSCode来编辑我的文件,并通过在集成的*PowerShell终端中键入make进行编译。
  • 我正在使用 Mingw 发行版进行编译。

任何帮助将不胜感激! :)

我不是 CMake 或编译器专家,但这是我理解正在发生的事情的方式。

编译器不会四处搜索任何标头或源文件,除非被告知必须这样做。但是编译器什么时候必须搜索它们呢?

  1. 该文件(通常是标头)包含在编译器已经知道的文件中。
  2. 编译器已明确知道该文件(例如,在 CMakeLists.txt 中)。

在您的情况下,编译器知道头文件,因为它是在 pentest.cpp 源文件(上面的变体 1)中#include的。编译器是如何知道pentest.cpp的?在函数add_executable中明确声明,特定的构建目标pentest是从此文件构建的。

现在Block.cpp呢?编译器不知道,因为它既没有包含在 CMakeLists.txt 中,也没有说明编译器必须使用此文件。所以编译器无法知道它。

正如您已经提到的,包含.cpp文件不是一个好的样式。仅包含头文件的一个巨大优势(在我看来)是,如果函数的实现发生变化(不是它的声明,而是函数的主体),你不必重新编译使用它的所有内容。您只需要重新编译该.cpp文件即可。

那么解决方案是什么?您必须使编译器知道应该用于构建目标pentest的所有源文件。因此,您必须将这些源文件添加到函数add_executable.add_executable的 CMake 文档告诉您以下内容。

add_executable(<name> [WIN32] [MACOSX_BUNDLE]
[EXCLUDE_FROM_ALL]
[source1] [source2 ...])

添加调用要从命令调用中列出的源文件生成的可执行目标。

因此,您必须将所有用于构建目标的源文件添加到同一个add_executable命令调用中。在您的情况下,CMakeLists.txt 看起来像这样。请注意,我必须在计算机上添加target_compile_features和删除add_compile_definitions,以强制使用 C++17。

cmake_minimum_required(VERSION 2.8.12)
project(TheEndless)

# add_definitions("-std=c++17") # does not work on my Windows 10 machine

include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
conan_basic_setup()

include_directories(
${PROJECT_SOURCE_DIR}/include
${PROJECT_SOURCE_DIR}/src
${PROJECT_SOURCE_DIR}/src/model
)

link_directories(${CMAKE_SOURCE_DIR}/lib)

add_executable(main src/main.cpp)
# add all source files needed to build to the executable
# GameGrid.cpp is not needed because it is included in the header.
add_executable(pentest src/pentest.cpp src/model/Block.cpp)
target_link_libraries(main ${CONAN_LIBS})
target_link_libraries(pentest ${CONAN_LIBS})
target_compile_features(pentest PRIVATE cxx_std_17) # added to really use C++17, see the docs for explanation

我看到的唯一"问题"是:Visual Studio将Block.hpp,GameGrid.hpp和GameGrid标记为"外部依赖项".cpp。如果您希望将它们显示为目标的一部分,您也可以将它们添加到add_executable.我不确定是否有另一种解决方案,因为它似乎有点多余。

相关内容

  • 没有找到相关文章

最新更新