简体   繁体   English

获取C结构成员列表

[英]Get list of C structure members

Is it possible to get the list of members of a structure as a char ** ? 是否可以将结构成员列表作为char **

For example, something like this: 例如,像这样:

struct mystruct {
    int x;
    float y;
    char *z;
};

/* ... */

char **members = MAGIC(struct mystruct); /* {"x", "y", "z", NULL}. */

I am also interested in compiler-dependent methods. 我也对编译器相关的方法感兴趣。 Is there such a thing ? 有这样的事吗?

Thank you for your time. 感谢您的时间。

Here's a proof of concept: 这是一个概念证明:

#include <stdio.h>
#include <string.h>

#define MEMBER(TYPE,NAME,MORE) TYPE NAME MORE

#define TSTRUCT(NAME,MEMBERS) \
  typedef struct NAME { \
    MEMBERS \
  } NAME; \
  const char* const NAME##_Members = #MEMBERS;

#define PRINT_STRUCT_MEMBERS(NAME) printStructMembers(NAME##_Members)

TSTRUCT(S,
  MEMBER(int,x;,
  MEMBER(void*,z[2];,
  MEMBER(char,(*f)(char,char);,
  MEMBER(char,y;,
  )))));

void printStructMembers(const char* Members)
{
  int level = 0;
  int lastLevel = 0;
  const char* p;
  const char* pLastType = NULL;
  const char* pLastTypeEnd = NULL;

  for (p = Members; *p; p++)
  {
    if (strstr(p, "MEMBER(") == p)
    {
      p += 6; // strlen("MEMBER")
      level++;
      lastLevel = level;
      pLastType = p + 1;
    }
    else if (*p == '(')
    {
      level++;
    }
    else if (*p == ')')
    {
      level--;
    }
    else if (*p == ',')
    {
      if (level == lastLevel)
      {
        if ((pLastType != NULL) && (pLastTypeEnd == NULL))
        {
          pLastTypeEnd = p;
        }
      }
    }
    else if (strstr(p, ";,") == p)
    {
      if ((pLastType != NULL) && (pLastTypeEnd != NULL))
      {
        const char* pp;
        printf("[");
        for (pp = pLastType; pp < pLastTypeEnd; pp++)
          printf("%c", *pp); // print type
        printf("] [");
        for (pp = pLastTypeEnd + 1; pp < p; pp++)
          printf("%c", *pp); // print name
        printf("]\n");
      }
      pLastType = pLastTypeEnd = NULL;
    }
  }
}

char fadd(char a, char b)
{
  return a + b;
}

S s =
{
  1,
  { NULL, NULL },
  &fadd,
  'a'
};

int main(void)
{
  PRINT_STRUCT_MEMBERS(S);
  return 0;
}

This is it's output: 这是它的输出:

[int] [x]
[void*] [z[2]]
[char] [(*f)(char,char)]
[char] [y]

You can improve it to better support more complex member types and to actually build a list of names of the members. 您可以对其进行改进,以更好地支持更复杂的成员类型,并实际构建成员名称列表。

There's definitely no standard way. 绝对没有标准的方法。

If you're willing to compile the code twice, you could have a preprocessor-define-wrapped codepath only enabled for the second pass which reads debugging information from the compilation units produced by the first pass to get the member names. 如果您愿意两次编译代码,则可以为第二次传递启用预处理程序定义包装的代码路径,该传递从第一次传递生成的编译单元读取调试信息以获取成员名称。 You could also analyze the source code to get the list at run time. 您还可以分析源代码以在运行时获取列表。

Finally, you could use preprocessor macros to define the struct and have the macros also emit an entry in another variable for each struct member, effectively keeping two not-directly-related items in sync. 最后,您可以使用预处理器宏来定义结构,并让宏在每个结构成员的另一个变量中发出一个条目,从而有效地保持两个不直接相关的项目同步。

There's no portable standard way of doing this. 这样做没有便携式标准方式。 Last time I wanted to solve a similar problem I used SWIG to produce some XML which I then processed to generate the meta information I wanted. 上次我想解决类似的问题时,我使用SWIG生成一些XML,然后我处理它以生成我想要的元信息。 gcc-xml could do the same thing too. gcc-xml也可以做同样的事情。

Take a look at Metaresc library https://github.com/alexanderchuranov/Metaresc 看看Metaresc库https://github.com/alexanderchuranov/Metaresc

It provides interface for types declaration that will also generate meta-data for the type. 它为类型声明提供了接口,该接口也将为该类型生成元数据。 Based on meta-data you can easily serialize/deserialize objects of any complexity. 基于元数据,您可以轻松地序列化/反序列化任何复杂的对象。 Out of the box you can serialize/deserialize XML, JSON, XDR, Lisp-like notation, C-init notation. 开箱即用,您可以序列化/反序列化XML,JSON,XDR,类似Lisp的表示法,C-init表示法。

Here is a simple example: 这是一个简单的例子:

#include <stdio.h>
#include <stdlib.h>

#include "metaresc.h"

TYPEDEF_STRUCT (sample_t,
                int x,
                float y,
                string_t z
                );

int main (int argc, char * argv[])
{
  mr_td_t * tdp = mr_get_td_by_name ("sample_t");

  if (tdp)
    {
      int i;
      for (i = 0; i < tdp->fields_size / sizeof (tdp->fields[0]); ++i)
        printf ("offset [%zd] size %zd field '%s'\n",
                tdp->fields[i].fdp->offset,
                tdp->fields[i].fdp->size,
                tdp->fields[i].fdp->name.str,
                tdp->fields[i].fdp->type);
    }
  return (EXIT_SUCCESS);
}

This program will output 该程序将输出

$ ./struct
offset [0] size 4 field 'x' type 'int'
offset [4] size 4 field 'y' type 'float'
offset [8] size 8 field 'z' type 'string_t'

Library works fine for latest gcc and clang. 图书馆适用于最新的gcc和clang。

No, that's not possible. 不,那是不可能的。

C is a statically typed language without reflection. C是一种没有反射的静态类型语言。 Type names don't have any meaning past the compilation stage, and it's not even the case that any particular variable is at all visible in the binary code. 类型名称在编译阶段之后没有任何意义,甚至连二进制代码中任何特定变量都不可见的情况也是如此。 The compiler has a lot of freedom to optimize and reorder, as long as the program behaves as described by the language standard. 只要程序的行为符合语言标准,编译器就可以自由地进行优化和重新排序。

You can try some preprocessor magic to get a limited handle on type names, but that's far from general reflection (and strictly speaking outside the C langauge). 您可以尝试一些预处理器魔术来获得类型名称的有限处理,但这远非一般反映(严格来说,在C语言之外)。

The principle thing you cannot do in C is this: 你在C中不能做的主要是:

const char * tn = "int";
auto n = get_type(tn)(42); // a.k.a. "int n = 42;", NOT POSSIBLE

Type names are not runtime concepts; 类型名称不是运行时概念; and above that, static typing makes any such construction impossible. 除此之外,静态类型使任何这样的结构都不可能。

Here's one of the few preprocessor gimmicks I can think of: 这是我能想到的为数不多的预处理器噱头之一:

#define print_size(t) printf("sizeof(" #t ") = %u\n", sizeof(t));

print_size(int);
print_size(long double);

Qt scans the .h files and generated .cpp with function returning such code. Qt扫描.h文件并使用返回此类代码的函数生成.cpp。

You can also achieve this with a bunch of macros, but you need to write them by hand for each types 您也可以使用一堆宏来实现这一点,但是您需要为每种类型手动编写它们

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM