【问题标题】:Error including "private" header with CMake在 CMake 中包含“私有”标头的错误
【发布时间】:2020-04-11 14:05:29
【问题描述】:

在我的库中,我将公共标头与源代码分开,将它们放入includesrc。在使用 Cmake 时,我的库中有这个:

target_include_directories(${PROJECT_NAME}
    PRIVATE 
        "${CMAKE_CURRENT_SOURCE_DIR}/include/Thoth"
        "${CMAKE_CURRENT_SOURCE_DIR}/src"
    INTERFACE 
        "${CMAKE_CURRENT_SOURCE_DIR}/include"
)

这背后的想法是我希望最终用户(使用库)只有 include 目录,因此它们必须像这样包含:

#include <Thoth/file.h>

但在库中我可以省略Thoth
现在我还在库中包含了src 目录,因为那里有一些私有头文件。
这没问题,我可以将每个文件都包含在预期的路径中。

在编译使用该库的 exe 时,该库会成功编译,但会在 exe 本身上失败。

这是由于在库 src 中找不到标头。现在该文件未包含在 exe 中,因为它是私有的。但这是因为 exe 包含一个包含私有标头的标头。

我已经尝试了一些可见性设置并进行了谷歌搜索,但我没有找到答案。
其他人如何处理分离私有和公共标头。

当然,我总是可以在库中使用相对路径,但我宁愿避免这种情况:

#include "../src/private.h"

很丑。
当然,如果这是唯一的方法,那就这样吧。虽然我认为其他人会遇到这种情况并想要一个解决方案。

我能想到的另一种方法是只公开包含 src 文件夹并相信用户不包含私有标头,但这会污染包含路径并且是不可取的。

完整的库 CMake:

cmake_minimum_required(VERSION 3.13)

project(Thoth)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

add_library(${PROJECT_NAME}
    src/IndentData.cpp
    src/RenderComponent.cpp
    src/RenderElement.cpp
    src/RenderManager.cpp
)

target_include_directories(${PROJECT_NAME}
    PRIVATE 
        "${CMAKE_CURRENT_SOURCE_DIR}/include/Thoth"
        "${CMAKE_CURRENT_SOURCE_DIR}/src"
    INTERFACE 
        "${CMAKE_CURRENT_SOURCE_DIR}/include"
)

完整的 exe CMake:

cmake_minimum_required(VERSION 3.13)

project(myExe)

add_subdirectory(lib/Thoth)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
add_executable(${PROJECT_NAME} src/main.cpp)

target_link_libraries(${PROJECT_NAME} PUBLIC Thoth)

至于文件,显示起来有点困难,但问题是RenderComponent.hpp(在include/Thoth 内)包含IndentData.hpp(在src 内)。因此,当 exe 包含 #include &lt;Thoth/RenderComponent.hpp&gt; 时,库的 src 目录不在包含路径中。

【问题讨论】:

  • 请举例
  • 很难在不使文件过大的情况下显示大部分文件的一部分。希望我添加的内容有所帮助。
  • 那么您有一个包含私有标头的公共标头?您将不得不将RenderComponent.hpp 更改为不包括IndentData.hpp

标签: c++ cmake


【解决方案1】:

@sparik 有一个很好的答案。这里有一些更详细的说明。

如果您的公共标头之一依赖于私有标头,则需要将该私有标头移动到公共位置这是因为当您部署库(二进制 + 标头)时,用户不应依赖于访问你的源代码树。

我能想到的另一种方法是只公开包含 src 文件夹并相信用户不包含私有标头,但这会污染包含路径并且是不可取的。

这是选项 1。它是最简单的并且并不少见。如果您查看像 boost unit_test_framework 这样的大型库的公共包含目录,您会看到私有的东西(即未记录的,受 api 损坏的)位于 &lt;boost/test/detail/...hpp&gt;&lt;boost/test/impl/...ipp&gt;

选项 2 是减少公共标头包含私有标头的需要。这可以通过向前声明所有内容来完成。存储对这些私有类的引用而不是由这些私有类组成是帮助您做到这一点的一种方法。

【讨论】:

  • 这似乎有道理。我必须问我什么时候可以使用纯粹的私有标头?因为我根本看不到私有标头最终不会落入最终用户手中的情况。
【解决方案2】:

库的用户直接或传递地包含的所有标题都应该在/include中。

如果LibInterface.h 包含LibInternal.h 并且用户包含LibInterface.h,那么LibInternal.h 也应该在/include 中,因为用户包含LibInternal.h 是可传递的。

如果您可以避免将LibInternal.h 包含在LibInterface.h 中,请执行此操作。有时你不能,因为LibInterface.h 可能依赖于LibInternal.h 中定义的东西。

如果你想阻止用户直接包含LibInternal.h,你可以把它放在 /include/detail 或类似的地方。

在某些情况下,您可以使用pimpl idiom 来打破接口和实现之间的依赖关系,但这也有其缺点,因此需要权衡。

【讨论】:

  • 这让我有点难过。不管怎样,我很可能会去为我的内部资料创建一个细节。您是说应该将纯粹用于内部的文件放在 details 子文件夹中还是直接省略?
  • 再想一想,如果所有文件都必须在文件 exe 包含路径中,那么能够描述单独的私有和公共 include_dirs 有什么意义?
  • 不是所有文件。仅包含在您的 cpp 文件中的 cpp 文件和标头不必是
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-03-05
相关资源
最近更新 更多