探索C++头文件解析方法
最近一直在搞基于SWIG的C++接口翻译Java代码的工作。SWIG内部基于Bison(Yacc)的C/C++解析器,最近纠结于SWIG不能解析C++构造函数中的默认初始化赋值操作,想找一个能够补充此项能力的工具。
尝试了Cast-xml,因为官网上说编译需要依赖llvm+clang,结果浪费我半天的时间去研究怎么编译llvm+clang,耗费巨大的磁盘空间(12GB才到70%)作罢。后来发现Ubuntu上可以直接安装编译好的Cast-xml,试了一把发现解析出来的AST(抽象语法树)根本就没有初始值的相关的内容,只有大量的符号表之类的。坑~~幸亏没有在编译llvm+clang的路上一根筋搞下去。
又尝试了好几个cpp开源库发现也不行,最后找到了一个名为 CppHeaderParser (可pip安装)的Python库,用起来倒是非常简单, 也能够分析头文件并拿到函数原型,非常接近我需要的目标了!可万万没想到居然不解析函数体内容,功亏一篑啊。。。
例如这样一个头文件:
#ifndef _TEST_H
#define _TEST_H #include <string> class MyClass {
public:
MyClass() : _iValue(), _fValue(3.14) {
_strValue = "Hello";
} int GetIValue() const; private:
int _iValue;
float _fValue;
std::string _strValue;
}; #endif
用 CppHeaderParser 解析出来的信息为:
class MyClass
{
public
// Methods
{'line_number': 8, 'parent': {'inherits': [], 'line_number': 6, 'forward_declares': {'protected': [], 'public': [], 'private': []}, 'name': 'MyClass', 'parent': None, 'abstract': False, 'namespace': '', 'declaration_method': 'class', 'properties': {'protected': [], 'public': [], 'private': [{'line_number': 18, 'constant': 0, 'reference': 0, 'raw_type': 'int', 'static': 0, 'array': 0, 'pointer': 0, 'aliases': [], 'typedef': None, 'namespace': '', 'function_pointer': 0, 'mutable': False, 'type': 'int', 'property_of_class': 'MyClass', 'parent': None, 'ctypes_type': 'ctypes.c_int', 'typedefs': 0, 'extern': False, 'class': 0, 'unresolved': False, 'name': '_iValue', 'fundamental': True}, {'line_number': 19, 'constant': 0, 'reference': 0, 'raw_type': 'float', 'static': 0, 'array': 0, 'pointer': 0, 'aliases': [], 'typedef': None, 'namespace': '', 'function_pointer': 0, 'mutable': False, 'type': 'float', 'property_of_class': 'MyClass', 'parent': None, 'ctypes_type': 'ctypes.c_float', 'typedefs': 0, 'extern': False, 'class': 0, 'unresolved': False, 'name': '_fValue', 'fundamental': True}, {'line_number': 20, 'constant': 0, 'reference': 0, 'raw_type': 'std::string', 'static': 0, 'array': 0, 'pointer': 0, 'aliases': ['std::string'], 'typedef': None, 'namespace': '', 'function_pointer': 0, 'mutable': False, 'type': 'std::string', 'property_of_class': 'MyClass', 'parent': None, 'ctypes_type': 'ctypes.c_void_p', 'typedefs': 0, 'extern': False, 'class': 0, 'unresolved': True, 'name': '_strValue', 'fundamental': 0}]}, 'typedefs': {'protected': [], 'public': [], 'private': []}, 'structs': {'protected': [], 'public': [], 'private': []}, 'enums': {'protected': [], 'public': [], 'private': []}, 'final': False, 'nested_classes': [], 'methods': {'protected': [], 'public': [{...}, {'line_number': 15, 'parent': {...}, 'defined': False, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'int', 'extern': False, 'path': 'MyClass', 'returns_pointer': 0, 'parameters': [], 'class': None, 'returns_reference': False, 'const': True, 'name': 'GetIValue', 'pure_virtual': False, 'debug': '\t int GetIValue ( ) const ;', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': 'int', 'template': False, 'constructor': False, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}], 'private': []}}, 'defined': True, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'void', 'extern': False, 'path': 'MyClass', 'returns_pointer': 0, 'parameters': [], 'class': None, 'returns_reference': False, 'const': False, 'name': 'MyClass', 'pure_virtual': False, 'debug': '\t MyClass ( ) : \t _iValue ( 123 ) , \t _fValue ( 3.16 ) \t {', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': '', 'template': False, 'constructor': True, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}
{'line_number': 15, 'parent': {'inherits': [], 'line_number': 6, 'forward_declares': {'protected': [], 'public': [], 'private': []}, 'name': 'MyClass', 'parent': None, 'abstract': False, 'namespace': '', 'declaration_method': 'class', 'properties': {'protected': [], 'public': [], 'private': [{'line_number': 18, 'constant': 0, 'reference': 0, 'raw_type': 'int', 'static': 0, 'array': 0, 'pointer': 0, 'aliases': [], 'typedef': None, 'namespace': '', 'function_pointer': 0, 'mutable': False, 'type': 'int', 'property_of_class': 'MyClass', 'parent': None, 'ctypes_type': 'ctypes.c_int', 'typedefs': 0, 'extern': False, 'class': 0, 'unresolved': False, 'name': '_iValue', 'fundamental': True}, {'line_number': 19, 'constant': 0, 'reference': 0, 'raw_type': 'float', 'static': 0, 'array': 0, 'pointer': 0, 'aliases': [], 'typedef': None, 'namespace': '', 'function_pointer': 0, 'mutable': False, 'type': 'float', 'property_of_class': 'MyClass', 'parent': None, 'ctypes_type': 'ctypes.c_float', 'typedefs': 0, 'extern': False, 'class': 0, 'unresolved': False, 'name': '_fValue', 'fundamental': True}, {'line_number': 20, 'constant': 0, 'reference': 0, 'raw_type': 'std::string', 'static': 0, 'array': 0, 'pointer': 0, 'aliases': ['std::string'], 'typedef': None, 'namespace': '', 'function_pointer': 0, 'mutable': False, 'type': 'std::string', 'property_of_class': 'MyClass', 'parent': None, 'ctypes_type': 'ctypes.c_void_p', 'typedefs': 0, 'extern': False, 'class': 0, 'unresolved': True, 'name': '_strValue', 'fundamental': 0}]}, 'typedefs': {'protected': [], 'public': [], 'private': []}, 'structs': {'protected': [], 'public': [], 'private': []}, 'enums': {'protected': [], 'public': [], 'private': []}, 'final': False, 'nested_classes': [], 'methods': {'protected': [], 'public': [{'line_number': 8, 'parent': {...}, 'defined': True, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'void', 'extern': False, 'path': 'MyClass', 'returns_pointer': 0, 'parameters': [], 'class': None, 'returns_reference': False, 'const': False, 'name': 'MyClass', 'pure_virtual': False, 'debug': '\t MyClass ( ) : \t _iValue ( 123 ) , \t _fValue ( 3.16 ) \t {', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': '', 'template': False, 'constructor': True, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}, {...}], 'private': []}}, 'defined': False, 'namespace': '', 'operator': False, 'static': False, 'returns_fundamental': True, 'rtnType': 'int', 'extern': False, 'path': 'MyClass', 'returns_pointer': 0, 'parameters': [], 'class': None, 'returns_reference': False, 'const': True, 'name': 'GetIValue', 'pure_virtual': False, 'debug': '\t int GetIValue ( ) const ;', 'explicit': False, 'virtual': False, 'destructor': False, 'returns': 'int', 'template': False, 'constructor': False, 'override': False, 'inline': False, 'final': False, 'friend': False, 'returns_class': False}
protected
private
// Properties
{'line_number': 18, 'constant': 0, 'reference': 0, 'raw_type': 'int', 'static': 0, 'array': 0, 'pointer': 0, 'aliases': [], 'typedef': None, 'namespace': '', 'function_pointer': 0, 'mutable': False, 'type': 'int', 'property_of_class': 'MyClass', 'parent': None, 'ctypes_type': 'ctypes.c_int', 'typedefs': 0, 'extern': False, 'class': 0, 'unresolved': False, 'name': '_iValue', 'fundamental': True}
{'line_number': 19, 'constant': 0, 'reference': 0, 'raw_type': 'float', 'static': 0, 'array': 0, 'pointer': 0, 'aliases': [], 'typedef': None, 'namespace': '', 'function_pointer': 0, 'mutable': False, 'type': 'float', 'property_of_class': 'MyClass', 'parent': None, 'ctypes_type': 'ctypes.c_float', 'typedefs': 0, 'extern': False, 'class': 0, 'unresolved': False, 'name': '_fValue', 'fundamental': True}
{'line_number': 20, 'constant': 0, 'reference': 0, 'raw_type': 'std::string', 'static': 0, 'array': 0, 'pointer': 0, 'aliases': ['std::string'], 'typedef': None, 'namespace': '', 'function_pointer': 0, 'mutable': False, 'type': 'std::string', 'property_of_class': 'MyClass', 'parent': None, 'ctypes_type': 'ctypes.c_void_p', 'typedefs': 0, 'extern': False, 'class': 0, 'unresolved': True, 'name': '_strValue', 'fundamental': 0}
}
确实有构造函数的初始化列表的内容,但是少了构造函数体中的赋值操作。总不好意思去要求所有人都必须用初始化列表来初始化吧?更何况确实有赋值语句给初值的情况。。
谁有更好的开源库方法?多谢!
==========================================
看来看去,感觉只有clang最靠谱最有希望。快来看 LLVM 官方文档,既可以下载源码,也可以下载编译好的包,非常靠谱!
要利用clang来解析C++语法树,感觉还是尽可能用Python接口吧,方便啊,转下一篇《利用Clang(Python接口)来解析C++》
1 参考LLVM介绍
引用博文《LLVM原理和使用》
在理解LLVM时,我们可以认为它包括了一个狭义的LLVM和一个广义的LLVM。广义的LLVM其实就是指整个LLVM编译器架构,包括了前端、后端、优化器、众多的库函数以及很多的模块;而狭义的LLVM其实就是聚焦于编译器后端功能(代码生成、代码优化、JIT等)的一系列模块和库。
1.1 LLVM三段式架构
(1)传统编译器的三段式:前端(Frontend)-- 优化器(Optimizer)-- 后端(Backend)
(2)LLVM的三段式:
前端可以使用不同的编译工具对代码文件做词法分析以形成抽象语法树AST,然后将分析好的代码转换成LLVM的中间表示IR(intermediate representation);中间部分的优化器只对中间表示IR操作,通过一系列的pass对IR做优化;后端负责将优化好的IR解释成对应平台的机器码。LLVM的优点在于,中间表示IR代码编写良好,而且不同的前端语言最终都转换成同一种的IR。
1.2 Clang与LLVM的关系
Clang是一个C++编写、基于LLVM、发布于LLVM BSD许可证下的C/C++/Objective-C/Objective-C++编译器。那么为什么已经有了GCC还要开发Clang呢?Clang相比于GCC有什么优势呢? 其实,这也是Clang当初在设计开发的时候所主要考虑的原因。Clang是一个高度模块化开发的轻量级编译器,它的编译速度快、占用内存小、非常方便进行二次开发。
LLVM与Clang是C/C++编译器套件。对于整个LLVM的框架来说,包含了Clang,因为Clang是LLVM的框架的一部分,是它的一个C/C++的前端。Clang使用了LLVM中的一些功能,目前知道的就是针对中间格式代码的优化,或许还有一部分生成代码的功能。从源代码角度来讲,clang是基于LLVM的一个工具。而功能的角度来说,LLVM可以认为是一个编译器的后端,而clang是一个编译器的前端。
1.3 LLVM 编译流程
LLVM编译一个源文件的过程:预处理 -> 词法分析 -> Token -> 语法分析 -> AST -> 代码生成 -> LLVM IR -> 优化 -> 生成汇编代码 -> Link -> 目标文件。
2 CLang工具命令
2.1 打印语法树
可以用如下命令打印语法树:
clang -Xclang -ast-dump -fsyntax-only -Iinclude -x c++ test.h > out.txt
2.2 打印分词(词法分析)
如下命令:
clang -Xclang -dump-tokens -Iinclude test.h > out2.txt
2.3 打印语法分析(语法分析)
如下命令:
clang -fsyntax-only -Xclang -ast-dump -x c++ -Iinclude test.h > out3.txt
探索C++头文件解析方法的更多相关文章
- NSObject头文件解析 / 消息机制 / Runtime解读 (一)
NSObject头文件解析 当我们需要自定义类都会创建一个NSObject子类, 比如: #import <Foundation/Foundation.h> @interface Clas ...
- NSObject头文件解析 / 消息机制 / Runtime解读 (二)
本章接着NSObject头文件解析 / 消息机制 / Runtime解读(一)写 给类添加属性: BOOL class_addProperty(Class cls, const char *name, ...
- 八、Android学习第七天——XML文件解析方法(转)
(转自:http://wenku.baidu.com/view/af39b3164431b90d6c85c72f.html) 八.Android学习第七天——XML文件解析方法 XML文件:exten ...
- Spring的配置文件ApplicationContext.xml配置头文件解析
Spring的配置文件ApplicationContext.xml配置头文件解析 原创 2016年12月16日 14:22:43 标签: spring配置文件 5446 spring中的applica ...
- linux(kali,centos)安装vm及其提示缺少c头文件解决方法
我电脑系统是kali最新版 首先去官网下一个vm安装包,给个直达网址 http://www.vmware.com/cn/products/workstation/workstation-evaluat ...
- 在Eclipse中设置进行JNI的头文件编译方法(转 http://blog.csdn.net/mirkerson/article/details/17187109)
这两天在搞NDK开发,JNI的头文件进行编译的时候,要跑到对应的class文件路径下(通常是工程的bin目录),进行编译生成,很是不便,也容易出错,所以考虑在Eclipse中作为外部工具引入,所以便查 ...
- MSP430G2553头文件解析
MSP430寄存器中文注释---P1/2口(带中断功能) /************************************************************ ...
- TC297B - 外设头文件解析(以IO为例)
打开例程,目录树下的Includes中包含了各个片上资源对应的头文件,这些头文件定义了相应外设的寄存器地址(寄存器是内置于各个 IP 外设中,是一种用于配置外设功能的存储器,就是一种内存,并且有相对应 ...
- C/C++不同文件夹下包含头文件的方法及#include的使用
转自:http://blog.sina.com.cn/s/blog_6e0693f70100so42.html 本文主要介绍了如何不同文件夹下使用预处理器指示符#include. 假设我们有如下一个工 ...
随机推荐
- shell版的nginx安装
#!/bin/bash # Name:Centos 6.4 安装nginx1.8.1 # Date:-- # Author:qifei@meizu.com Home=$(cd ``;pwd) #这个命 ...
- LOJ 10002 喷水装置 未完
#include<bits/stdc++.h> using namespace std; int T,n,l,w; ; struct S{ double s,t; }a[maxn]; bo ...
- python实现三级菜单间的前后跳转
#录入菜单def createMenu(): Menu = {} provinces = ["陕西","四川","江苏"] sh ...
- java如何快速创建List
几个快速添加list的方法 1. 使用Collections.addAll()方法,前提还是需要手动 new ArrayList ArrayList<String> s = new Arr ...
- 神州数码DHCP及DHCP中继配置
实验要求:掌握DHCP及DHCP中继配置方法 拓扑如下 R1 enable 进入特权模式 config 进入全局模式 hostname R1 修改名称 interface g0/5 进入端口 ip a ...
- ThreadPoolExecutor 几个疑惑与解答
任务是否都要先放入队列? 当工作线程数小于核心线程数时,任务是不会经过队列,而是直接创建 Worker 时传入.但是如果工作线程数已经大于核心线程数,则任务是要先放入队列的.实际上只要是被创建的工作线 ...
- jupyter notebook添加虚拟环境
原本以为,当进入虚拟环境之后,再运行jupyter notebook应该是这个环境下的jupyter,比如我默认创建一个文件,这个文件调用的编译器应该是这个虚拟环境中的编译器,实际上并不是 当你进入j ...
- 利用JQuery 解析MVC控制器传到前台的Json数据
<script type="text/javascript"> function Getweb() { var name = $("#CityName&quo ...
- 在typeScript+vue项目中使用ref
因为vue项目是无法直接操作dom的,但是有时候开发需求迫使我们去操作dom. 两个办法,一个是很low的再引入jq,然后通过jq来操作,但是这样就失去了我们使用vue的意义, 可惜的是我曾经这样干过 ...
- JavaScript 之 预编译 作用域,作用域链
第一次写博客,本来是学习jQuery遇到闭包问题,发现并没有理解闭包,发现闭包牵扯的知识点太多.复习了一遍(发现自己该记住的全忘了)写在博客里,自己也是小白,希望大神们指点迷津,必将感激不尽. 我们知 ...