Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to implement a generic macro in C?

FUNC(param);

When param is char *,dispatch to func_string.

when it's int,dispatch to func_int

I think there may be a solution to this,as variable types are known at compile time..

like image 598
lexer Avatar asked Aug 31 '11 11:08

lexer


People also ask

What is generic macro?

One of the most hyped features of C11 is the _Generic() keyword for macros. This keyword kind of allows type-generic macros by letting you write a macro that selects between different implementations based on the types of their arguments.

How do you declare a macro in C?

To define a macro that uses arguments, you insert parameters between the pair of parentheses in the macro definition that make the macro function-like. The parameters must be valid C identifiers, separated by commas and optionally whitespace.

Can you do generics in C?

Unlike C++ and Java, C doesn't support generics. How to create a linked list in C that can be used for any data type? In C, we can use a void pointer and a function pointer to implement the same functionality. The great thing about void pointer is it can be used to point to any data type.

What is generic selection in C?

A generic selection is a primary expression. Its type and value depend on the selected generic association. The following diagram shows the generic selection syntax: _Generic ( assignment-expression , , type-name : assignment-expression default : assignment-expression 1 )


2 Answers

You can test for the characteristics of the types.

For example, int can hold a negative value, while char* can't. So if ((typeof(param))-1) < 0, param is unsigned:

if (((typeof(param))-1) < 0) {
    do_something_with_int();
} else {
    do_something_with_char_p();
}

The compiler obviously optimizes this out.

Try it here: http://ideone.com/et0v1

This would be even easier if the types had different sizes. For example, if you want to write a generic macro than can handle different character sizes:

if (sizeof(param) == sizeof(char)) {
    /* ... */
} else if (sizeof(param) == sizeof(char16_t)) {
    /* ... */
} else if (sizeof(param) == sizeof(char32_t)) {
    /* ... */
} else {
   assert("incompatible type" && 0);
}

GCC has a __builtin_types_compatible_p() builtin function that can check for types compatibility:

if (__builtin_types_compatible_p(typeof(param), int)) {
    func_int(param);
} else if (__builtin_types_compatible_p(typeof(param), char*)) {
    func_string(param);
}

Try it here: http://ideone.com/lEmYE

You can put this in a macro to achieve what you are trying to do:

#define FUNC(param) ({                                                \
    if (__builtin_types_compatible_p(typeof(param), int)) {           \
        func_int(param);                                              \
    } else if (__builtin_types_compatible_p(typeof(param), char*)) {  \
        func_string(param);                                           \
    }                                                                 \
})

(The ({...}) is a GCC's statement expression, it allows a group of statements to be a rvalue.

The __builtin_choose_expr() builtin can choose the expression to compile. With __builtin_types_compatible_p this allows to trigger an error at compile-time if the type of param is not compatible with both int and char*: (by compiling somehting invalid in this case)

#define FUNC(param)                                                        \ 
    __builtin_choose_expr(__builtin_types_compatible_p(typeof(param), int) \ 
        , func_int(param)                                                  \ 
        , __builtin_choose_expr(__builtin_types_compatible_p(typeof(param), char*) \ 
            , func_string(param)                                           \ 
            , /* The void expression results in a compile-time error       \ 
                 when assigning the result to something.  */               \ 
            ((void)0)                                                      \ 
        )                                                                  \ 
    )

This is actually a slightly modified example from __builtin_choose_expr docs.

like image 161
Arnaud Le Blanc Avatar answered Sep 21 '22 06:09

Arnaud Le Blanc


This will be possible with C1X but not in the current standard.

It will look like this:

#define cbrt(X) _Generic((X), long double: cbrtl, \
                          default: cbrt, \
                          float: cbrtf)(X)
like image 36
Constantinius Avatar answered Sep 20 '22 06:09

Constantinius