NOTE: I am not interested in using GCC statement expression!
I am implementing a set data structure using a Red Black Tree and to make it work for multiple types I am using macros.
This is a definition of a node:
typedef struct {
bool colour;
void* val;
void* parent;
void* left;
void* right;
}* set;
I want to create a macro function set_contains(s, t)
, that will "return" a true expression if t
is in a set s
. By "return" I mean, that I can use it as such:
if(set_contains(my_set, 6)) {
...
}
For example, the following macro "returns" a true expression if x is an odd number:
#define isOdd(x) \
/* notice the lack of semicolon! */ \
(bool)((x)&1)
My set_contains
macro looks as follows:
#define set_contains(s, t) \
do { \
set it = s; \
while(it && *(typeof((t))*)it->val != (t)) { \
if((t) < *(typeof((t))*)it->val) it = it->left; \
else it = it->right; \
} \
/* This is what I want to "return" */ \
(bool)it; \
} while(0)
How can I have (bool)it
at the end of macro, so I can use the entire thing as an expression (like the isOdd
macro)? The do while
is necessary since I need to define a temp variable (it
) to iterate over the set. Of course I cannot use it
after while
, since it is undefined in that scope. Once again, I want to achieve this without using GCC statement expression.
Macros alone are insufficient for this purpose; probably the least painful solution is to create multiple type-aware functions and use _Generic
to select the correct one:
#define set_contains(set, val) _Generic((val), \
int: set_contains_int, \
double: set_contains_double, \
char *: set_contains_string, \
/* any additional types */ \
)(set, val)
...
bool set_contains_int(set s, int val) { ... }
bool set_contains_double(set s, double val) { ... }
bool set_contains_string(set s, char *val) { ... }
...
if (set_contains(set, 6))
// do something
The _Generic
directive picks the correct function based on the type of val
. Downside is you wind up with a lot of duplicated logic.
Alternately, you can create a comparator function that you pass as a callback:
int cmp_int( const void *l, const void *r )
{
const int *il = l;
const int *ir = r;
if ( *l < *r )
return -1;
else if ( *l > *r )
return 1;
return 0;
}
bool set_contains(set s, const void *val, int (*cmp)(const void *, const void *))
{
...
while( it && cmp(it->val, val) != 0 )
{
if ( cmp(it->val, val) < 0 )
it = it->left;
else
it = it->right;
}
return it != NULL;
}
which would be called as
int val = 6;
if (set_contains(set, &val, cmp_int))
...
but it means you can't call set_contains
directly with val
as a literal (something like set_contains(set, 6, cmp_int)
won't work). To do that you'd still have to create type-aware front ends like:
bool set_contains_int(set s, int val)
{
return set_contains(s, &val, cmp_int);
}
For stuff like this I prefer the second method (less duplication of effort IMO), but both are kind of gross compared to true type polymorphism.