• windows下C++的反射功能


    概述

    c/c++如果在日志中查看某个结构体/类的每个变量名,变量值信息,只能通过printf逐个格式化,非常繁琐,如何做到类似protobuff转json的序列化功能呢?该dll库先通过分析pdb文件获取结构体/类的变量名称、变量地址,并序列化成完整json字符串,极大降低了开发者工作量。

    详细

    概述

    通过pdb文件查找指定结构体的变量命名和变量地址,将指定的结构体变量内容序列话为json格式。

    实现原理

    com组件查找pdb文件

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

    1. HRESULT hr = ::CoInitialize(NULL);
    2. {
    3. CComPtr<IDiaDataSource> pDiaDataSource;
    4. CComPtr<IDiaSession> pDiaSession;
    5. CComPtr<IDiaSymbol> pGlobalSymbol;
    6. CComBSTR bstrFilename = pdb_path;
    7. if (LoadDataFromPdb(bstrFilename, pDiaDataSource, pDiaSession, pGlobalSymbol)) {
    8. LoadAllUDTs(pGlobalSymbol);//解析所有数据类型,保存在全局变量g_map_udt中
    9. LoadAllEnums(pGlobalSymbol);
    10. ret = 1;
    11. }
    12. }
    13. ::CoUninitialize();
    对变量进行序列化
    1. #define TCDUMP(ref_var) \
    2. tcDump(typeid(ref_var).name(), &(ref_var))

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

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

    demo效果:

    在release下执行tcdumpTest.exe 看效果

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

    定义类、结构体:
    1. Class Ctest
    2. {
    3. Public:
    4. Int M_i = 1;
    5. Float M_f = 0.1;
    6. Double M_d = 0.2;
    7. Char M_c = 'a';
    8. Std::String M_str = "Hello World";
    9. Std::Vector<Int> M_v;
    10. Std::Map<Int, Std::String> M_map;
    11. };
    赋值:
    1. Ctest Test;
    2. Test.M_v.Push_back(1);
    3. Test.M_v.Push_back(2);
    4. Test.M_v.Push_back(3);
    5. Test.M_map.Insert({ 1,"hello" });
    6. test.m_map.insert({ 2,"world" });
    初始化tcDump,并序列化对象
    1. int ret = TCDUMP_INIT(R"(..\tcDumpTest.pdb)");
    2. if (ret == 0) {
    3. std::cout << "load pdb failed !!!" << std::endl;
    4. return 0;
    5. }
    6. // 打印 test 变量内容,已json格式输出
    7. auto retJson = TCDUMP(test);
    8. if (NULL == retJson) {
    9. return false;
    10. }
    11. std::cout << retJson << std::endl;
    序列化结果retJson输出

    结果输出

    附加接口

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

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

    工程目录

    -tcdump
    -tcdumpTest

    tcdump为dll工程,核心代码实现。
    tcpdumpTest为测试工程。

    注意点:

    1、本工程基于windows,不支持linux
    2、本工程默认支持vs2019,其他版本可以自行编译。
    3、使用vs2019编译时,会提示map _Node 错误,只需把xtree文件中对应的 _Node 从protected修改为public,并重新编译即可
    4、使用前先看‘使用说明.txt’,需要先注册\DIA SDK\bin\msdia120.dll。
  • 相关阅读:
    Python面试高频问题:self到底是什么
    【AI】如何在Pycharm中使用AI辅助Coding工具Aws Toolkit
    【文件上传】upload-labs 通关
    Java基础10——日期和时间
    【Python机器学习】自动化特征选择——迭代特征选择
    HDFS 伪分布式环境搭建
    Linux 网络编程项目 —— FTP 网盘
    Salesforce-Visualforce-3.变量和公式(Variables and Formulas)
    springboot水环境检测系统的设计与实现毕业设计源码041446
    【Linux】线程池 | 自旋锁 | 读写锁
  • 原文地址:https://blog.csdn.net/hanjiepo/article/details/132927923