cdata-structuresmacrossetc-preprocessor

Define a variable and "return" an expression all within a single C macro


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.


Solution

  • 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.