How to implement a generic macro in C?

筅森魡賤 提交于 2019-12-04 03:08:52

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)

Variable types are known to the compiler, but not to the preprocessor (which sees the code simply as unstructured text a stream of tokens, and performs only simple replacement operations on it). So I am afraid you can't achieve this with C macros.

In C++, they invented templates to solve such problems (and more).

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.

Robert S. Barnes

There is no possibility to run time check types in C89 / ANSI C, but there is an extension to gcc which allows it. typeof or something along those lines if I remember. I saw it in the Linux Kernel once.

In kernel.h:

#define min(x, y) ({                \
typeof(x) _min1 = (x);          \
typeof(y) _min2 = (y);          \
(void) (&_min1 == &_min2);      \
_min1 < _min2 ? _min1 : _min2; })

Take a look at this article: GCC hacks in the Linux kernel

When I first saw this I actually asked a question here on SO about:

min macro in kernel.h

I'm not quite sure exactly how you would use it to solve your problem, but it's something worth taking a look at.

You can't do this with a macro. Macro's value are substituted at compile time and are not intepreted. They are just substitutions.

Variable types are indeed known at compile time, however macro expansion takes place before compilation. I suggest you implement 2 overloaded functions instead of a macro.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!