结合C ++头文件

时间:2011-12-07 08:33:11

标签: c++ include header-files

是否有自动方法来获取大量C ++头文件并将它们组合在一起?

当然,此操作必须以正确的顺序连接文件,以便在即将到来的类和函数中使用之前不会定义任何类型等。

基本上,我正在寻找一些允许我将库分发到两个文件(libfoo.h, libfoo.a)中的东西,而不是当前的一堆包含文件+二进制库。

8 个答案:

答案 0 :(得分:9)

正如你的评论所说:

  

..我想让图书馆用户更容易,所以他们可以只做一个#include并拥有一切。

然后您可以花一些时间,包括所有标题在“包装”标题中,按正确的顺序。 50个标题不是那么多。做一些像:

// libfoo.h
#include "header1.h"
#include "header2.h"
// ..
#include "headerN.h"

如果您手动执行此操作,则不会花费太多时间。

此外,稍后添加新标题 - 只需几秒钟,即可将其添加到此“包装标题”中。

在我看来,这是最简单,最干净,最有效的解决方案。

答案 1 :(得分:2)

你想做什么听起来像是“javascriptish”给我:-)。但如果你坚持,总会有“猫”(或Windows中的等价物):

$ cat file1.h file2.h file3.h > my_big_file.h 

或者,如果您使用的是gcc,请创建一个文件my_decent_lib_header.h,其中包含以下内容:

#include "file1.h"
#include "file2.h"
#include "file3.h"

然后使用

$ gcc -C -E my_decent_lib_header.h -o my_big_file.h

这样你甚至可以得到引用原始文件的文件/行指令(如果你愿意,可以禁用它)。

至于你的文件订单的自动化程度,嗯,它根本不是;你必须自己决定订单。事实上,我会惊讶地听到可以构建一个在C / C ++的所有情况下正确排序标头依赖关系的工具。

答案 2 :(得分:2)

(适应我的重复问题的答案:)

还有其他一些库,这些库旨在以单头形式分发,但是使用多个文件进行开发。他们也需要这样的机制。对于某些(大多数?),它是不透明的,不是分布式代码的一部分。幸运的是,至少有一个例外:Lyra,一个命令行参数解析库;它使用基于Python的包含文件融合/合并脚本,您可以找到here

该脚本的文档记录不充分,但是您使用它们的方式是使用3个命令行参数:

  • --src-include-要转换的包含文件,即将其包含指令合并到其主体中。您的情况是libfoo.h包括其他文件。
  • --dst-include-要写入的输出文件-合并的结果。
  • --src-include-dir-指定相对于包含文件的目录(即一个目录的“包含搜索路径”;该脚本不支持C ++编译器提供的多个包含路径和搜索优先级的复杂机制优惠)

该脚本具有递归作用,因此,如果file1.h--src-include-dir下包含另一个文件,则该文件也应合并。

现在,我可以挑剔该脚本的代码,但是-嘿,它可以工作,并且它是FOSS-随Boost许可证一起分发。

答案 3 :(得分:1)

如果您有一个包含所有其他可用的主包含文件,您可以简单地在Perl中破解C预处理器重新实现。仅处理“”样式包括并递归粘贴这些文件的内容。应该是二十二班。

如果没有,你必须自己写一个或随机尝试。 C ++中的自动依赖关系跟踪是 hard 。就像“让我们看看这个模板实例化是否会导致参数类的隐式实例化”一样。我看到的唯一自动化方式是将您的包含文件随机排序,查看整个数据库是否编译,并重新混洗它们直到它编译。哪个会拿n!时间,你手写包含文件可能会更好。

虽然第一个变体很容易破解,但我怀疑这个hack的敏感性,因为你想要在包级别(源代码tarball,deb包,Windows安装程序)而不是文件级别上进行分发。

答案 4 :(得分:1)

如果您的库太大而无法构建和维护像Kiril建议的单个包装头文件,这可能意味着它的架构不够好。

因此,如果您的库非常庞大(超过一百万行源代码),您可以考虑使用

