Получить имя определения члена по заданному адресу определения члена? - PullRequest
1 голос
/ 10 апреля 2019

Учитывая адрес переменной-члена Structs, возможно ли получить имя переменной-члена и сохранить его в символьном указателе?

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

1 Ответ

0 голосов
/ 10 апреля 2019

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 и _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 позволяет вам перебирать переменные аргументы только один за другим. Но нам нужно циклически перебрать аргумент variadic с шагом 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__))

Аргумент variadic _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 статические константы. Мы просто реструктурируем массив 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 (в частности, определение макросов в других макросах).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...