For this macro:
#define ncmp(x, y) ((x) > (y)) - ((x) < (y))
the main problems are:
It requires an additional set of parentheses in the expansion to form a primary expression.There are not enough parentheses in the expansion to turn it into a primary expression. It should be:
#define ncmp(x, y) (((x) > (y)) - ((x) < (y)))
It evaluates (x)
and (y)
twice, which could be a problem if the evaluation has side effects.
To avoid the problem of multiple evaluation, the macro expansion could use a generic selection expression to call a different function for each type being compared.
Note 1: generic selection was added in the 2011 version of the C standard (C11).)
Here is an example macro using generic selection. It may need to be extended to support additional types:
#define ncmp(x, y) _Generic((x) < (y), \
int: ncmp_si, \
unsigned: ncmp_ui, \
long: ncmp_sli, \
unsigned long: ncmp_uli, \
long long: ncmp_slli, \
unsigned long long: ncmp_ulli, \
float: ncmp_f, \
double: ncmp_d, \
long double: ncmp_ld \
)((x), (y))
Note 2: The controlling expression of the generic selection ((x) < (y)
) is not evaluated, but its type is used to select a corresponding generic association expression (if any).
Note 3: The choice of <
in the controlling expression does not matter much, but it does at least check that (x)
and (y)
have an ordered relationship. For arithmetic operands, the type of the controlling expression is the result of the usual arithmetic conversions.
Note 4: Due to the usual arithmetic conversions done to the operands of <
in the controlling expression, there is no need to add cases for integer types below the rank of int
.
Note 5: It is possible to add a default:
generic association. For example, it could be defined to fall back to using the less safe multiple evaluation method as follows:
#define ncmp(x, y) _Generic((x) < (y), \
int: ncmp_si((x), (y)), \
unsigned: ncmp_ui((x), (y)), \
long: ncmp_sli((x), (y)), \
unsigned long: ncmp_uli((x), (y)), \
long long: ncmp_slli((x), (y)), \
unsigned long long: ncmp_ulli((x), (y)), \
float: ncmp_f((x), (y)), \
double: ncmp_d((x), (y)), \
long double: ncmp_ld((x), (y)), \
default: ((x) > (y)) - ((x) < (y)) \
)
but I chose to leave it up to the programmer to add the missing cases.
It is necessary to define the functions used by each of the generic associations above. To save a bit of typing, a helper macro could be defined to define them:
#define MK_NCMP_(suf, T) \
static inline int ncmp_##suf(T x, T y) { return (x > y) - (x < y); }
MK_NCMP_(si, int)
MK_NCMP_(ui, unsigned)
MK_NCMP_(sli, long)
MK_NCMP_(uli, unsigned long)
MK_NCMP_(slli, long long)
MK_NCMP_(ulli, unsigned long long)
MK_NCMP_(f, float)
MK_NCMP_(d, double)
MK_NCMP_(ld, long double)