获取C结构成员的列表

qybjjes1  于 2023-01-16  发布在  其他
关注(0)|答案(6)|浏览(199)

是否可以将结构的成员列表作为char **来获取?
例如,像这样的事情:

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

/* ... */

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

我对编译器相关的方法也很感兴趣,有这种东西吗?
谢谢你抽出时间。

xpszyzbs

xpszyzbs1#

以下是概念证明:

#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;
}

这是它的输出:

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

您可以对其进行改进,以更好地支持更复杂的成员类型,并实际构建成员名称列表。

q3aa0525

q3aa05252#

绝对没有标准的方法。
如果你想编译代码两次,你可以只在第二次编译时启用预处理器定义 Package 代码路径,它从第一次编译产生的编译单元读取调试信息,以获取成员名称。你也可以在运行时分析源代码以获取列表。
最后,您可以使用预处理器宏来定义结构,并让宏在另一个变量中为每个结构成员发出一个条目,从而有效地使两个不直接相关的项保持同步。

u4vypkhs

u4vypkhs3#

查看Metaresc库https://github.com/alexanderchuranov/Metaresc
它为类型声明提供了接口,也为类型生成元数据。基于元数据,你可以轻松地序列化/反序列化任何复杂的对象。开箱即用,你可以序列化/反序列化XML,JSON,XDR,Lisp类符号,C-init符号。
下面是一个简单的例子:

#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 (NULL == tdp)
     return (EXIT_FAILURE);

   int i;
   for (i = 0; i < tdp->fields_size / sizeof (tdp->fields[0]); ++i)
     MR_PRINT ("offset [", tdp->fields[i].fdp->offset,
               "] size ", tdp->fields[i].fdp->size,
               " field '", tdp->fields[i].fdp->name.str,
               "' type '", tdp->fields[i].fdp->type,
               "' mr_type ", (mr_type_t, &tdp->fields[i].fdp->mr_type)
               );

  return (EXIT_SUCCESS);
}

此程序将输出

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

库对最新的gcc和clang工作正常。

lkaoscv7

lkaoscv74#

没有可移植的标准方法来实现这一点。上次我想解决一个类似的问题,我使用SWIG生成一些XML,然后处理这些XML以生成我想要的 meta信息。gcc-xml也可以做同样的事情。

vhmi4jdf

vhmi4jdf5#

不,那不可能。
C是一种没有反射的静态类型语言。类型名在编译阶段之后没有任何意义,甚至在二进制代码中任何特定的变量都是不可见的。编译器有很大的自由来优化和重新排序,只要程序按照语言标准的描述运行。
您可以尝试一些预处理器魔法来获得类型名称的有限句柄,但这与一般反射相去甚远(严格地说,在C语言之外)。
在C语言中你不能做的主要事情是:

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

类型名 * 不是 * 运行时概念;更重要的是,静态类型使得任何这样的构造都不可能。
下面是我能想到的少数几个预处理器技巧之一:

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

print_size(int);
print_size(long double);
7fhtutme

7fhtutme6#

Qt扫描.h文件并生成.cpp,函数返回此类代码。
您也可以使用一组宏来实现这一点,但是您需要为每种类型手工编写宏

相关问题