首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >代码的“真面目”---如何查看cpp预处理后程序代码

代码的“真面目”---如何查看cpp预处理后程序代码

原创
作者头像
望天
修改2020-12-08 21:55:32
5K0
修改2020-12-08 21:55:32
举报
文章被收录于专栏:along的开发之旅along的开发之旅

cpp中预处理必不可少,如何查看预处理后的程序代码呢?单文件?CMake+makefile?CMake+ninja?ndk-build? XCode? 答案都在这里。

一、问题缘起

cpp的宏定义,适当的使用既可以减少重复代码,又避免了模板带来的代码膨胀,是很顺手的利器。

但使用宏定义后,宏在预处理阶段才展开,会造成代码阅读的不便;尤其是宏嵌套,会极大加深代码阅读和了解难度。

恐怖的宏定义
恐怖的宏定义

用宏封装后,使用起来会非常方便。但是第一次阅读时,会比较难以理解。如果能阅读宏展开后的代码,会轻松方便很多。

所以本文目的就是如何方便快捷的获得宏展开后的代码?

二、定位分析

我们先看下传统编译模型下,源码的编译步骤:

C/C++ 代码编译过程
C/C++ 代码编译过程

对于单文件,我们可以简单的使用gcc -E 获得预处理文件,使用gcc -S获得汇编文件,其他文件输出详见GCC Options Controlling the Kind of Output

但是在实际中,项目是由很多个文件组成的,文件间是有依赖关系的;手动确定依赖关系,并输入gcc来编译获得预处理文件,速度慢流程复杂,不具有实际使用意义。

所以需要找个一个方便且能自动帮我们确定依赖关系,直接输出预处理文件的方法。

三、解决方案

1. CMake + make

平常验证cpp代码喜欢使用CLion,CLion默认使用CMake + make构建系统,项目结构如下:

Clion项目结构
Clion项目结构

分析了CMake默认生成的makefile,意外发现里面就有我需要的target。

target “main.cpp.i”,其内容如下,作用是生成预处理preprocess文件。

# target to preprocess a source file
main.cpp.i:
	$(MAKE) -f CMakeFiles/cppConcurrencyDemo.dir/build.make CMakeFiles/cppConcurrencyDemo.dir/main.cpp.i

进入命令行,和makefile同级别目录,然后执行“make main.cpp.i”,就会生成对应的preprocess文件。

其支持的target如下,可以看到除了生成预处理文件,还有生成汇编的target "main.s"。

# Help Target
help:
	@echo "The following are some of the valid targets for this Makefile:"
	@echo "... all (the default if no target is provided)"
	@echo "... clean"
	@echo "... depend"
	@echo "... rebuild_cache"
	@echo "... edit_cache"
	@echo "... cppConcurrencyDemo"
	@echo "... main.o"
	@echo "... main.i"
	@echo "... main.s"

总结:由于是借助于CMake+makefile的能力,所以理论上所有CMake+makefile项目都可以用这种方法来获得预处理文件。

2. CMake + ninja

本以为探索到此为止。。。但是当我准备把这套方案挪到Android NDK项目上时,才忽然意识到,Android NDK项目是基于CMake+ninja构建系统,不是CMake+makefile这套。

最初想的是在ninja中找到makefile对应的预处理构建任务,然后用ninja来执行这些预处理构建任务。但是查询资料后发现,ninja为了提升构建速度,既没有默认生成这些中间文件,也没有生成这些中间文件的任务。同时gcc/clang最新的构建流程中,也不会生成这些中间文件。

继续探索,幸运的发现gcc的Debugging-Options有一个选项-save-temps,意如其名,保存临时文件,预处理和汇编都是生成object的中间临时文件。

-save-temps
Store the usual "temporary" intermediate files permanently; 
place them in the current directory and name them based on the source file. 
Thus, compiling foo.c with -c -save-temps would produce files foo.i and foo.s, as well as foo.o. 
This creates a preprocessed foo.i output file even though the compiler now normally uses an integrated preprocessor.

没毛病,给CMake加上这个参数,看下效果。

因为使用的是CMake,需要设置CMAKE_C_FLAGSCMAKE_CXX_FLAGS;前者是对c文件生效,后者是对cpp文件生效。

set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -save-temps")

set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -save-temps")

运行,rg -uuu --file | rg \.s,果然找到了生成的预处理文件,大功告成。

进一步查找,发现-save-temps还可以跟一个参数-save-temps=obj,表示生成预处理文件的位置和.o同目录,这样会更便于查看。

而且这个参数是gcc/clang都支持的。

到这一步,对于所有的CMake+gcc/clang构建系统,都可以方便快捷的生成预处理文件了。

3. ndk-build + Android.mk

但是Android NDK还有legacy NDK构建系统 ndk-build,配合魔改过的Android.mk。这种构建方式支持生成预处理文件么?

既然我们都知道gcc/clang的编译参数-save-temps=obj,那么只要把这个选项设置进c和cxx的编译参数中即可。

Android.mk中LOCAL_CFLAGS/LOCAL_CPPFLAGS和CMake中的CMAKE_C_FLAGS/CMAKE_CXX_FLAGS参数类似,只是 LOCAL_CFLAGS同时对c和cpp起作用,所以我们只需要设置这个就可。

LOCAL_CFLAGS := -save-temps=obj 

此时就可以在hello-jni/app/build/intermediates/ndkBuild/debug/obj/local/arm64-v8a/objs-debug/hello-jni/hello-jni.i找到生成的预处理文件。

到这里,对Android NDK的两种构建系统,我们都可以快速生成预处理文件了。

4. XCode

最后看下在iOS的XCode中,如何查看cpp预处理文件?

XCode中查看预处理文件非常方便和优雅。

选中文件后,只需点击Product/Perform Action,即可看到Preprocess/Assemble,点击执行即可生成。

不过必须选中.cpp才有用, 在选中.h/.hpp时试了都是无效的。

Preprocess/Assemble
Preprocess/Assemble

XCode 生成预编译相当简单,但是在CMake构建系统中摸爬滚打,也让我们找到了非常多的乐趣。

到这里,对于Android、iOS涉及cpp时,生成预处理文件我们都有了方案,探索到此结束,共勉。

参考:

https://www3.ntu.edu.sg/home/ehchua/programming/cpp/gcc_make.html

https://www.cnblogs.com/Wayou/p/macros_in_c_and_cpp.html

https://gcc.gnu.org/onlinedocs/gcc-3.4.0/gcc/Overall-Options.html#Overall%20Options

http://anadoxin.org/blog/generating-preprocessed-sources-in-cmake-projects.html

https://gcc.gnu.org/onlinedocs/gcc-3.4.0/gcc/Debugging-Options.html#Debugging%20Options

https://clang.llvm.org/docs/CommandGuide/clang.html

https://gcc.gnu.org/onlinedocs/gcc-3.4.0/gcc/Option-Summary.html#Option%20Summary

https://clang.llvm.org/docs/index.html#

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、问题缘起
  • 二、定位分析
  • 三、解决方案
    • 1. CMake + make
      • 2. CMake + ninja
        • 3. ndk-build + Android.mk
          • 4. XCode
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档