GNU C replacement/workaround for typeof/__auto_type applied to bit fields

Viewed 550

GNU C has two extensions that it proposes to make safe macros like MAX and MIN that will evaluate arguments only once: typeof and __auto_type. To give examples of two MAX macros demonstrating each one:

#define MAX(a, b) ({    \
    typeof(a) _a = (a); \
    typeof(b) _b = (b); \
    _a > _b ? _a : _b;  \
})

#define MAX(a, b) ({      \
    __auto_type _a = (a); \
    __auto_type _b = (b); \
    _a > _b ? _a : _b;    \
})

The problem with both of these is that typeof and __auto_type give errors if it is used on a bit field. This example code shows the problems with a bit field using either MAX:

#include <stdio.h>
#include <stdint.h>

// Insert one of the MAX macros here

struct bitfields {
    uint8_t a: 4;
    uint8_t b: 4;
};

int main(int argc, char *args[]) {
    struct bitfields x = {12, 4};
    printf("%d\n", MAX(x.a, x.b));
    return 0;
}

GCC gives these error messages for typeof and __auto_type, respectively:

error: 'typeof' applied to a bit-field
error: '__auto_type' used with a bit-field initializer

So the question is: Why does GCC not allow these to be used with bit fields (I can't find any documentation on it), and what can be done to make a MAX macro that evaluates arguments only once for any type that still works with bit fields?

2 Answers

You use __typeof__(+(a)) to get the promoted type according to default promotions. This will work at least for bitfields of type int. I'm not sure how compilers treat the type for larger bitfield types, which are implementation-defined.

This problem has a simple acceptable solution using the unary plus operator to force integer promotion:

#define MAX(a, b) ({    \
    typeof(+(a)) _a = (a); \
    typeof(+(b)) _b = (b); \
    _a > _b ? _a : _b;  \
})

Yet this approach has several issues:

  • The macros use compiler specific extensions, such as typeof or __auto_type, as well as the statement expression ({ ... }) which are not portable.

  • They are not really type-safe if a and b have different types: a simple expression MAX(1L, -1U) will even have a different value depending on the size of type long.

  • Furthermore, the behavior will be incorrect if the expressions passed as arguments refer to variables named _a or _b.

I'm afraid typed inline functions seem a better way to portability and readability, but a more generic solution would be desirable indeed.

Related