zoukankan      html  css  js  c++  java
  • 动态获取结构体中指定的属性值

    一、前言

      Microsoft Windows中存在大量获取系统属性的API,其中参数很多都是结构体类型,比如获取系统内存信息API:

     1 void main()
     2 {
     3     MEMORYSTATUSEX memInfo{};
     4     memInfo.dwLength = sizeof(memInfo);
     5     ::GlobalMemoryStatusEx(&memInfo);
     6 
     7     std::cout << "Avail Extended Virtual : " << memInfo.ullAvailExtendedVirtual 
     8         << "Avail PageFile : " << memInfo.ullAvailPageFile
     9         << "Avail Phys : " << memInfo.ullAvailPhys 
    10         << "Avail Virtual : " << memInfo.ullAvailVirtual
    11         << "Total PageFile : " << memInfo.ullTotalPageFile 
    12         << "Total Phys : " << memInfo.ullTotalPhys
    13         << "Total Virtual : " << memInfo.ullTotalVirtual 
    14         << std::endl;
    15 }

      这样我们就可以获取到结构体中所有属性的值。但是有时候,我们可能单独提供一个方法,然后传入结构体中我们关心的属性值;那么我们有这个办法吗?^-^

    二、chromium的办法

      这个问题的提出还是我在看chromium源码发现的,第一眼就让我有点蒙蔽,但是清醒的大脑让我意识到这个可能与结构体内存分布有关。先看下chromium提供的方法:

     1 int64_t AmountOfMemory(DWORDLONG MEMORYSTATUSEX::* memory_field) {
     2     MEMORYSTATUSEX memInfo{};
     3     memInfo.dwLength = sizeof(memInfo);
     4 
     5     if (::GlobalMemoryStatusEx(&memInfo)) {
     6 
     7         int64_t rv = static_cast<int64_t>(memInfo.*memory_field);
     8 
     9         return rv < 0 ? std::numeric_limits<uint64_t>::max() : rv;
    10     }
    11 
    12     return 0;
    13 }

      客户端调用代码如下:

    1 int main()
    2 {
    3     auto totalVirtual = AmountOfMemory(&MEMORYSTATUSEX::ullTotalVirtual);
    4     
    5     return 0;
    6 }

      方法的形参和转换方式是不是很特别? 内部原理简单,只是我们几乎没有这么写过。顺便在官方文档上查了MEMORYSTATUSEX结构体定义:

     1 typedef struct _MEMORYSTATUSEX {
     2   DWORD     dwLength;
     3   DWORD     dwMemoryLoad;
     4   DWORDLONG ullTotalPhys;
     5   DWORDLONG ullAvailPhys;
     6   DWORDLONG ullTotalPageFile;
     7   DWORDLONG ullAvailPageFile;
     8   DWORDLONG ullTotalVirtual;
     9   DWORDLONG ullAvailVirtual;
    10   DWORDLONG ullAvailExtendedVirtual;
    11 } MEMORYSTATUSEX, *LPMEMORYSTATUSEX;

      用VS调试并查看形参和结构体对象的地址信息:

       结构体字节为64(40是十六进制)个字节,其中ullTotalVirtual的偏移地址为40(28是十六进制)个字节,长度是8个字节;我们用观察下该对象首地址到偏移地址内存数据是否符合:

       完全一样,函数的返回值也是这个值。通过上面的简单调试,是不是立刻知道这么使用的“讨论”呢?

    三、小试牛刀

      既然我们知道这样实现的方式,不妨也写个简单的demo试试手:

     1 struct Info
     2 {
     3     uint32_t length;
     4     uint32_t a;
     5     uint32_t b;
     6     uint32_t c;
     7 };
     8 
     9 uint32_t GetInfoField(uint32_t Info::* field) {
    10     Info info{};
    11     info.length = sizeof(info);
    12     info.a = 10;
    13     info.b = 20;
    14     info.c = 30;
    15 
    16     return static_cast<uint32_t>(info.*field);
    17 }
    18 
    19 void main()
    20 {
    21    auto a = GetInfoField(&Info::a);
    22    auto b = GetInfoField(&Info::b);
    23    auto c = GetInfoField(&Info::c);
    24 }

      通过上面的方式调试结果都正确获取到了。这里需要注意一点,上面结构体属性变量大小是一样的,如果不一样呢?比如:

     1 struct Info
     2 {
     3     uint32_t length;
     4     uint32_t a;
     5     uint8_t ch;
     6     uint32_t b;
     7     uint64_t temp;
     8     uint32_t c;
     9 };
    10 
    11 uint32_t GetInfoField1(uint32_t Info::* field) {
    12     Info info{};
    13     info.length = sizeof(info);
    14     info.a = 10;
    15     info.ch = 'a';
    16     info.b = 20;
    17     info.temp = 15;
    18     info.c = 30;
    19 
    20     return static_cast<uint32_t>(info.*field);
    21 }
    22 
    23 int main()
    24 {
    25     auto temp = GetInfoField1(&Info::temp); // Error : 这里即使强制转换类型也不行
    26     auto ch = GetInfoField1(&Info::ch);     // Error : 这里即使强制转换类型也不行
    27     
    28     return 0;  
    29 }

      立马就考虑模板函数,形参模板化,函数内部根据模版类型动态推导转换:

     1 template <typename T>
     2 T GetInfoField2(T Info::* field) {
     3     Info info{};
     4     info.length = sizeof(info);
     5     info.a = 10;
     6     info.ch = 'a';
     7     info.b = 20;
     8     info.temp = 15;
     9     info.c = 30;
    10 
    11     return static_cast<T>(info.*field);
    12 }
    13 
    14 int main()
    15 {
    16     auto temp = GetInfoField2(&Info::temp);
    17     auto ch = GetInfoField2(&Info::ch);
    18     
    19     return 0;
    20 }

      完美解决。个人建议在使用Windows API获取类似这种结构体数据指定的值时候,可以适当使用这种方式,但是不建议大量使用;导致其他人接触这种编写代码的风格有点莫名其妙!!

  • 相关阅读:
    Sysinternals Suite
    扩展Visual Studio Test Project:自定义TestClassAttribute
    扩展Visual Studio Test Project:自定义TestClassAttribute
    SQL Server Single-user Mode
    MAXDOP(max degree of parallelism)
    关于log4net
    Go 切片的一种有趣内存泄漏方式
    Go 中的内联优化
    优化 Golang 服务来减少 40% 以上的 CPU
    Go 编译器内部知识:向 Go 添加新语句-第 2 部分
  • 原文地址:https://www.cnblogs.com/smartNeo/p/14530070.html
Copyright © 2011-2022 走看看