有没有办法在C中实现函数重载?我正在寻找简单的函数被重载像

foo (int a)  
foo (char b)  
foo (float c , int d)

我认为没有直接的方法;我在寻找变通办法,如果有的话。


当前回答

如果你的编译器是gcc的,并且你不介意每次添加新的重载时进行手动更新,你可以做一些宏魔术,并在调用方方面得到你想要的结果。但这是有可能的

看一下__builtin_types_compatible_p,然后用它定义一个宏,做类似的事情

#define foo(a) \
((__builtin_types_compatible_p(int, a)?foo(a):(__builtin_types_compatible_p(float, a)?foo(a):)

但是很讨厌,就是不要

编辑:C1X将获得对类型泛型表达式的支持,它们看起来像这样:

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

其他回答

有以下几种可能性:

Printf样式函数(类型作为参数) Opengl风格函数(输入函数名) c++的c子集(如果你能使用c++编译器)

就你的意思来说——不,你不能。

你可以像这样声明一个va_arg函数

Void my_func(char*格式,…);

,但是您需要在第一个参数中传递一些关于变量数量及其类型的信息-就像printf()所做的那样。

如果你的编译器是gcc的,并且你不介意每次添加新的重载时进行手动更新,你可以做一些宏魔术,并在调用方方面得到你想要的结果。但这是有可能的

看一下__builtin_types_compatible_p,然后用它定义一个宏,做类似的事情

#define foo(a) \
((__builtin_types_compatible_p(int, a)?foo(a):(__builtin_types_compatible_p(float, a)?foo(a):)

但是很讨厌,就是不要

编辑:C1X将获得对类型泛型表达式的支持,它们看起来像这样:

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

Leushenko的答案真的很酷——仅仅是:foo的例子没有使用GCC编译,它在foo(7)处失败,绊倒了FIRST宏和实际的函数调用((_1,__VA_ARGS__),剩下一个多余的逗号。此外,如果我们想提供额外的重载,比如foo(double),就会遇到麻烦。

因此,我决定进一步详细说明答案,包括允许void重载(foo(void) -这造成了相当大的麻烦…)

现在的想法是:在不同的宏中定义多个泛型,让我们根据参数的数量选择正确的一个!

参数的数量很简单,基于这个答案:

#define foo(...) SELECT(__VA_ARGS__)(__VA_ARGS__)

#define SELECT(...) CONCAT(SELECT_, NARG(__VA_ARGS__))(__VA_ARGS__)
#define CONCAT(X, Y) CONCAT_(X, Y)
#define CONCAT_(X, Y) X ## Y

这很好,我们解析为SELECT_1或SELECT_2(或更多参数,如果你想要/需要它们),所以我们只需要适当的定义:

#define SELECT_0() foo_void
#define SELECT_1(_1) _Generic ((_1),    \
        int: foo_int,                   \
        char: foo_char,                 \
        double: foo_double              \
)
#define SELECT_2(_1, _2) _Generic((_1), \
        double: _Generic((_2),          \
                int: foo_double_int     \
        )                               \
)

好吧,我已经添加了void重载-然而,这实际上是不被C标准覆盖的,它不允许空的可变参数,也就是说,我们依赖于编译器扩展!

首先,一个空宏调用(foo())仍然产生一个令牌,但是是一个空令牌。计数宏实际上返回1而不是0,即使是空宏调用。我们可以“轻松”消除这个问题,如果我们有条件地在__VA_ARGS__后面加上逗号,这取决于列表是否为空:

#define NARG(...) ARG4_(__VA_ARGS__ COMMA(__VA_ARGS__) 4, 3, 2, 1, 0)

这看起来很简单,但是逗号宏是一个相当沉重的宏;幸运的是,这个话题已经在Jens Gustedt的博客中提到了(谢谢,Jens)。基本的技巧是,如果函数宏后面没有括号,就不会展开,要进一步解释,可以看看Jens的博客…我们只需要根据需要稍微修改一下宏(为了简洁起见,我将使用更短的名称和更少的参数)。

#define ARGN(...) ARGN_(__VA_ARGS__)
#define ARGN_(_0, _1, _2, _3, N, ...) N
#define HAS_COMMA(...) ARGN(__VA_ARGS__, 1, 1, 1, 0)

#define SET_COMMA(...) ,

#define COMMA(...) SELECT_COMMA             \
(                                           \
        HAS_COMMA(__VA_ARGS__),             \
        HAS_COMMA(__VA_ARGS__ ()),          \
        HAS_COMMA(SET_COMMA __VA_ARGS__),   \
        HAS_COMMA(SET_COMMA __VA_ARGS__ ()) \
)

#define SELECT_COMMA(_0, _1, _2, _3) SELECT_COMMA_(_0, _1, _2, _3)
#define SELECT_COMMA_(_0, _1, _2, _3) COMMA_ ## _0 ## _1 ## _2 ## _3

#define COMMA_0000 ,
#define COMMA_0001
#define COMMA_0010 ,
// ... (all others with comma)
#define COMMA_1111 ,

现在我们没事了……

完整的代码在一个块:

/*
 * demo.c
 *
 *  Created on: 2017-09-14
 *      Author: sboehler
 */

#include <stdio.h>

void foo_void(void)
{
    puts("void");
}
void foo_int(int c)
{
    printf("int: %d\n", c);
}
void foo_char(char c)
{
    printf("char: %c\n", c);
}
void foo_double(double c)
{
    printf("double: %.2f\n", c);
}
void foo_double_int(double c, int d)
{
    printf("double: %.2f, int: %d\n", c, d);
}

#define foo(...) SELECT(__VA_ARGS__)(__VA_ARGS__)

#define SELECT(...) CONCAT(SELECT_, NARG(__VA_ARGS__))(__VA_ARGS__)
#define CONCAT(X, Y) CONCAT_(X, Y)
#define CONCAT_(X, Y) X ## Y

#define SELECT_0() foo_void
#define SELECT_1(_1) _Generic ((_1), \
        int: foo_int,                \
        char: foo_char,              \
        double: foo_double           \
)
#define SELECT_2(_1, _2) _Generic((_1), \
        double: _Generic((_2),          \
                int: foo_double_int     \
        )                               \
)

#define ARGN(...) ARGN_(__VA_ARGS__)
#define ARGN_(_0, _1, _2, N, ...) N

#define NARG(...) ARGN(__VA_ARGS__ COMMA(__VA_ARGS__) 3, 2, 1, 0)
#define HAS_COMMA(...) ARGN(__VA_ARGS__, 1, 1, 0)

#define SET_COMMA(...) ,

#define COMMA(...) SELECT_COMMA             \
(                                           \
        HAS_COMMA(__VA_ARGS__),             \
        HAS_COMMA(__VA_ARGS__ ()),          \
        HAS_COMMA(SET_COMMA __VA_ARGS__),   \
        HAS_COMMA(SET_COMMA __VA_ARGS__ ()) \
)

#define SELECT_COMMA(_0, _1, _2, _3) SELECT_COMMA_(_0, _1, _2, _3)
#define SELECT_COMMA_(_0, _1, _2, _3) COMMA_ ## _0 ## _1 ## _2 ## _3

#define COMMA_0000 ,
#define COMMA_0001
#define COMMA_0010 ,
#define COMMA_0011 ,
#define COMMA_0100 ,
#define COMMA_0101 ,
#define COMMA_0110 ,
#define COMMA_0111 ,
#define COMMA_1000 ,
#define COMMA_1001 ,
#define COMMA_1010 ,
#define COMMA_1011 ,
#define COMMA_1100 ,
#define COMMA_1101 ,
#define COMMA_1110 ,
#define COMMA_1111 ,

int main(int argc, char** argv)
{
    foo();
    foo(7);
    foo(10.12);
    foo(12.10, 7);
    foo((char)'s');

    return 0;
}

如果你的编译器支持,试着将这些函数声明为extern " c++ ", http://msdn.microsoft.com/en-us/library/s6y4zxec(VS.80).aspx