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).