是否可以将结构体的成员列表作为char **
?
例如:
struct mystruct {
int x;
float y;
char *z;
};
/* ... */
char **members = MAGIC(struct mystruct); /* {"x", "y", "z", NULL}. */
我也对依赖编译器的方法感兴趣。有这样的事吗?
感谢您的宝贵时间。
这是一个概念证明:
#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]
您可以改进它以更好地支持更复杂的成员类型,并实际构建成员名称列表。
绝对没有标准的方法。
如果您愿意编译代码两次,您可以为第二次编译启用预处理器定义包装的代码路径,它从第一次编译产生的编译单元中读取调试信息,以获得成员名。您还可以分析源代码以在运行时获得列表。
最后,您可以使用预处理器宏来定义结构体,并让宏在另一个变量中为每个结构体成员发出一个条目,从而有效地保持两个不直接相关的项同步。
看看Metaresc库https://github.com/alexanderchuranov/Metaresc
它提供了类型声明的接口,该接口还将为类型生成元数据。基于元数据,您可以轻松地序列化/反序列化任何复杂性的对象。开箱可用,可以序列化/反序列化XML、JSON、YAML、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]->offset,
"] size ", tdp->fields[i]->size,
" field '", tdp->fields[i]->name.str,
"' type '", tdp->fields[i]->type,
"' mr_type ", (mr_type_t, &tdp->fields[i]->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
没有可移植的标准方法来做到这一点。上次我想解决类似的问题时,我使用SWIG生成了一些XML,然后对这些XML进行处理以生成我想要的元信息。
不,那是不可能的。
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 ") = %un", sizeof(t));
print_size(int);
print_size(long double);
Qt扫描。h文件并生成。cpp函数返回这样的代码。
您也可以使用一堆宏来实现这一点,但您需要为每种类型手工编写