One approach would be through the (ab)use of x-macros. They allow you to reduce repetition, at the expense of potential rage of your fellow coworkers. The benefit is that you will only need to update the list of items at one place, and the struct and all the necessary metadata will be autogenerated by the preprocessor.
I.e. you define simply a list of entries like this, where FILE_ENTRY
is yet undefined:
#define X_FILE_LIST(X_FILE_ENTRY) \
X_FILE_ENTRY(radiation_insolation, 7) \
X_FILE_ENTRY(radiation_radiation, 5) \
X_FILE_ENTRY(winds, 9) \
X_FILE_ENTRY(pressure, 1) \
X_FILE_ENTRY(humidity, 1) \
X_FILE_ENTRY(temperature, 4)
And then define FILE_ENTRY(name, len)
as you wish:
// number of entries
#define X_EXPAND_AS_COUNT(name, len) 1 +
const int FILES_count = X_FILE_LIST(X_EXPAND_AS_COUNT) 0;
// struct definition
#define X_EXPAND_AS_FIELD(name, len) FILE_ name[len];
typedef struct {
X_FILE_LIST(X_EXPAND_AS_FIELD)
}
FILES;
// byte offsets of each field
#define X_EXPAND_AS_BYTEOFFSET(name, len) offsetof(FILES, name),
int FILES_byte_offsets[] = {
X_FILE_LIST(X_EXPAND_AS_BYTEOFFSET)
};
// FILE_ offsets of each field
#define X_EXPAND_AS_FILEOFFSET(name, len) offsetof(FILES, name)/sizeof(FILE_),
int FILES_offsets[] = {
X_FILE_LIST(X_EXPAND_AS_FILEOFFSET)
};
// sizes of each array
#define X_EXPAND_AS_LEN(name, len) len,
int FILES_sizes[] = {
X_FILE_LIST(X_EXPAND_AS_LEN)
};
// names of each field
#define X_EXPAND_AS_NAME(name, len) #name,
const char * FILES_names[] = {
X_FILE_LIST(X_EXPAND_AS_NAME)
};
This will expand to something like:
const int FILES_count = 1 + 1 + 1 + 1 + 1 + 1 + 0;
typedef struct {
FILE_ radiation_insolation[7];
FILE_ radiation_radiation[5];
FILE_ winds[9];
FILE_ pressure[1];
FILE_ humidity[1];
FILE_ temperature[4];
}
FILES;
int FILES_byte_offsets[] = {
((size_t)&(((FILES*)0)->radiation_insolation)),
((size_t)&(((FILES*)0)->radiation_radiation)),
((size_t)&(((FILES*)0)->winds)),
((size_t)&(((FILES*)0)->pressure)),
((size_t)&(((FILES*)0)->humidity)),
((size_t)&(((FILES*)0)->temperature)),
};
int FILES_offsets[] = {
((size_t)&(((FILES*)0)->radiation_insolation))/sizeof(FILE_),
((size_t)&(((FILES*)0)->radiation_radiation))/sizeof(FILE_),
((size_t)&(((FILES*)0)->winds))/sizeof(FILE_),
((size_t)&(((FILES*)0)->pressure))/sizeof(FILE_),
((size_t)&(((FILES*)0)->humidity))/sizeof(FILE_),
((size_t)&(((FILES*)0)->temperature))/sizeof(FILE_),
};
int FILES_sizes[] = { 7, 5, 9, 1, 1, 4, };
const char * FILES_names[] = {
"radiation_insolation", "radiation_radiation",
"winds", "pressure", "humidity", "temperature",
};
You can then iterate it using something like:
for (int i = 0; i < FILES_count; i++)
{
FILE_ * first_entry = (FILE_ *)&files + FILES_offsets[i];
for (int j = 0; j < FILES_sizes[i]; j++)
{
FILE_ * file = first_entry + j;
printf("%s[%d].skip_lines = %d \n",
FILES_names[i],
j,
file->skip_lines);
}
}
This will iterate through all the members of FILES
, and through all array members of each field:
// output of the program above
radiation_insolation[0].skip_lines = 0
radiation_insolation[1].skip_lines = 0
radiation_insolation[2].skip_lines = 0
radiation_insolation[3].skip_lines = 0
radiation_insolation[4].skip_lines = 0
radiation_insolation[5].skip_lines = 0
radiation_insolation[6].skip_lines = 0
radiation_radiation[0].skip_lines = 0
radiation_radiation[1].skip_lines = 0
radiation_radiation[2].skip_lines = 0
radiation_radiation[3].skip_lines = 0
radiation_radiation[4].skip_lines = 0
winds[0].skip_lines = 0
winds[1].skip_lines = 0
winds[2].skip_lines = 0
winds[3].skip_lines = 0
winds[4].skip_lines = 0
winds[5].skip_lines = 0
winds[6].skip_lines = 0
winds[7].skip_lines = 0
winds[8].skip_lines = 0
pressure[0].skip_lines = 0
humidity[0].skip_lines = 0
temperature[0].skip_lines = 0
temperature[1].skip_lines = 0
temperature[2].skip_lines = 0
temperature[3].skip_lines = 0
And this brings you to the actual "reflection" which allows you to find the member by its name:
FILE_ * get_entry_by_name_and_index(FILES * files, const char * name, int idx)
{
// NOTE: no bounds checking/safe string function, etc
for (int i = 0; i < FILES_count; i++)
{
if (strcmp(FILES_names[i], name) == 0)
{
int base_offset = FILES_offsets[i];
return (FILE_ *)files + base_offset + idx;
}
}
return NULL;
}
For example, this will get the pointer to files.winds[4]
:
FILE_ * item = get_entry_by_name_and_index(&files, "winds", 4);
assert((void*)item == (void*)&files.winds[4]);