C ++ header-implementation-header-implementation依赖关系链

时间:2011-09-25 17:18:04

标签: c++ compiler-construction build incremental-build

我正在尝试使用依赖项解析器创建简单的C ++增量构建工具。 我对cpp构建过程的一个问题感到困惑。 想象一下,我们有一个库包含几个文件:

// h1.h
void H1();

// s1.cpp
#include "h1.h"
#include "h2.h"
void H1(){ H2(); }

// h2.h
void H2();

// s2.cpp
#include "h2.h"
#include "h3.h"
void H2(){ /*some implementation*/ }
void H3(){ /*some implementation*/ }

// h3.h
void H3();

在客户端代码中包含h1.h

// app1.cpp
#include "h1.h"
int main()
{
  H1();
  return 0;
}

有s2.cpp实现的隐式依赖: our_src - > h3 - > s1 - > h2 - > S2。所以我们需要链接两个obj文件:

g++ -o app1 app1.o s1.o s2.o

相比之下,包括h3.h

// app2.cpp
#include "h3.h"
int main()
{
  H3();
  return 0;
}

只有一个源依赖: our_src - > h3 - > S2

所以当我们包含h3.h时,我们只需要编译s2.cpp(尽管s1.cpp - > h2.h包含):

g++ -o app2 app2.o s2.o

这是一个非常简单的问题示例,在实际项目中,我们肯定会有几百个文件,而低效的包含链可能包含更多文件。

所以我的问题是:当我们检查依赖关系时(没有CPP解析),是否有一种方法或工具可以找出哪些包含内容可以省略?

我很感激任何回应。

3 个答案:

答案 0 :(得分:2)

如果您声明看到对s2.cpp的隐式依赖,则需要解析实现模块s1.cpp,因为只有在那里您才会发现s1模块正在使用s2。所以对于“如果不解析.cpp文件我能解决这个问题”的问题,答案显然是否定的。

就语言而言,在头文件或实现文件中可以放置的内容之间没有区别。 #include指令在C ++级别不起作用,它只是一个文本宏函数,不需要理解语言。 而且即使解析“只是”C ++声明也是一个真正的噩梦(C ++语法的难点部分是声明,而不是语句/表达式)。

您可以使用解析C ++文件的gccxml结果并返回可以检查的XML数据结构。

答案 1 :(得分:0)

这不是一个容易的问题。只有很多事情让这很困难:

  1. 如果在N> 1源文件中实现一个头文件怎么办?例如,假设class Foo中定义了foo.h,但在foo_cotr_dotr.cppfoo_this_function.cppfoo_that_function.cpp中实施了Foo::bar()
  2. 如果在多个源文件中实现相同的功能怎么办?例如,假设foo_bar_linux.cppfoo_bar_osx.cppfoo_bar_sunos.cpp,{{1}}中有实现。要使用的实现取决于目标平台。
  3. 一个简单的解决方案是构建共享或动态库并链接到该库。让工具链解决这些依赖关系。问题#1完全消失,如果你有足够聪明的makefile,问题#2也会消失。

    如果你坚持反对这个简单的解决方案,你将需要做一些事情来自己解决这些依赖关系。您可以通过项目规则一个头文件==一个源文件消除上述问题(不是详尽的列表)。我已经看过这样一条规则,但并不像我看到的一个项目规则那样频繁地说一个函数==一个源文件。

答案 2 :(得分:0)

您可以查看我如何实施Wand。它使用指令为各个源文件添加依赖项。文档尚未完全完成,但Gabi的源代码中有Wand指令的例子。

实施例

线程类包含文件

Thread.h在链接时需要thread.o

#ifdef __WAND__
dependency[thread.o]
target[name[thread.h] type[include]]
#endif

Windows上的线程类实现(thread-win32.cpp)

只有在Windows是目标平台时才能编译此文件

#ifdef __WAND__
target[name[thread.o] type[object] platform[;Windows]]
#endif

GNU / Linux上的线程类实现(thread-linux.cpp)

只有在GNU / Linux是目标平台时才能编译此文件。在GNU / Linux上,链接时需要外部库pthread。

#ifdef __WAND__
target
    [
    name[thread.o] type[object] platform[;GNU/Linux]
    dependency[pthread;external]
    ]
#endif

利弊

赞成

  • Wand可以扩展为适用于其他编程语言
  • Wand将通过命令wand
  • 保存成功链接新程序所需的所有必要数据
  • 项目文件不需要提及任何依赖项,因为它们存储在源文件中

缺点

  • Wand在每个源文件中都需要额外的指令
  • 该工具尚未被图书馆作家广泛使用