【问题标题】:C++ / CMake / Conan.io - 'Undefined Reference to Class::Method' when including headerC++ / CMake / Conan.io - 包含标头时“未定义对 Class::Method 的引用”
【发布时间】:2021-06-14 18:20:46
【问题描述】:

我在编译项目时遇到了一点麻烦,使用Conan.ioCMake

我正在构建一个基于 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 类是一个模板类,但我还是让标题在末尾包含了实现文件(在this StackOverflow question 之后)。
  • 我对 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:\projects\TheEndless\build> 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.cpp 包含到pentest.cpp 会使问题消失,但我有点想让项目变得干净,所以我不想这样做。

其他信息:

  • 我使用的是 Windows 10。
  • 我正在使用VSCode 编辑我的文件,并通过在集成的*PowerShell 终端中键入make 进行编译。
  • 我正在使用Mingw distro 进行编译。

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

【问题讨论】:

  • 我自己不是 CMake 英雄,但编译器似乎连 Block 都没有编译。您是否尝试将 Block.cpp 添加到 CMakeLists.txt 中的 add_executable 中?
  • @TobiasBrösamle 我刚刚做了,但它告诉我有一个undefined reference to 'WinMain'。我的看法是,这是因为它试图创建一个 'block.exe' 文件,但由于 'Block.cpp' 没有任何主要功能而失败
  • 你能具体说明你做了什么吗?您是将其添加到现有的 add_executable 语句中还是创建了一个新语句?
  • 在我的情况下,如果我将 .cpp 文件添加到 add_executable(pentest ...),它就可以工作。在这种情况下,将创建一个包含 .cpp 文件的 visualstudio 解决方案(我现在在 Windows 上)。如果您还希望解决方案中的 .hpp 文件,也必须添加它们。
  • 所以,我首先添加了一个新的add_executable(block src/model/block.cpp) 语句。它没有用。然后,我刚刚阅读了您的评论并将src/model/block.cpp 添加到现有的 pentest 评论中。它仍然停止链接,告诉我有多个定义到我的Block::Block()(以及其他与我的Block 类相关的)方法。

标签: c++ cmake include mingw undefined-reference


【解决方案1】:

我不是 CMake 或编译器专家,但这是我理解发生了什么的方式。

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

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

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

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

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

那么解决办法是什么?您必须让编译器知道应该用于构建目标pentest 的所有源文件。因此,您必须将这些源文件添加到函数add_executableCMake docs for add_executable 告诉你以下内容。

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

添加一个名为的可执行目标,从命令调用中列出的源文件构建

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

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。我不确定是否有其他解决方案,因为它似乎有点多余。

【讨论】:

  • 好的,非常感谢您的回复。我现在看得更清楚了!
猜你喜欢
  • 2013-01-19
  • 2012-07-29
  • 2011-06-05
  • 1970-01-01
  • 2015-07-10
  • 1970-01-01
  • 1970-01-01
  • 2015-05-05
  • 1970-01-01
相关资源
最近更新 更多