tags:

views:

182

answers:

6

I want to have a function that accepts different type of structures as argument. So, since I don't have a specific type, I have to use void*. Now question is: when I pass a structure to this function, how can I access a known member of this structure inside the function? Specifically, I know that all structures have str1 as a member and I want, for example, print it. Here is a 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);  
}
+5  A: 

You can cast it to the type you want it to be and then access the member, for example, suppose you have your prototype, do this:

struct X
{
char* str1;  
float tt1; } var1 = {"This is me", 12};  

void printStruct(void* str)
{
    ((struct X *)str)->str1;
    ...
}

Notice the use of brackets; what happens is a cast of type void* to struct var2 * and this type, inside the outermost () brackets, can be accessed as a variable of that type.

Ninefingers
I don't think this will work unless the structs have names. In the original question, "var2" is the variable name, but not the struct name.
Skrud
@Skrud yep, sorry edited in.
Ninefingers
A: 

If you're passing in a void* I don't see how the method could know anything about what you're passing in, except that it's an address to something. What this means is that you have to cast it before you can use it, or else make sure that all of the structs that you would pass in have the char* in exactly the same location each time.

John at CashCommons
+1  A: 

For most compilers it is just safe to cast and access the member that way, since it is the first member of both of your structures, odds are that the alignment will be the same.

The thing is though, since you will need to cast the pointers into a structure they need to 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 more variables, and different order, that this might not be safe, it all depends on the alignment the compiler chooses.

Ramon Zarazua
+1  A: 

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 struct: Define a new struct which only contains the first member -- the string: struct st { char * str; }; Now you can pass all of your other structs into your function by casting them to a virtual_st * and then do the same thing that is 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 c++ compilers that output c do it, except that the initial field is a pointer to a type structure which contains function pointers for member functions and static class members. I don't know exactly how multiple inheritance works, though.

nategoose
+3  A: 

You should give your structs names. For example,

struct foo {
   char * str1;
   float tt1;
};

Then you can declare your instances like this:

struct foo var1 = { "This is me", 12 };

Then you can cast the void * to a 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: Commenter Johannes was correct, my answer doesn't account for the fact that the original poster said he might not know the type of each struct. Updated solution is below:

Since you know that all your structs contain a char * str1, you can take advantage of C's "flat memory model" by casting your structs directly to generic, tagged struct that only contains char * str1. This will only work if char * str1 is the first element of the structs.

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 members that you want to share, you won't be able to use this same trick.

Another option is to define a struct that uses a union with an enumerator to keep track of which type is actually being used in the union:

enum { FLOAT_TYPE, INT_TYPE };
struct foo {
   char * str1;
   union {
      float tt1;
      int tt2;
   } numericValue;
   int unionType;
};

This would let you 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 for different types of structs, you can cast your void pointer to struct foo * instead:

void printStruct(void * ptr) {
   struct foo * p = (struct foo *)ptr;
   printf("string is %s\n", p->str1);
}

This might be require a bit more work but it's much cleaner, IMO.

Skrud
Tho the guy said that the function receives different types of structs. In your code, you assume it only ever receives `struct foo`.
Johannes Schaub - litb
Technically speaking `struct foo var1 = { "This is me", 12 };` is a _definition_, not a declaration. :p If the compiler reserves space for an instance or implements a function then it defines it; otherwise if it does absolutely nothing other than acknowledge an entity then it declares it.
wilhelmtell
@wilhelmtell, a definition is a declaration, so you are mistaken.
Johannes Schaub - litb
+6  A: 

You can pass a function pointer to decouple the function from its users

void printStruct(void* data, char * (*namef)(void *data)) {  
   printf("\n the structure string is %s", namef(data));    
}

Then suppose you pass a struct S1* to the function, you can pass it a getter 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 could use a more 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 char* member (so you have to cast to char** to get the appropriate type) and get the member value.

However i would prefer the first way of using functions. That way, the print function doesn't even have to know that data points to a struct. It can hand over 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.

Johannes Schaub - litb
Nice. I hope I'm never in a situation where I have to do that.
uncle brad