cstructunions

How to simply access data in a union inside of a struct?


Here is a type I declared:
(I declared t_sphere, t_cylinder and t_triangle too)

typedef struct  s_intersection{
  double       t1;
  double       t2;
  int id;
  union {
    t_sphere sph;
    t_cylinder cyl;
    t_triangle tri;
  } u;
} t_intersection;

When I use that intersection structure in some code, is there a handy way to refer to the member inside my union ?
e.g. Let's say I want to write a function that acts differently according to the type of geometric_figure it contains. Will I have to do it like this ?

if (geometric_figure_id == SPHERE_ID)
    // I know I will have to refer to p->u with p->u.sph...
else if(geometric_figure_id == CYLINDER_ID)
    // I know I will have to refer to p->u with p->u.cyl...
else if (geometric_figure_id == TRIANGLE_ID)
    // I know I will have to refer to p->u with p->u.tri... 

What if I had 10 different geometric_figures types inside my union ? This feels very heavy.
Do you have a more elegant solution ?


Solution

  • Let's say I want to write a function that acts differently according to the type of geometric_figure it contains. Will I have to do it like this ?

    It sounds like an awareness of other languages' support for runtime polymorphic function dispatch may be part of the context for your question. If so, then it's important to recognize that what you're dispatching on here is not the type of any object, as you typically would in (say) C++ or Java, but rather the value of an integer.

    If you want to follow different control paths for different runtime values of an integer, then there is no alternative to writing a flow-control statement -- generally an if / else if / else or a switch -- that directs control appropriately.*

    Now, if you were dispatching on type then C does offer type-generic expressions. For the most part, these make sense to use only inside a macro:

    #define VOLUME(x) _Generic((x), \
        t_sphere: 4 * PI * (x).r * (x).r * (x).r / 3, \
        t_cylinder: PI * (x).r * (x).r * (x).h, \
        t_cube: (x).edge * (x).edge * (x).edge \
    )
    

    In contexts other than a macro, you know the type involved already, so a type-generic expression gains you nothing worth having.

    Depending on how much macro magic you want to apply, there are ways avoid writing out long if / else if / else statements or long switch statements by hand. Type-generic macros would likely play a role in something like that. But such a course of action is difficult to implement well. You're more likely to end up with a confusing maintenance nightmare of complex macro stacks than with something that would compare favorably to manually-written switch statements.


    *Or maybe a complex expression with nested use of the ternary operator could be taken as fullfilling that need, but using such an expression is not realistic if it needs to be written and maintained by hand.