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

为什么应该用模块取代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上的讨论

分享到:
评论

相关推荐

    C-C++ 常见误区

    3. **头文件的引用方式**:在C/C++中,头文件的引用方式有两种,一种是使用尖括号`&lt;&gt;`,另一种是使用双引号`""`。尖括号通常用于引用系统库中的头文件,而双引号则用于引用用户自定义的头文件。然而,有些程序员在...

    openssl-include.rar_C常用头文件_openssl_ssl_visual c

    在这个名为"openssl-include.rar"的压缩包中,包含了C语言编程时使用OpenSSL所需的头文件,这对于在Visual C++环境下开发相关项目尤其有用。下面,我们将深入探讨这些知识点。 首先,`openssl_ssl`是指OpenSSL中的...

    cpp20学习笔记-模块1

    C++20 模块是该编程语言的一个重要更新,为开发者带来了许多优势。模块的主要目的是提高代码的可重用性和隔离性,减少命名冲突,同时通过预处理优化编译时间。在 C++20 之前,头文件是组织和共享代码的主要方式,但...

    C&C++ 语言参考

    C&C++语言是计算机编程领域中的基础且强大的工具,它们为开发者提供了低级内存管理和高性能编程的能力。"C&C++语言参考"是一个重要的资源,它涵盖了C和C++语言的各个方面,包括标准库、预处理命令和操作符优先级等...

    Android技术之JNI和HAL

    4. **编译C/C++代码为动态链接库**:使用编译器(如GCC)将C/C++代码编译成.so库文件,并确保这些库文件被正确加载到Android运行时环境中。 5. **集成与测试**:将生成的动态链接库文件放置到指定目录下,并在Java...

    高质量C++C编程指南

    在C++和C编程中,程序通常被划分为两类文件:**头文件**和**定义文件**。 - **头文件**:主要用于保存程序的声明(如函数原型、类定义等)。C++/C的头文件后缀一般为`.h`。 - **定义文件**:主要包含具体的实现细节...

    ataDrv.rar_C/C++_

    本篇将深入探讨如何使用C/C++语言开发一个针对SATA硬盘的驱动程序,特别是在嵌入式操作系统VxWorks中的实现。 首先,我们来了解一下什么是ATA(Advanced Technology Attachment)协议。ATA是一种广泛应用于硬盘、...

    从C++到Object-c

    这意味着可以在Objective-C程序中直接使用C的库和函数,这为开发提供了极大的灵活性。 #### 3.4 新类型与值 Objective-C引入了新的数据类型和值,包括: - `BOOL`: 布尔类型,可以是`YES`或`NO`。 - `nil`: 表示空...

    计算机二级c++之C++与C语言的区别.pdf

    C++的预处理器与C基本相同,但C++中`#include`可以使用尖括号(`&lt;iostream&gt;`)和引号(`"iostream"`)两种方式,尖括号通常用于标准库,引号用于本地头文件。 总的来说,C++在保留C语言的效率和灵活性的同时,增强...

    C++远控源码

    在C++中,通常使用标准库中的`&lt;sys/socket.h&gt;`和`&lt;netinet/in.h&gt;`等头文件来处理套接字操作。 再者,"C++"作为主要的编程语言,意味着源码将使用面向对象的编程思想。C++提供了类、对象、封装、继承、多态等特性,...

    JNI 简介与实现.zip

    2. **生成头文件**:使用`javah`工具(在现代版本的Android Studio中,已经由`javac -h`取代)从包含native方法的Java类生成C/C++头文件。这个头文件包含了Java方法签名的C/C++声明。 3. **编写本地代码**:基于...

    Visual C++中调用DLL实现数据加密

    总的来说,Visual C++中调用DLL实现数据加密涉及到模块化编程、动态链接技术、加密算法以及Windows API的使用。开发者需要具备扎实的C++基础知识,对Windows编程有深入理解,并熟悉各种加密算法的原理,才能有效地...

    keil for ARM 的重要文件 包括头文件 启动文件 库文件

    头文件在C或C++编程中用于包含常量定义、函数声明、结构体定义等,帮助程序员在多个源文件之间共享信息。在ARM开发中,头文件可能包括芯片厂商提供的外设驱动接口、系统配置宏、中断向量定义等。 3. **library** ...

    C++小程序

    标题 "C++小程序" 暗示我们关注的是使用C++编程语言开发的微型应用程序。C++是一种强大的、面向对象的编程语言,广泛应用于系统软件、应用软件、游戏开发、设备驱动程序等领域。在这个场景中,提到的是用VC++6.0...

    图像处理连通域算法 c++ ,vc 6.0

    4. **VC 6.0**:Visual C++ 6.0是微软发布的一个早期版本的集成开发环境(IDE),虽然现在已经被更新的版本取代,但在某些特定领域,如教学和老项目的维护,它仍然被广泛使用。 5. **源代码文件**: - **Connected...

    Turbo C++ 3.0

    7. **代码库和头文件**:Turbo C++ 3.0自带了一系列标准库函数和头文件,包括I/O操作、数学运算、字符串处理等,方便开发者快速构建应用程序。 8. **图形编程**:尽管运行在DOS下,Turbo C++ 3.0仍然支持图形编程,...

    iPhone软件开发编程语言ObjectiveC学习资料借鉴.pdf

    Objective-C是苹果生态系统中用于开发iOS和macOS应用的主要编程语言,它基于C语言,并添加了面向对象的特性。在初学者看来,Objective-C的语法可能显得复杂,特别是它的消息传递机制和类定义方式,但理解了其核心...

    jni开发demo

    - 在项目目录下的`app`模块的`build.gradle`文件中,启用C++支持。添加以下代码: ```groovy android { ... externalNativeBuild { cmake { cppFlags "" abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', '...

Global site tag (gtag.js) - Google Analytics