Reputation: 12817
I have a list of variables char [][20] ls = {"var_1", "var_2", ... , ""}
which are the names of the fields of a struct struct {char var1[10], ...} my_struct;
The variables inside the struct are all char[]
with changing lengths.
The list itself is const and should not change mid-run-time.
I want to access those variables in a loop in a somewhat generic way. Instead of calling myfunc(my_struct.var1); myfunc(my_struct.var2);
and so on, I would much rather have:
for (char * p = ls[0]; *p; p += sizeof(ls[0]))
{
myfunc(my_struct.{some magic that would put var_1 / var_2 here});
}
But I guess this is impossible due to fact that the loop is executed in run-time, and the variable name needs to be available in compile-time.
Am I correct or is there something that can be done here? (not have to be this way, just wants to know if I can pack this routine into a nice loop)
Upvotes: 2
Views: 78
Reputation: 213862
Assuming you have something like
const char* ls[] = {"var_1", "var_2", ""};
where this list is not tightly-coupled to the struct data (if so you can use the answer by dbush), but is a separate item for whatever reason.
Then the slightly hacky, but well-defined version would be to use look-up tables. Create two lookup tables, one with strings, one with offsets:
#include <stddef.h>
typedef struct
{
int var_1;
int var_2;
} my_struct_t;
static const char* VAR_STRINGS[] =
{
"var_1",
"var_2",
""
};
static const size_t VAR_OFFSET[] =
{
offsetof(my_struct_t, var_1),
offsetof(my_struct_t, var_2),
};
Then do something like index = search_in_VAR_STRINGS_for(ls[i]);
to get an index. (Loop through all items, or use binary search etc). The following code is then actually legal and well-defined:
unsigned char* ptr = (unsigned char*)&my_struct;
ptr += VAR_OFFSET[index];
int var_1 = *(int*)ptr;
This takes padding in account and the pointer arithmetic is guaranteed to be OK by C11 6.3.2.3/7:
When a pointer to an object is converted to a pointer to a character type, the result points to the lowest addressed byte of the object. Successive increments of the result, up to the size of the object, yield pointers to the remaining bytes of the object.
And since what's really stored at that address (effective type) is indeed an int
, the variable access is guaranteed to be OK by C11 6.5/7 ("strict aliasing"):
An object shall have its stored value accessed only by an lvalue expression that has one of the following types:
— a type compatible with the effective type of the object,
But various error handling obviously needs to be in place to check that something doesn't go out of bounds.
Upvotes: 1
Reputation: 223972
Since all members are arrays of the same type, you can create an array of addresses to each member and loop through that:
char *my_struct_addrs[] = { my_struct.var1, my_struct.var2, ... };
int i;
for (i=0; i < sizeof(my_struct_addrs) / sizeof(my_struct_addrs[0]); i++) {
myfunc(my_struct_addrs[i]);
}
Since the size of each of these arrays is different however, you'll need to take care not to pass the bounds of each one. You can address this by keeping track of the size of each field and passing that to the function as well:
struct addr_list {
char *addr;
int len;
};
struct addr_list my_struct_addrs[] = {
{ my_struct.var1, sizeof(my_struct.var1) },
{ my_struct.var2, sizeof(my_struct.var2) },
...
};
int i;
for (i=0; i < sizeof(my_struct_addrs) / sizeof(my_struct_addrs[0]); i++) {
myfunc(my_struct_addrs[i].addr, my_struct_addrs[i].len);
}
Upvotes: 5