如何在C中实现通用宏?

发布于 2024-12-02 07:08:50 字数 183 浏览 0 评论 0 原文

FUNC(param);

paramchar *时,调度到func_string

当它是 int 时,调度到 func_int

我认为可能有一个解决方案,因为变量类型在编译时是已知的。

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..

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

野鹿林 2024-12-09 07:08:50

这对于 C1X 是可能的,但在当前标准中是不可能的。

它看起来像这样:

#define cbrt(X) _Generic((X), long double: cbrtl, \
                          default: cbrt, \
                          float: cbrtf)(X)

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)
生生不灭 2024-12-09 07:08:50

编译器知道变量类型,但预处理器不知道(预处理器将代码简单地视为非结构化文本标记流,并且仅对其执行简单的替换操作)。所以恐怕你无法用 C 宏来实现这一点。

在 C++ 中,他们发明了模板来解决此类问题(以及更多问题)。

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).

冷心人i 2024-12-09 07:08:50

您可以测试类型的特征。

例如,int 可以保存负值,而 char* 则不能。所以如果 ((typeof(param))-1) ((typeof(param))-1) ((typeof(param))-1) 0param 是无符号的:

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

编译器显然对此进行了优化。

在这里尝试一下:http://ideone.com/et0v1

如果类型具有不同的大小,这会更容易。例如,如果您想编写一个可以处理不同字符大小的通用宏:

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 有一个 __builtin_types_兼容_p() 可以检查类型兼容性的内置函数:

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

在这里尝试一下:http://ideone.com/lEmYE

您可以将其放入宏中以实现您想要执行的操作

#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);                                           \
    }                                                                 \
})

:( ({...}) 是一个 GCC的语句表达式,它允许一组语句作为右值。

__builtin_choose_expr()builtin 可以选择要编译的表达式。 __builtin_types_兼容_p 如果参数的类型与 intchar* 都不兼容,则允许在编译时触发错误:(通过在这种情况下编译无效的内容

#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)                                                      \ 
        )                                                                  \ 
    )

)实际上是 __builtin_choose_expr 文档

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.

我们的影子 2024-12-09 07:08:50

C89 / ANSI C 中不可能运行时检查类型,但 gcc 的扩展允许这样做。 typeof 或者类似的东西,如果我记得的话。我在 Linux 内核中见过一次。

kernel.h 中:

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

看一下这篇文章: GCC Linux 内核中的黑客

当我第一次看到这个时,我实际上在这里问了一个问题:

kernel.h 中的 min 宏

我不太确定您将如何使用它来解决您的问题,但它值得一看。

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.

谁人与我共长歌 2024-12-09 07:08:50

你不能用宏来做到这一点。宏的值在编译时被替换并且不被解释。他们只是替代品。

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

以酷 2024-12-09 07:08:50

变量类型确实在编译时已知,但是宏扩展在编译之前发生。我建议你实现 2 个重载函数而不是宏。

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.

夜灵血窟げ 2024-12-09 07:08:50

我对泛型的定义:
结构化抽象类型,只能用其他具体类型的输入来完全定义,

这听起来就像一个宏,

请原谅伪 c 代码,我的 c 生锈了

#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;
}

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;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文