如何在C++中通过编程获取类的完整类型信息(Debug环境)
在非IDE环境下,我们无法获得IntelliSense支持,且有时也没有现成的方法/API文档。这种情况下,若能获取所用底层类的类型信息会很有帮助,类似Python中的help(type)功能。
以下为示例程序:
#include<iostream> class Add { private: int first; int second; public: Add(int a, int b): first(a), second(b) {} ~Add() { } int PerformAdd(){ int sum {0}; sum = first + second; return sum; } }; int main() { int m = 10; int n = 20; Add ad(m,n); std::cout << "Addition Of " << m << " and " << n << " is: " << ad.PerformAdd() << std::endl; }
当使用GDB调试该Debug版本代码时,通过ptype命令可获取Add类的完整类型信息:
Breakpoint 1, main () at Program.cpp:19 19 int m = 10; (gdb) n 20 int n = 20; (gdb) 22 Add ad(m,n); (gdb) 23 std::cout << "Addition Of " << m << " and " << n << " is: " << ad.PerformAdd() << std::endl; (gdb) whatis ad type = Add (gdb) ptype Add type = class Add { private: int first; int second; public: Add(int, int); ~Add(int); int PerformAdd(void); } (gdb)
现在的问题是:当拥有Debug二进制文件时,是否能通过编程方式打印类的类型信息或相近内容?这在无网络的非IDE终端环境中会很实用,就像在Python中常用help(type)排查问题、了解类功能一样。
当然可以通过编程方式从Debug二进制文件中提取类的类型信息,以下是几种实用的方法:
1. 利用GDB批处理模式
GDB支持非交互式的批处理运行,你可以编写脚本让GDB自动加载二进制文件并输出指定类的类型信息,无需手动进入调试会话。
比如创建一个gdb_script.gdb文件:
file your_debug_binary ptype Add quit
然后在终端执行:
gdb -x gdb_script.gdb
这样就能直接输出Add类的完整类型信息,完全适配无网络的终端环境。
如果需要动态指定要查询的类,可以写一个shell脚本封装:
#!/bin/bash if [ $# -ne 2 ]; then echo "Usage: $0 <binary_file> <class_name>" exit 1 fi gdb -batch -ex "file $1" -ex "ptype $2"
保存为class_info.sh并赋予执行权限后,直接运行./class_info.sh your_debug_binary Add即可得到结果。
2. 解析DWARF调试信息
Debug版本的二进制文件包含DWARF格式的调试信息,你可以用专门工具直接解析:
- objdump(GNU binutils工具集):
该命令会过滤出和objdump --dwarf=info your_debug_binary | grep -A 20 "Add"Add类相关的调试信息,输出格式虽比GDB的ptype原始,但包含所有类成员和方法的细节。 - readelf(同属binutils工具):
readelf --debug-dump=info your_debug_binary | grep -A 30 "Add"
如果需要更结构化的输出,可使用pyelftools(Python库)编写脚本解析DWARF信息,自定义输出类的成员、方法等内容:
from elftools.elf.elffile import ELFFile from elftools.dwarf.die import DIE def find_class_die(dwarf_info, class_name): for CU in dwarf_info.iter_CUs(): for DIE in CU.iter_DIEs(): if DIE.tag == 'DW_TAG_class_type' and DIE.attributes.get('DW_AT_name').value.decode() == class_name: return DIE return None def print_class_info(dwarf_info, die): class_name = die.attributes['DW_AT_name'].value.decode() print(f"class {class_name} {{") # 遍历子节点解析成员和方法 for child in die.iter_children(): if child.tag == 'DW_TAG_member': member_name = child.attributes['DW_AT_name'].value.decode() type_die = dwarf_info.get_DIE_from_refaddr(child.attributes['DW_AT_type'].value) member_type = type_die.attributes['DW_AT_name'].value.decode() # 权限判断(简化处理,实际需读取DW_AT_accessibility) print(f" private:") print(f" {member_type} {member_name};") elif child.tag == 'DW_TAG_subprogram': func_name = child.attributes['DW_AT_name'].value.decode() print(f" public:") print(f" {func_name}(...);") print("}") with open('your_debug_binary', 'rb') as f: elf_file = ELFFile(f) if elf_file.has_dwarf_info(): dwarf_info = elf_file.get_dwarf_info() class_die = find_class_die(dwarf_info, 'Add') if class_die: print_class_info(dwarf_info, class_die) else: print("Class not found")
若处于离线环境,可提前打包pyelftools库带到目标机器安装使用。
3. 自定义编译时生成类型信息
如果可以修改源代码,可利用C++特性手动生成类的类型信息并输出。比如给Add类添加静态方法:
#include <iostream> class Add { private: int first; int second; public: Add(int a, int b): first(a), second(b) {} ~Add() { } int PerformAdd(){ int sum {0}; sum = first + second; return sum; } static void PrintClassInfo() { std::cout << "class Add {" << std::endl; std::cout << " private:" << std::endl; std::cout << " int first;" << std::endl; std::cout << " int second;" << std::endl; std::cout << " public:" << std::endl; std::cout << " Add(int, int);" << std::endl; std::cout << " ~Add();" << std::endl; std::cout << " int PerformAdd();" << std::endl; std::cout << "}" << std::endl; } }; int main() { // 调用静态方法打印类信息 Add::PrintClassInfo(); int m = 10; int n = 20; Add ad(m,n); std::cout << "Addition Of " << m << " and " << n << " is: " << ad.PerformAdd() << std::endl; }
这种方法需手动维护类型信息,适合快速查看特定类的场景,但扩展性较差。
内容的提问来源于stack exchange,提问作者Mantosh Kumar

