问题描述
给定一个结构体成员变量的地址,是否可以获取成员变量的名称并将其存储在字符指针中?
#include 结构结构名称{字符 char_1;字符 char_2;};int main(){struct custom_struct struct_name;字符 *member_name = NULL;member_name = getName( struct_name + 1);//member_name = "char_2"返回0;}
AFAIK C 本身不支持反射.
所以如果你真的想要它,你需要自己做.一种可行的方法(该解决方案的可接受程度取决于您)是通过预处理器.
使用全局变量
这里的主要思想是每个结构体都关联了 2 个全局常量:一个指定结构体的字段数(我们不需要它,但如果您想遍历所有字段名称,这可能很有用)和表示字段名称的数组.要自动执行此操作,您需要牺牲定义结构的方式.
这里的解决方案有点依赖 GCC(我们将使用 ##
变体)但它应该很容易移植.
我也在使用 P99 项目来帮助我更轻松地执行宏处理.>
起点是如何定义结构体:
//variadic 一个逗号分隔的字段类型和字段名列表//示例: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__) };\\结构结构名称 { \_GENERATE_STRUCT_FIELDS(结构名称,__VA_ARGS__);\}
基本上在调用 DEFINE_STRUCT 时,我们将生成 2 个全局(静态)常量.在示例中,它们将被称为 _struct_name_fieldCount
和 _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 允许您仅一个一个地循环可变参数.但是我们需要用第 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) 类型值#define _GENERATE_STRUCT_FIELDS(structName, ...) FOR_PAIR(, _STRUCT_REDUCE, _STRUCT_MAP, ## __VA_ARGS__)
在示例中它将生成 char char_1;char char_2
.最后,宏 GET_FIELD_NAME
允许我们查询 2 个静态常量.我们简单地重构数组 constat _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) 类型值#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__) };\\结构结构名称 { \_GENERATE_STRUCT_FIELDS(结构名称,__VA_ARGS__);\}#define GET_FIELD_NAME(structName, id) P99_PASTE(_, structName, _, fieldNames)[id]DEFINE_STRUCT(struct_name, char, char_1, char, char_2);无效主(){结构结构名称结构名称;const char* member_name = NULL;member_name = GET_FIELD_NAME(struct_name, 1);//member_name = "char_2"printf("第二个成员名称是 %s\n", member_name);}
缺点
反射是通过交易数据空间和污染全局范围获得的.这可能对你不利.一个解决方案可能是生成宏而不是常量;然而,这还有其他几个缺点,其中一个是 GCC 扩展的更强使用(特别是其他宏中的宏定义).
Given an address to a structs member variable, is it possible to get the name of the member variable and store it in a character pointer?
#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 doesn't natively support reflecton.
So if you really want it, you need to do it yourself. One way you can do it (how acceptable this solution is up to you) is via the preprocessor.
Using global variables
The main idea here is that each struct has associated 2 global constants: one specifying the number of fields of the struct (we won't need it but if you want to loop over all the fields name this can be useful) and an array representing the field name. To automatically do it, you need to sacrifice how you define a struct.
The solution here is a little GCC dependent (we will use the ##
variant) but it should be easy to port.
I'm also using P99 project to help me performing the macro processing more easily.
The starting point is how to define a struct:
//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__); \
}
Basically when calling DEFINE_STRUCT we will generate the 2 global (static) constants. In the example they will be called _struct_name_fieldCount
and _struct_name_fieldNames
. Staticness isn't really necessary and it can be bad if you want to query the reflection outside the translation unit.
The first constant is easily generated.As for the second constant we need to loop over the pairs "type field - type name":
#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
macro is a macro we need to define: sadly P99 allows you to loop over variadic arguments only one by one,. But we need to cycle over the variadic argument with step of 2. So we define such macro (just for example I allow up to 5 fields, but this limit can be easily update buy adding more macro definitions):
#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__))
The variadic argument of _GENERATE_FIELDS_NAME
is, as usual, the pair "type field -type name". In the example it will generate "char_1", "char_2". Finally with _GENERATE_STRUCT_FIELDS
we generate the actual body of the struct (hewre we use again 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__)
in the example it will generate char char_1; char char_2
.Finally the macro GET_FIELD_NAME
allows us to query the 2 static constants.We simply recustruct the array constat _struct_name_fieldsName
and access a cell value:
#define GET_FIELD_NAME(structName, id) P99_PASTE(_, structName, _, fieldNames)[id]
Following the complete example with a test:
#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);
}
Drawbacks
Reflection is obtained by trading data space and by polluting the global scope. This may be bad for you.A solution might be to generate macros instead of constants; however this has several other drawbacks, one being a stronger use of GCC extensions (in particular the definition of macros within other macros).
这篇关于在给定成员定义地址的情况下获取成员定义名称?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!