【问题标题】:Header files dependencies between C++ modulesC++ 模块之间的头文件依赖关系
【发布时间】:2010-11-12 17:20:02
【问题描述】:

在我这里,我们有一个庞大的 C++ 代码库,我认为如何使用头文件存在问题。

有很多 Visual Studio 项目,但问题是概念上的,与 VS 无关。每个项目都是一个模块,执行特定的功能。每个项目/模块都编译为库或二进制文件。每个项目都有一个包含所有源文件的目录 - *.cpp 和 *.h。有些头文件是模块的API(我的意思是声明创建库的API的头文件子集),有些是它内部的。

现在的问题 - 当模块 A 需要与模块 B 一起工作时,A 添加 B 的源目录以包含搜索路径。因此,A 在编译时可以看到所有 B 的模块内部头文件。

作为一个副作用,开发人员不必专注于每个模块的确切 API 是什么,无论如何我认为这是一个坏习惯。

我首先考虑了一个选项。我想过在 每个项目都有一个只包含接口头文件的专用目录。希望使用该模块的客户端模块只允许包含接口目录。

这种方法可以吗?问题是如何代替你解决的?

UPD 在我之前的地方,开发是在 Linux 上使用 g++/gmake 完成的,我们确实曾经将 API 头文件安装到一个公共目录中,这是一些建议的答案。现在我们有了使用 cmake 生成项目文件的 Windows (Visual Studio)/Linux (g++) 项目。如何在 Visual Studio 中强制预安装 API 头文件?

谢谢 德米特里

【问题讨论】:

    标签: c++ dependencies development-environment


    【解决方案1】:

    这听起来像你在正确的轨道上。许多第三方库都做同样的事情。例如:

    3rdParty/myLib/src/                  - 包含编译库所需的头文件和源文件
    3rdParty/myLib/include/myLib/ - 包含外部应用程序需要包含的标头

    有些人/项目只是将要被外部应用程序包含的标题放在 /3rdParty/myLib/include 中,但添加额外的 myLib 目录有助于避免名称冲突。

    假设您使用的结构是:3rdParty/myLib/include/myLib/

    在外部应用程序的 Makefile 中: --------------- 包含 =-I$(3RD_PARTY_PATH)/myLib/include INCLUDE+=-I$(3RD_PARTY_PATH)/myLib2/include ... ... 在外部应用程序的 Source/Headers 中 #include "myLib/base.h" #include "myLib/object.h" #include "myLib2/base.h"

    【讨论】:

      【解决方案2】:

      将接口头文件放在项目的根目录中,并为非 API 头文件创建一个子文件夹(称为“内部”或“助手”或类似名称)不是更直观吗?

      【讨论】:

      • 我相信谷歌将他们所有的内部标题放在一个名为 internal 的文件夹中。
      【解决方案3】:

      我已经看到通过在模块 B 中设置一组标头来解决此类问题,这些标头作为构建过程的一部分与 lib 一起被复制到发布目录。然后,模块 A 只能看到这些标头,并且永远无法访问 B 的内部。通常我只在公开发布的大型项目中看到过这种情况。

      对于内部项目,这不会发生。通常发生的情况是,当它们很小时,这并不重要。当他们长大后,将它分开是非常混乱的,没有人愿意这样做。

      【讨论】:

        【解决方案4】:

        通常我只看到一个包含所有接口头文件的包含目录。它当然可以很容易地包含标题。人们在为链接器指定模块时仍然需要考虑他们依赖哪些模块。

        也就是说,我更喜欢你的方法。你甚至可以避免将这些目录添加到包含路径中,这样人们就可以通过顶部#includes 中的相对路径来判断源文件依赖于哪些模块。

        根据您的项目的布局方式,从头文件中包含它们可能会出现问题,因为头文件的相对路径来自 .cpp 文件,而不是来自 .h 文件,所以 .h 文件不一定知道它的 .cpp 文件在哪里。

        但是,如果您的项目具有平面层次结构,这将起作用。说你有

        base\foo\foo.cpp
        base\bar\bar.cpp
        base\baz\baz.cpp
        base\baz\inc\baz.h
        

        现在任何头文件都可以包含
        #include "..\baz\inc\baz.h
        它会起作用,因为所有的 cpp 文件都比 base 深一层。

        【讨论】:

          【解决方案5】:

          在我工作的地方,我们在构建时创建了一个交付文件夹结构。定义库的头文件被复制到包含文件夹中。我们使用自定义构建脚本,让开发人员指定应导出哪些头文件。

          然后我们的构建植根于substed 驱动器,这允许我们使用绝对路径来包含目录。

          我们还有一个基于网络的参考构建,允许我们将映射驱动器用于包含和库引用。

          更新:我们的参考构建是我们构建服务器上的网络共享。我们使用一个参考构建脚本来设置构建环境并映射(使用net use)构建服务器上的命名共享(即\BLD_SRV\REFERENCE_BUILD_SHARE)。然后在每周构建(或手动)期间,我们将共享(使用net share)设置为指向新构建。

          我们的项目然后是包含和库引用的绝对路径列表。

          例如:

          subst'ed local build drive j:\
          mapped drive to reference build: p:\
          path to headers: root:\build\headers
          path to libs: root:\build\release\lib
          include path in project settings j:\build\headers; p:\build\headers
          lib path in project settings     j:\build\release\lib;p:\build\release\lib
          

          这将首先对您进行本地更改,然后如果您没有进行任何本地更改(或者至少您没有构建它们),它将使用您上次在构建服务器上构建的头文件和库。

          【讨论】:

            【解决方案6】:

            在我一直在工作的一个小组中,所有公共内容都保存在特定于模块的文件夹中,而私有内容(私有标头、cpp 文件等)保存在 _imp 文件夹中:

            base\foo\foo.h
            base\foo\_imp\foo_private.h
            base\foo\_imp\foo.cpp
            

            这样你就可以在你的项目文件夹结构中找到你想要的标题。您可以使用 grep 查找包含 _imp 的 #include 指令并仔细查看它们。您还可以抓取整个文件夹,将其复制到某个位置,然后删除所有 _imp 子文件夹,因为您知道您已经准备好发布 API。 在通常包含为

            的项目标题中
            #include "foo/foo.h"
            

            但是,如果项目必须使用某些 API,那么 API 标头将被 API 的构建复制/安装到构建系统应该在该平台上的任何位置,然后作为系统标头安装:

            #include <foo/foo.h>
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2021-09-01
              • 1970-01-01
              • 2012-03-09
              • 2010-10-16
              • 1970-01-01
              • 1970-01-01
              • 2015-12-28
              • 2016-09-24
              相关资源
              最近更新 更多