`
xpp02
  • 浏览: 1010825 次
社区版块
存档分类
最新评论

为什么应该用模块取代C/C++中的头文件?

 
阅读更多
摘要:本文整理自Apple C++工程师Doug Gregor的演讲Slide,他表示希望使用模块(Module)这一概念替代C/C++中的头文件,现已被C++标准化委员会任命为Module研究组的,研究该提议的可能性。考虑到Apple的开源项目LLVM在编辑器领域中的地位,这一提议非常值得重视。

为什么应该使用模块(Module)替代头文件(Header)?

头文件糟透了!


众所周知,C程序在编译时一般会预处理头文件:

常规解决办法如下:

  1. LLVM_WHY_PREFIX_UPPER_MACROS
  2. LLVM_CLANG_INCLUDE_GUARD_H
  3. template<class_Tp>
  4. const_Tp&min(const_Tp&__a,
  5. const_Tp&__b);
  6. #include<windows.h>
  7. #undefmin//because#defineNOMINMAX
  8. #undefmax//doesn’t

但结果依然不够理想,比较一下代码与程序大小你会发现:

另外,头文件形式的可扩展性天生不足。假设有n个源文件,每个源文件引用了m个头文件,那么构建过程的开销会是m×n。这在C++中表现得尤为糟糕。所以预说处理头文件是一个非常糟糕的解决方案。

C家族的模块系统

模块是什么?

  • 库的接口(API)
  • 库的实现

使用“import”导入已命名的模块:

import会在源文件中忽略预处理状态,并且选择性导入,所以弹性(resilience)非常好。

使用“import”会导入什么?

  • 函数、变量、类型、模板、宏,等等;
  • 公开API——其它的都隐藏;
  • 没有特别的命名空间机制。

C/C++引入模块会怎么样?


引入模块的目标在于:

  • 在源文件中指定模块名称;
  • API公开;
  • 没有头文件!

要编写一个模块非常简单,只需要使用export:

但是这么做会遇到很多遗留问题:

  • 需要迁移现在基于头文件的类库;
  • 与不支持模块的编译器的互操作性;
  • 工具需要理解模块;

所以应该使用引入模块的过渡方案——直接从头文件中构建模块。这么做有以下好处:

  • 头文件有利于互操作;
  • 程序员不需要完全改变自己习惯的开发模式;

模块地图(Module Map)

模块地图是模块的关键,用来定位模块相关(子)模块,包含以下功能:

  • 模块定义命名(子)模块

  • 头文件在(子)模块中包含命名头文件的内容

保护伞头文件(Umbrella Header)

  • 保护伞头文件会在其目录下包含所有头文件信息
  • 使用通配符submodules (module *) 可以为每一个包含的头文件创建一个子模块:
    1. AST/Decl.h->ClangAST.Decl
    2. AST/Expr.h->ClangAST.Expr

模块编译过程:

  1. 找到命名模块的module map;
  2. 产生一个独立编译器实例;
  3. 在module map中解析头文件。

编辑模块文件过程:

  1. 在“import”声明处导入模块文件;
  2. 把模块文件保存在缓存中待重用。

从头文件到模块化


从头文件编程转换到使用模块非常简单:

库方面:合并复合定义的结构、函数、宏,并且为头文件导入依赖,最后编写好模块地图;

开发者方面只需要从“#include”过渡到“import”:

  1. 把“#inlude”都换成“import”;
  2. 使用module maps确定(子)模块(类似头文件里的“#include”);

当然,你也可以使用工具来自动化重写代码,非常简单。

工具


编辑性能

使用模块能够提升语法解析性能:

  • 模块化的头文件只需要解析一次,之后放在缓存中,于是m×n --> m+n
  • 所有基于源(source-based)工具都能带来好处
  • 自动链接大大简化了库的使用

  • 自动导入可以阻止“#include”带来的可怕的调试结果

调试流

通过DWARF的双程调试有损耗:

  • 只能获得“用过”类型和函数
  • 丢失了行内函数、模板

另外调试过程还会出现信息冗余

使用模块的调试又会怎样?

1.提高了构建性能

  • 编译器发出的DWARF更少
  • 链接器清除重复的DWARF也更少

2.提高了调试体验

  • 调试器的ASF精度非常完美
  • 调试器不需要寻找DWARF

总结


总而言之,C/C++使用模块化非常有潜力:

  • 编译/构建时间的缩短
  • 修复各种预处理问题
  • 更好的工具体验
  • 通过设计,能够平稳地过渡
  • Clang实现已经在进行了

这个Slide在Hacker News上引起了激烈讨论,大部分网友还是赞成模块化的方式:

baberman:对我来说没什么不便,而且还给出了过渡方案,可能会很适合某些C/C++项目。我们应该对任何提升C/C++性能的想法持开放态度。

greggman:预处理有什么不好吗?如果我不想用预处理,我完全可以使用Objective-C等。现在的机器性能已经够强大了,import在编译上的性能优势对我来说没有任何吸引力,我更喜欢C/C++的传统方式。

msbarnett反驳greggman:我认为这正是这份提议的精髓所在,你既可以保留使用#include,也可以从现在开始就转向import模块的方式。

_djo_:这个想法会非常有前途!要说缺点的话就是来得太迟了!

nkurz:我不同意“m个头文件+n个源文件 --> m×n倍编译性能”。只要使用“#ifndef _HEADER_H”就不会出现这个问题,或者,为什么不使用#include_once <header.h>来解决呢?预处理很可怕吗?预处理确实有一些问题,但是却是可以克服的。

SeoxyS:我不关心性能,现在性能已经不是问题了,我更关心怎样给开发者更少的负担。

各位CSDN网友是怎样看的呢?欢迎一起讨论。

相关链接:Doug的SlideHacker News上的讨论

分享到:
评论

相关推荐

    深入浅出MFC 2e

    为什么使用Application Framework Microsoft Foundation Classes(MFC) 白头宫女话天宝:Visual C++与MFC 纵览MFC General Purpose classes CObject 数据处理类(collection classes) 杂项类 异常处理类...

    深入浅出MFC【侯捷】

    侯捷怎么说 我怎么说 别人怎么说 为什么使用Application Framework Microsoft Foundation Classes(MFC) 白头宫女话天宝:Visual C++与MFC 纵览MFC General Purpose classes CObject 数据处理类(collection ...

    侯捷- -深入浅出MFC

    为什么使用Application Framework Microsoft Foundation Classes(MFC) 白头宫女话天宝:Visual C++与MFC 纵览MFC General Purpose classes CObject 数据处理类(collection classes) 杂项类 异常处理类...

    LuaBind 源码 (Lua增强库)

    为了暂停(yielding)C++函数,(不支持在C++侧和Lua侧传送数据块),你可以使用 yield 策略. 接受 object 参数的resume_function()的重载版本要求对象必须是一个协程对象.(thread) lua_State* thread = lua_newthread(L...

    本科毕业设计开题报告(成绩管理系统的设计与实现V3)

    本系统使用的开发语言是c++语言,C++语言是一种优秀的面向对像程序设计语言,它在C语言的基础上发展而来,但它比C语言更容易为人们学习和掌握。C++以其独特的语言机制在计算机科学的各个领域中得到了广泛的应用。...

    深入浅出MFC-简体版(2)PDF

    需要什么头文件(.H) 以消息为基础,以事件驱动之(message based,event driven) 一个具体而微的Win32程序 程序进入点WinMain 窗口类之注册与窗口之诞生 消息循环 窗口的生命中枢:窗口函数 消息映射(Message ...

    VC++6.0核心编程源码.rar

    当一个Windows函数返回一个错误代码时,它常常可以用来了解函数为什么会运行失败。Microsoft公司编译了一个所有可能的错误代码的列表,并且为每个错误代码分配了一个32位的号码。 从系统内部来讲,当一个Windows函数...

    网管教程 从入门到精通软件篇.txt

    HPP:C++程序头文件 HQX:Macintosh BinHex 4.0文件 HT:HyperTerminal(超级终端) HTM,HTML:超文本文档 HTT:Microsoft超文本模板 HTX:扩展HTML模板 I ICO:Windows图标 IDX:Microsoft FoxPro相关...

Global site tag (gtag.js) - Google Analytics