How do I access a member of a struct in a function that receives it as a void * type?
I want to have a function that takes various types of structures as an argument. So, since I don't have a specific type, I have to use void *. Now the question is, when I pass a structure to this function, how can I access a known member of that structure inside the function? In particular, I know that all structs have str1 as a member, and I want to print it, for example. Here's some sample code:
struct {
char* str1;
float tt1; } var1 = {"This is me", 12};
struct {
char* str1;
int tt2; } var2 = {"This is you", 18};
void printStruct(void* str)
{
printf("\n the structure string is %s", ??);
//can I put something in ?? to print the string?
}
main(....)
{
printStruct(&var1);
printStruct(&var2);
}
source to share
You can pass a function pointer to separate the function from your users
void printStruct(void* data, char * (*namef)(void *data)) {
printf("\n the structure string is %s", namef(data));
}
Then let's say you are passing a function struct S1*
to a function, you can pass it to a receiver like this:
char * whois(void *data) {
return ((struct S1*)data)->str1;
}
int main(void) {
struct S1 s = {"This is me", 12};
printStruct(&s, whois);
}
You can use the uglier alternative with offsetof
, though
void printStruct(void* data, size_t named) {
printf("\n the structure string is %s", *(char**) ((char*)data + named));
}
int main(void) {
struct S1 s = {"This is me", 12};
printStruct(&s, offsetof(struct S1, str1));
}
offsetof
is a macro that gives you the offset of a member within a struct. In the function, you position the pointer to the element char*
(so you need to cast to char**
to get the corresponding type) and get the value of the member.
However, I would prefer the first way to use functions. So the print function doesn't even have to know what data
the structure is pointing to. It can pass it as a black box to the getter function, and in it, you can let the compiler handle the low level offset calculations for you.
source to share
You can cast it to the type you want and then access the member, for example, suppose you have a prototype, do this:
struct X
{
char* str1;
float tt1; } var1 = {"This is me", 12};
void printStruct(void* str)
{
((struct X *)str)->str1;
...
}
Note the use of parentheses; what happens is a cast from type void * to struct var2 *, and this type inside the outer () brackets can be accessed as a variable of that type.
You must provide the names of your structures. For instance,
struct foo {
char * str1;
float tt1;
};
Then you can declare your instances like this:
struct foo var1 = { "This is me", 12 };
Then you can apply void *
to struct foo *
in your function:
void printStruct(void * ptr) {
printf("str=%s\n", ((struct foo *)ptr)->str1);
}
And you can call it like this:
int main(...) {
struct foo var1 = { "This is me", 12 };
printStruct(&var1);
}
Update: Johannes' comment was correct, my answer does not account for the fact that the original poster said he might not know the type of each structure. Updated solution below:
Since you know what all of your structures contain char * str1
, you can use the flat-memory C model by simply translating your structures into a generic, labeled structure containing only char * str1
. This will only work if char * str1
is the first element of the structures .
struct base { char * str1; };
struct { char * str1; float tt1; } var1 = { "This is me", 12 };
struct { char * str1, int tt2; } var2 = { "This is me", 18 };
void printStruct(void * ptr) {
printf("str is %s\n", ((struct base *)ptr)->str1);
}
This is a pretty dirty hack, though IMO. If there are other contributors you want to share, you won't be able to use this trick.
Another option is to define struct
which one uses enumerator coalescing to keep track of what type is actually being used in the join:
enum { FLOAT_TYPE, INT_TYPE };
struct foo {
char * str1;
union {
float tt1;
int tt2;
} numericValue;
int unionType;
};
This will allow you to define variables like this:
struct foo var1 = { .str1 = "This is me", .numericValue.tt1 =12, .unionType = FLOAT_TYPE };
struct foo var2 = { .str1 = "This is me", .numericValue.tt2 =18, .unionType = INT_TYPE };
Then you don't need to handle different types of structures, you can instead cast a pointer to struct foo *
:
void printStruct(void * ptr) {
struct foo * p = (struct foo *)ptr;
printf("string is %s\n", p->str1);
}
It might take a little more work to do this, but it's much cleaner IMO.
source to share
It is just safe for most compilers to display and access an element this way, since it is the first member of both of your structures, chances are the alignment will be the same.
The point is, since you will need to overlay pointers to the structure, they must have tags:
struct S1 { char* str1; float tt1; } var1 = {"This is me", 12};
struct S2 { char* str1; int tt2; } var2 = {"This is you", 18};
void printStruct(void* str) { printf("\n the structure string is %s", ((struct S1 *)str)->str1); }
Note, however, that for more complex examples with a large number of variables and different ordering, this may not be safe, depending on the alignment the compiler chooses.
source to share
First, a simple example:
int booger(int type, void * st) {
switch(type) {
case foo:
struct foo * f = (struct foo *)st;
/// do stuff
break;
case bar:
/// ...
Now for your structure: Define a new structure that contains only the first member, a string: struct st {char * str; }; Now you can pass all your other structs to your function by converting them to virtual_st * and then do the same as in the first example to get the actual members.
int baz(struct st * s) {
if (!strcmp(s->str, "foo") ) {
// ...
EDIT: By the way, this is similar to how the C ++ compilers that infer c do it, except that the start field is a pointer to a type structure that contains pointers to member functions and static members of the class. I don't know exactly how multiple inheritance works.
source to share
If you are passing in void*
, I don't see how the method can know anything about what you are passing through, except that it is the address for something. This means you have to use it before you can use it, or else make sure everyone struct
you pass will have it char*
in the exact same place every time.
source to share