等工具实现自动化。
  • GCC使依赖关系生成器preprocessor options更像-M -MD -MF等,并使用另一个手工制作的脚本对其进行排序
  • 昂贵的商业静态分析工具,如coverity
  • 通过插件或(对于GCC 4.6)MELT扩展名
  • 自定义编译器

但我不明白为什么你想要一种自动化的方法。如果库的大小合理,您应该理解它并且能够手动编写和维护包装头。自动执行该任务将花费您一些努力(可能是几周,而不是几分钟),因此仅适用于非常大的库。

答案 5 :(得分:0)

通常,您不希望将所有标题中的所有信息都包含在特殊标题中,以便潜在用户实际使用您的库。非平凡的类型定义删除,进一步包括或定义,对于您的界面的用户来说不是必需的,无法自动完成。据我所知。

对你的主要问题的简短回答:

  • 没有

我的建议:

  • 手动创建一个新标题,其中包含库接口用户的所有相关信息(仅此而已)。为它包含的每个组件添加好的文档注释。

  • 尽可能使用前向声明,而不是完整的包含定义。将实际包含放在您的实现文件中。您在标题中包含的声明越少越好。

  • 不要构建包含深层嵌套的层次结构。这使得很难对您包含的每个位的内容进行概述。您的库的用户将查看标题以了解如何使用它。并且他可能无法区分相关代码和第一眼看不相关的代码。您希望最大化库中主标题中每个总代码的相关代码的比率。

修改

如果你真的有一个工具包库,并且包含的​​顺序无关紧要,并且你有一堆独立的标题,你想要枚举只是为了方便单个标题,那么你可以使用一个简单的脚本。像下面的Python(未经测试):

import glob
with open("convenience_header.h", 'w') as f:
  for header in glob.glob("*.h"):
    f.write("#include \"%s\"\n" % header)

答案 6 :(得分:0)

你真的需要一个构建脚本来在你工作时生成它,以及一个预处理器标志来禁止使用合并(可能是你的用途)。

为简化此脚本/程序,有助于获得标题结构并以最佳形式包含卫生。

  • 您的程序/脚本需要知道您的发现路径(提示:如果可能,请尽量减少搜索路径的数量)。

  • 运行脚本或程序(您创建)以使用头文件内容替换include指令。

  • 假设您的标题都是典型的保护标记,您可以跟踪已实际包含的文件,如果有其他请求包含它们,则不执行任何操作。如果找不到标题,请保持原样(作为include指令) - 这是系统/第三方标题所必需的 - 除非您为外部包含使用单独的标题(这根本不是一个坏主意)

  • 最好只包含标题的构建阶段/翻译,并产生零警告或错误(警告为错误)。

或者,您可以创建一个特殊的分发存储库,这样他们就不需要做更多的工作,而不是偶尔从中获取。

答案 7 :(得分:0)

有点晚了,但现在确实如此。我最近自己偶然发现了同样的问题并编写了这个解决方案:https://github.com/rpvelloso/oneheader

  

它是如何运作的?

     
      
  1. 扫描您项目的文件夹中的C / C ++标题,并创建找到的标题列表;

  2.   
  3. 对于列表中的每个标头,它会分析其#include指令并按以下方式组合依赖关系图:

         
        
    1. 如果包含的标题不在项目文件夹中,则会被忽略(例如,如果它是系统标题);

    2.   
    3. 如果包含的标题位于项目文件夹内,则在依赖关系图中创建边缘,将包含的标题链接到正在分析的当前标题;

    4.   
  4.   
  5. 依赖关系图在拓扑上排序,以确定将标头连接到单个文件的正确顺序。如果在图表中找到一个循环,则该过程被中断(即,如果它不是DAG);

  6.         

    限制:

         
        
    1. 目前只检测单行#include指令(例如,#include);
    2.   
    3. 它不处理不同路径中具有相同名称的标头;
    4.   
    5. 它只给你一个正确的顺序来组合所有标题,你仍然需要连接它们(也许你想要在合并之前删除或修改它们中的一些)。
    6.         

      编译:

           

      g ++ -Wall -ggdb -std = c ++ 1y -lstdc ++ fs oneheader.cpp -o oneheader [.exe]

           

      用法:

           

      ./ oneheader [.exe] project_folder /> file_sequence.txt