A single header C library for safe construction and use of tagged-unions.
To define a tagged-union just #define
a macro that takes 2 arguments:
2ndnd arg. can be ignored, but the 1st one is used to define members:
#define Shape(entry, ...) \
entry(LINE, Line) \
entry(SQUARE, struct Square) \
entry(NUMBER, float) \
entry(TRIANGLE, struct { float mA, mB, mC; })
Note that the arguments can have any name.
Then you just pass it to tagged_union
macro:
tagged_union(Shape)
which will create all the necessary type declarations.
For type-safe tagged-union initialization the header provides tu_new
macro.
To create an array it can be used like this:
Shape lShapes[] = {
tu_new(LINE, { 1, 2 } ),
tu_new(TRIANGLE, { 3, 5, 6 } ),
tu_new(NUMBER, 7 ),
tu_new(SQUARE, { 8 } )
};
First we need to specify the tagged-union type, then the tag enum label will tell the both set the right tag and make sure the correct union member is used, lastly we initialize the member.
Header provides tu_resolve
macro which helps to process individual cases.
for (i = 0; i < sizeof(lShapes) / sizeof(*lShapes); i++)
{
tu_resolve (Shape, lShapes + i)
tu_matches (LINE, line)
line->mDirection = 0;
printf("len: %f dir: %f\n", line->mLength, line->mDirection);
tu_matches (TRIANGLE, triangle)
printf("sides: %f %f %f\n", triangle->mA, triangle->mB, triangle->mC);
tu_matches (NUMBER, num)
printf("%f\n", *num);
tu_resolved
}
This macro is a wrapper around switch
statement and stores a pointer to the tagged-union for later use (compiler should be able to optimize this out),
that is why the macro requires the type of the tagged-union and a pointer to it.
Each tu_matches
handles the specific union member,
for that you need to specify tag enum label (from which the right member and its type is derrived),
name of a variable to which a pointer to the member will be stored from previously stored tagged-union (compiler should be able to optimize this out) and
what should be done with the variable holding the correctly cast union member (this code will have its own { /*scope*/ break; }
, so there is no "fallthrough" for individual "cases").
Then the whole thing needs to be closed with tagged_end
macro.
You can check the example.c
, to see what is going on try following command:
gcc -D_STDIO_H -D_STDDEF_H example.c -E | cat -s | sed '/^#/d' > example.i
This will do just the preprocessing step of gcc and also remove all the extra junk.
Note: This library is just a fun experiment and exists only to share knowledge about fun features of C.