2

Based on the answer to the question Passing variable type as function parameter:

I could write something like this:

enum {
    TYPEA,
    TYPEB,
    TYPEC,
    TYPED
} TYPE;

void foo(TYPE t, void* x){
    switch(t){
        case TYPEA:
            struct A* y = (struct A*)x;
                            //do something with a member named "next"   
            break;
        case TYPEB:
            struct B* y = (struct B*)x;
                            //do something with a member named "next" 
         ...
    }
}

Is there any way to avoid rewriting the "something with a member named next" multiple times?

We are assuming that "next" in A and B are not in the same relative memory position in each struct.

Community
  • 1
  • 1
merlin2011
  • 71,677
  • 44
  • 195
  • 329
  • Have you considered using a macro? – Hasturkun Dec 07 '12 at 10:01
  • does the `next` member at least have the same type in all structs ? – Sander De Dycker Dec 07 '12 at 10:02
  • thats the solution which was accepted in the referred question. I guess he doesn't want to use it. – UmNyobe Dec 07 '12 at 10:03
  • You could move the *do something with member xxx* part into a (static) function, which takes a void* pointer as an argument. NOTE: if the pointers point to (almost similar) structs with a common "prefix", this construct is known as a "smart union" – wildplasser Dec 07 '12 at 10:19

4 Answers4

2

Assuming the items of the enum aren't given custom numbers, you can do a compact version of a switch-statement, by using function pointers.

enum {
    TYPEA,
    TYPEB,
    TYPEC,
    TYPED,

    TYPE_N // number of enum items
} TYPE;


typedef void(*type_func_t)(void*);



static void TYPEA_specific (void* x) 
{
  struct A* y = x;

  // specific stuff related to TYPEA here

  do_something_with_next(y->next);
}

static void TYPEB_specific (void* x) 
{
  struct B* y = x;

  // specific stuff related to TYPEB here

  do_something_with_next(y->next);
}



static const type_func_t TYPE_HANDLER [TYPE_N] = 
{
  TYPEA_specific,
  TYPEB_specific
  ...
};


inline void foo (TYPE t, void* x)
{
  TYPE_HANDLER[t](x);
}
Lundin
  • 195,001
  • 40
  • 254
  • 396
1

This solution uses a macro:

#include <stdio.h>

#define POLYCAST_AB(etype, target, member) \
  *((etype) == TYPEA ? &((struct A *)(target))->member :  \
    (etype) == TYPEB ? &((struct B *)(target))->member : 0)

enum TYPE {
   TYPEA,
   TYPEB
};

struct A {
   int next;
} a = {42};

struct B {
   int i;
   int next;
} b = {43, 44};

static void foo(enum TYPE t, void *x) {
   POLYCAST_AB(t, x, next) += 100;  // <-- most other answers can't do this
   printf("next=%d\n", POLYCAST_AB(t, x, next));
}

int main(void) {
   foo(TYPEA, &a);   
   foo(TYPEB, &b);   
   return 0;
}

If you don't need an lvalue, you can omit the extra * and & in the macro definition (and also omit the assumption that the next all have the same type).

Joseph Quinsey
  • 9,553
  • 10
  • 54
  • 77
0

C doesn't have dynamic look-up of structure members, of course.

The solution that will save the most keystrokes is probably to use a macro.

Also, there's no point in casting the pointer when converting from void *.

unwind
  • 391,730
  • 64
  • 469
  • 606
0

In each case, you can take the address of next with &y.next and then either pass it to a function or transfer control to code that uses the pointer to do something with next. This presumes that next has the same type in each struct, although it may be in different locations.

Here are three examples:

// Example 0:  Functional call.
switch(t)
{
    case TYPEA:
        MyFunction(&(struct A *)x->next);
        break;
    case TYPEB:
        MyFunction(&(struct B *)x->next);
        break;
}

// Example 1: Code after the switch.
TypeOfNext *next;
switch(t)
{
    case TYPEA:
        next = &(struct A *)x->next;
        break;
    case TYPEB:
        next = &(struct B *)x->next;
        break;
}
… code that uses next…

// Example 2: Code in the switch, with goto.
switch(t)
{
    TypeOfNext *next;
    case TYPEA:
        next = &(struct A *)x->next;
        goto common;
    case TYPEB:
        next = &(struct B *)x->next;
    common:
        … code that uses next…
        break;
}
Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312