windows下C++对象的反射功能

概述

c/c++如果在日志中查看某个结构体/类的每个变量名,变量值信息,只能通过printf逐个格式化,非常繁琐,如何做到类似protobuff转json的序列化功能呢?

该dll库先通过分析pdb文件获取结构体/类的变量名称、变量地址,并将指定的对象序列化成完整json字符串,极大降低了开发者工作量。

demo效果

在release下执行tcdumpTest.exe 看效果

注意:使用前先看‘使用说明.txt’,需要先注册\DIA SDK\bin\msdia120.dll。

  • 定义类、结构体:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    Class Ctest
    {
    Public:
    Int M_i = 1;
    Float M_f = 0.1;
    Double M_d = 0.2;
    Char M_c = 'a';
    Std::String M_str = "Hello World";
    Std::Vector<Int> M_v;
    Std::Map<Int, Std::String> M_map;
    };
  • 赋值:

    1
    2
    3
    4
    5
    6
    Ctest Test;
    Test.M_v.Push_back(1);
    Test.M_v.Push_back(2);
    Test.M_v.Push_back(3);
    Test.M_map.Insert({ 1,"hello" });
    test.m_map.insert({ 2,"world" });
  • 初始化tcDump,并序列化对象

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    int ret = TCDUMP_INIT(R"(..\tcDumpTest.pdb)");
    if (ret == 0) {
    std::cout << "load pdb failed !!!" << std::endl;
    return 0;
    }
    // 打印 test 变量内容,已json格式输出
    auto retJson = TCDUMP(test);
    if (NULL == retJson) {
    return false;
    }
    std::cout << retJson << std::endl;
  • 序列化结果retJson输出

upload successful

  • 附加接口

能够通过已知结构体大小返回大小一致的结构体名称

1
2
3
/ 打印pdb中大小为 n的类和结构体名称
std::cout << tcDump_GetSizeClass(sizeof(CTest)) << std::endl;
输出:---{ CTest }---

实现原理

com组件查找pdb文件

使用visual studio 编译后的c/c++工程在输出目录都会输出xxx.pdb文件,该pdb文件中包含了工程中类型的符号文件,在软件调试过程中非常重要。pdb文件格式的解析依赖com组件msdia120.dll,在成功注册msdia120.dll组件后,通过提供的方法对pdb文件进行解析:

1
2
3
4
5
6
7
8
9
10
11
12
13
HRESULT hr = ::CoInitialize(NULL);
{
CComPtr<IDiaDataSource> pDiaDataSource;
CComPtr<IDiaSession> pDiaSession;
CComPtr<IDiaSymbol> pGlobalSymbol;
CComBSTR bstrFilename = pdb_path;
if (LoadDataFromPdb(bstrFilename, pDiaDataSource, pDiaSession, pGlobalSymbol)) {
LoadAllUDTs(pGlobalSymbol);//解析所有数据类型,保存在全局变量g_map_udt中
LoadAllEnums(pGlobalSymbol);
ret = 1;
}
}
::CoUninitialize();

对变量进行序列化

1
2
#define TCDUMP(ref_var)            \
tcDump(typeid(ref_var).name(), &(ref_var))

通过输入变量地址,通过关键字typeid获取变量类型,按照不同的变量类型进行不同方式的处理,比如已内置变量类型为列:

1
2
3
if (dump_as_builtin(type, ptr, root)) {
return true;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
bool dump_inv_as_builtin(const std::string& type, void* ptr, const Json::Value& root) 
{
static const char* buildin_signed[] = {
"char",
"wchar_t",
"signed char",
"int",
"short",
"long",
"__int8",
"__int16",
"__int32",
"__int64",
};
static const char* buildin_unsigned[] = {
"unsigned char",
"unsigned short",
"unsigned int",
"unsigned long",
"unsigned __int8",
"unsigned __int16",
"unsigned __int32",
"unsigned __int64"
};
if (type == "bool") {
*(bool*)ptr = root.asBool();
return true;
}
if (type == "float") {
*(float*)ptr = root.asFloat();
return true;
}
if (type == "double") {
*(double*)ptr = root.asDouble();
return true;
}
for (auto t : buildin_signed) {
if (type == t) {
switch (type_size(type))
{
case 1:
*(int8_t*)ptr = root.asInt();
return true;
case 2:
*(int16_t*)ptr = root.asInt();
return true;
case 4:
*(int32_t*)ptr = root.asInt();
return true;
case 8:
*(int64_t*)ptr = root.asInt64();
return true;
default:
return false;
}
}
}
for (auto t : buildin_unsigned) {
if (type == t) {
switch (type_size(type))
{
case 1:
*(uint8_t*)ptr = root.asUInt();
return true;
case 2:
*(uint16_t*)ptr = root.asUInt();
return true;
case 4:
*(uint32_t*)ptr = root.asUInt();
return true;
case 8:
*(uint64_t*)ptr = root.asUInt64();
return true;
default:
return false;
}
}
}
return false;
}

源码下载地址

https://download.csdn.net/download/corrupt/82779744