c - 获取成员定义名称给定成员定义地址?
问题描述
给定结构成员变量的地址,是否可以获取成员变量的名称并将其存储在字符指针中?
#include <stdio.h>
struct struct_name{
char char_1;
char char_2;
};
int main(){
struct custom_struct struct_name;
char *member_name = NULL;
member_name = getName( struct_name + 1); // member_name = "char_2"
return 0;
}
解决方案
AFAIK C 本身并不支持反射。
所以如果你真的想要它,你需要自己做。您可以做到这一点的一种方法(这个解决方案的可接受程度取决于您)是通过预处理器。
使用全局变量
这里的主要思想是每个结构都有关联的 2 个全局常量:一个指定结构的字段数(我们不需要它,但如果您想遍历所有字段名称,这可能很有用)和一个表示字段名称。要自动执行此操作,您需要牺牲定义结构的方式。
这里的解决方案有点依赖 GCC(我们将使用##
变体),但它应该很容易移植。
我还在使用P99 项目来帮助我更轻松地执行宏处理。
起点是如何定义一个结构:
//variadic a comma separated list of field type and field name
//example: DEFINE_STRUCT(foo, char, char_1, char char_2)
#define DEFINE_STRUCT(structName, ...) \
static const int P99_PASTE(_, structName, _, fieldCount) = P99_DIV(P99_NARG(__VA_ARGS__), 2); \
static const char* P99_PASTE(_, structName, _, fieldNames)[] = { _GENERATE_FIELDS_NAME(structName, __VA_ARGS__) }; \
\
struct structName { \
_GENERATE_STRUCT_FIELDS(structName, __VA_ARGS__); \
}
基本上,当调用 DEFINE_STRUCT 时,我们将生成 2 个全局(静态)常量。在示例中,它们将被称为_struct_name_fieldCount
and _struct_name_fieldNames
。静态并不是真正必要的,如果您想查询翻译单元之外的反射,它可能会很糟糕。
第一个常数很容易产生。至于第二个常量,我们需要遍历“类型字段 - 类型名称”对:
#define _METADATA_REDUCE(NAME, I, REC, RES) REC, RES
#define _METADATA_MAP(context, length, type, value) #value
#define _GENERATE_FIELDS_NAME(structName, ...) FOR_PAIR(, _METADATA_REDUCE, _METADATA_MAP, ## __VA_ARGS__)
FOR_PAIR
宏是我们需要定义的宏:遗憾的是,P99 只允许您一个一个地循环可变参数。但是我们需要循环使用 step 2 的可变参数。所以我们定义了这样的宏(例如我允许最多 5 个字段,但是这个限制可以很容易地更新购买添加更多的宏定义):
#define FOR_PAIR(CONTEXT, OP, FUNC, ...) P99_PASTE2(_BASE_FOR_PAIR_, P99_NARG(__VA_ARGS__))(CONTEXT, OP, FUNC, ## __VA_ARGS__)
#define _BASE_FOR_PAIR_2(CONTEXT, OP, FUNC, value1, value2) FUNC(CONTEXT, 1, value1, value2)
#define _BASE_FOR_PAIR_4(CONTEXT, OP, FUNC, value1, value2, ...) OP(CONTEXT, 1, FUNC(CONTEXT, 2, value1, value2), _BASE_FOR_PAIR_2(CONTEXT, OP, FUNC, ## __VA_ARGS__))
#define _BASE_FOR_PAIR_6(CONTEXT, OP, FUNC, value1, value2, ...) OP(CONTEXT, 2, FUNC(CONTEXT, 3, value1, value2), _BASE_FOR_PAIR_4(CONTEXT, OP, FUNC, ## __VA_ARGS__))
#define _BASE_FOR_PAIR_8(CONTEXT, OP, FUNC, value1, value2, ...) OP(CONTEXT, 3, FUNC(CONTEXT, 4, value1, value2), _BASE_FOR_PAIR_6(CONTEXT, OP, FUNC, ## __VA_ARGS__))
#define _BASE_FOR_PAIR_10(CONTEXT, OP, FUNC, value1, value2, ...) OP(CONTEXT, 4, FUNC(CONTEXT, 5, value1, value2), _BASE_FOR_PAIR_8(CONTEXT, OP, FUNC, ## __VA_ARGS__))
像往常一样,可变参数_GENERATE_FIELDS_NAME
是“类型字段-类型名称”对。在示例中,它将生成“char_1”、“char_2”。最后,_GENERATE_STRUCT_FIELDS
我们生成了结构体的实际主体(我们再次使用FOR_PAIR
):
#define _STRUCT_REDUCE(NAME, I, REC, RES) REC; RES
#define _STRUCT_MAP(context, length, type, value) type value
#define _GENERATE_STRUCT_FIELDS(structName, ...) FOR_PAIR(, _STRUCT_REDUCE, _STRUCT_MAP, ## __VA_ARGS__)
在示例中它将生成char char_1; char char_2
. 最后,宏GET_FIELD_NAME
允许我们查询 2 个静态常量。我们只需重新构造数组 const_struct_name_fieldsName
并访问一个单元格值:
#define GET_FIELD_NAME(structName, id) P99_PASTE(_, structName, _, fieldNames)[id]
按照完整的示例进行测试:
#define FOR_PAIR(CONTEXT, OP, FUNC, ...) P99_PASTE2(_BASE_FOR_PAIR_, P99_NARG(__VA_ARGS__))(CONTEXT, OP, FUNC, ## __VA_ARGS__)
#define _BASE_FOR_PAIR_2(CONTEXT, OP, FUNC, value1, value2) FUNC(CONTEXT, 1, value1, value2)
#define _BASE_FOR_PAIR_4(CONTEXT, OP, FUNC, value1, value2, ...) OP(CONTEXT, 1, FUNC(CONTEXT, 2, value1, value2), _BASE_FOR_PAIR_2(CONTEXT, OP, FUNC, ## __VA_ARGS__))
#define _BASE_FOR_PAIR_6(CONTEXT, OP, FUNC, value1, value2, ...) OP(CONTEXT, 2, FUNC(CONTEXT, 3, value1, value2), _BASE_FOR_PAIR_4(CONTEXT, OP, FUNC, ## __VA_ARGS__))
#define _BASE_FOR_PAIR_8(CONTEXT, OP, FUNC, value1, value2, ...) OP(CONTEXT, 3, FUNC(CONTEXT, 4, value1, value2), _BASE_FOR_PAIR_6(CONTEXT, OP, FUNC, ## __VA_ARGS__))
#define _BASE_FOR_PAIR_10(CONTEXT, OP, FUNC, value1, value2, ...) OP(CONTEXT, 4, FUNC(CONTEXT, 5, value1, value2), _BASE_FOR_PAIR_8(CONTEXT, OP, FUNC, ## __VA_ARGS__))
#define _METADATA_REDUCE(NAME, I, REC, RES) REC, RES
#define _METADATA_MAP(context, length, type, value) #value
#define _GENERATE_FIELDS_NAME(structName, ...) FOR_PAIR(, _METADATA_REDUCE, _METADATA_MAP, ## __VA_ARGS__)
#define _STRUCT_REDUCE(NAME, I, REC, RES) REC; RES
#define _STRUCT_MAP(context, length, type, value) type value
#define _GENERATE_STRUCT_FIELDS(structName, ...) FOR_PAIR(, _STRUCT_REDUCE, _STRUCT_MAP, ## __VA_ARGS__)
#define DEFINE_STRUCT(structName, ...) \
static const int P99_PASTE(_, structName, _, fieldCount) = P99_DIV(P99_NARG(__VA_ARGS__), 2); \
static const char* P99_PASTE(_, structName, _, fieldNames)[] = { _GENERATE_FIELDS_NAME(structName, __VA_ARGS__) }; \
\
struct structName { \
_GENERATE_STRUCT_FIELDS(structName, __VA_ARGS__); \
}
#define GET_FIELD_NAME(structName, id) P99_PASTE(_, structName, _, fieldNames)[id]
DEFINE_STRUCT(struct_name, char, char_1, char, char_2);
void main(){
struct struct_name struct_name;
const char* member_name = NULL;
member_name = GET_FIELD_NAME(struct_name, 1); // member_name = "char_2"
printf("second member name is %s\n", member_name);
}
缺点
反射是通过交易数据空间和污染全局范围获得的。这可能对您不利。一个解决方案可能是生成宏而不是常量;然而,这还有其他几个缺点,一个是对 GCC 扩展的更强使用(特别是在其他宏中定义宏)。
推荐阅读
- linux - 如何从基于同一台计算机的 Linux VM 在 Windows 计算机上运行 Internet Explorer?
- java - 如何在运行时从 maven repo 下载 dll?
- c# - 将两个字典合并为一个字典
- javascript - 如何使用 HTML 和 JavaScript 在单击按钮时播放音频文件?
- javascript - 将 css :target 选择器与 url 哈希参数组合以显示模式时出现问题
- c# - 单个元素的 XML 序列化覆盖
- node.js - 如何收集几个 Json 响应并将它们保留在函数之外?
- java - 在 Java 中并行调用多个函数是否有捷径?
- javascript - 我需要为计算机科学制作摇滚、剪纸,但我需要添加蜥蜴和史波克
- while-loop - 如何在与线程计划下线程数相同的控制器中执行