4

I have a function that draws a circle using OpenGL, I would like to pass it a structure containing the x and y coordinates and the radius. The problem is this same function has to be used with 3 different structures all containing the coordinates, radius and some other things that the draw function doesn't use.

Is there some way to only have one argument for 3 different structures (only one is sent at a time).

I hope I've been enough precise.

PS : the functions have to be "abstract".

ctuffli
  • 3,559
  • 4
  • 31
  • 43
DennisVDB
  • 1,347
  • 1
  • 14
  • 30

2 Answers2

6

Yes you can use a prototype like this:

void foo(char type, void *data);

Use the type to tell the function which struct to use the data as, and you're good.

struct c *prepareStructC(void);
//...
struct c *toto = prepareStructC();
foo('c', toto);
//...
void foo(char type, void *data)
{
  int x, y;
  switch (type)
  {
    case 'c':
      x = ((struct c*)data)->x;
      y = ((struct c*)data)->y;
      break;
    //...
  }
  //...
}

Second option, if you want to avoid a switch/case, and be able to add more struct types afterwards, without evolving foo, you can make sure all your structs begin with the necessary data, always in the same order, with the same type. This way you can make something like an "interface" from C++, and use abstract versions of the type:

struct abstract
{
  int x;
  int y;
  int radius;
}

struct a
{
  struct abstract abs;
  //... other data ...
}
struct b
{
  struct abstract abs;
  //... other data ...
}
struct c
{
  struct abstract abs;
  //... other data ...
}

//Two choices : either you have:
void foo(void *data)
{
  int x,y,r;
  x = ((struct abstract*)data)->x;
  y = ((struct abstract*)data)->y;
  r = ((struct abstract*)data)->radius;
  //...
}

//OR Smarter way:
void foo2(struct abstract *data)
{
  int x,y,r;
  x = data->x;
  y = data->y;
  r = data->radius;
}
//Calling foo2 with:
struct a *sa = prepareStructA();
struct b *sb = prepareStructB();
struct c *sc = prepareStructC();
foo2(sa->abs);
foo2(sb->abs);
foo2(sc->abs);

The second part of the second method allows you more flexibility, breaking down specific information in a subtype, enables you to put the abs part anywhere inside the struct a/b/c, and is better in the principle of a single purpose for a struct (Having coordinates and radius and other things inside a struct is not always the best.)

Eregrith
  • 4,263
  • 18
  • 39
1

Only if the function accepts a void* as the argument, and then you cast the void* to the correct pointer type in the function before dereferencing it to access the data within.

typedef enum
{
    type1, type2, type3
} datatype;

void foo(void* data, datatype type)
{
    switch(type)
    {
        case type1:
        // do something with data by casting it to the proper type
        break;

        // ... other cases here
    }
}

However, by doing so you will a) need to pass another argument that indicates the original type being casted to void* before being passed, and b) you are abandoning the type system which is hardly ever a good idea and should always be avoided.

If you take this approach, highly recommend creating three "wrapper" functions that take the correct types before calling the function (that takes the void*) internally. Make it a strict convention to never call the void* function directly; only call the wrapper. In this fashion, you still have the type system to help out with compilation errors.

void bar1(someStruct* data)
{
    foo((void*) data, type1);
}
void bar2(someOtherStruct* data)
{
    foo((void*) data, type2);
}
void bar3(yetAnotherStruct* data)
{
    foo((void*) data, type3);
}
inspector-g
  • 4,146
  • 2
  • 24
  • 33
  • You're right, the enum is better than a regular "char", my first method's dirtier ^^" – Eregrith Apr 18 '12 at 15:42
  • Agreed, but you make a good point with beginning the separate structs with the same data "signature", allowing for both use of the type system and (theoretically) fewer changes to the primary function over time. – inspector-g Apr 18 '12 at 15:47