开发者

How to implement a generic macro in C?

FU开发者_如何学JAVANC(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..


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.


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.


my definition of a generic: a structured abstract type which can only be fully defined with an input of other concrete types

this sounds exactly like a macro to me

pardon the psudo c code, my c is rusty

#include <stdio.h>

// todo: ret=self needs vec3##generic_t##_copy(self, ret);
// not to mention we should probably be using __builtin_add_overflow
// __builtin_add_overflow might actually itself be a reasonably generics method example
// please bear with me
#define GENERIC_VEC3_ADD(generic_t) \
    generic_t vec3##generic_t##_add(generic_t self, generic_t other) {\
        generic_t ret = self;\
        ret[0] += other [0];;\
        ret[1] += other [1];\
        ret[2] += other [2];\
        return ret;\
    }

#define GENERIC_VEC3_FREPR(generic_t, printf_ts) \
    int vec3##generic_t##_frepr(generic_t self, FILE fd)\
        rerurn fprintf(fd, "<vec3##generic_t (##printf_ts##, printf_ts##, printf_ts##)>", \
        self[0], self[1], self[2]);\
    }

// here is the generic typedef, with some methods
#define GENERIC_VEC3(genetic_t, printf_ts) \
    typedef vec3##generic_t generic_t[3];\
    GENERIC_VEC3_ADD(generic_t) \
    GENERIC_VEC3_FREPR(generic_t, printf_ts)

// later we decide what types we want this genic for
GENERIC_VEC3(int, %ul)

// and use our generic
int main()
{
    vec3int foo = { 1, 2, 3 };;
    vec3int bar = { 1, 2, 3 };;
    vec3int sum = vec3int_add(foo, bar);
    vec3int_frepr(sum, stderr);
    fprintf(stderr, "\n");
    exit EXIT_SUCCESS;
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