c - 获取给定成员定义地址的成员定义名称?

标签 c struct

给定结构成员变量的地址,是否可以获取成员变量的名称并将其存储在字符指针中?

#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 project帮助我更轻松地执行宏处理。

起点是如何定义结构:

//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_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) type value
#define _GENERATE_STRUCT_FIELDS(structName, ...) FOR_PAIR(, _STRUCT_REDUCE, _STRUCT_MAP, ## __VA_ARGS__)

在示例中它将生成char char_1;字符 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) 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 扩展(特别是在其他宏中定义宏)。

关于c - 获取给定成员定义地址的成员定义名称?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55617241/

相关文章:

c - as400 : C headers include

c - WCHAR [256] as WCHAR ** 即,迭代数组字符串与仅迭代一个字符串

你能在结构中有一个数组吗?

c - 指向结构体数组的指针

c - 如何在 Windows 中退出阻塞的 accept() 调用?

c - 这些 C 运算符是什么意思?

c - 在结构中使用 Option<unsafe fn ...> 的奇怪内存布局

struct - Elixir:如何使结构可枚举

c - 在声明时或在单独的函数中初始化结构

c++ - 如何处理 R 中的 C++ 内部数据结构以允许保存/加载?