有没有办法在C中实现函数重载?我正在寻找简单的函数被重载像
foo (int a)
foo (char b)
foo (float c , int d)
我认为没有直接的方法;我在寻找变通办法,如果有的话。
有没有办法在C中实现函数重载?我正在寻找简单的函数被重载像
foo (int a)
foo (char b)
foo (float c , int d)
我认为没有直接的方法;我在寻找变通办法,如果有的话。
当前回答
有以下几种可能性:
Printf样式函数(类型作为参数) Opengl风格函数(输入函数名) c++的c子集(如果你能使用c++编译器)
其他回答
有以下几种可能性:
Printf样式函数(类型作为参数) Opengl风格函数(输入函数名) c++的c子集(如果你能使用c++编译器)
如果你的编译器是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)
这可能没有任何帮助,但如果你使用clang,你可以使用overloadable属性-即使在编译为C时也可以
http://clang.llvm.org/docs/AttributeReference.html#overloadable
头
extern void DecodeImageNow(CGImageRef image, CGContextRef usingContext) __attribute__((overloadable));
extern void DecodeImageNow(CGImageRef image) __attribute__((overloadable));
实现
void __attribute__((overloadable)) DecodeImageNow(CGImageRef image, CGContextRef usingContext { ... }
void __attribute__((overloadable)) DecodeImageNow(CGImageRef image) { ... }
Yes!
自从提出这个问题以来,由于在C11中添加了_Generic关键字,标准C(没有扩展)已经有效地获得了对函数重载(不是操作符)的支持。(GCC从4.9版开始支持)
(重载并不是真正“内置”在问题中显示的方式,但它是非常容易实现这样的工作。)
_Generic是一个编译时操作符,与sizeof和_Alignof属于同一家族。在标准章节6.5.1.1中有描述。它接受两个主要参数:一个表达式(在运行时不会计算)和一个类型/表达式关联列表,它看起来有点像一个开关块。_Generic获取表达式的整体类型,然后“切换”它以在列表中选择其类型的最终结果表达式:
_Generic(1, float: 2.0,
char *: "2",
int: 2,
default: get_two_object());
上面的表达式求值为2——控制表达式的类型是int,所以它选择与int相关的表达式作为值。这些在运行时都不保留。(default子句是可选的:如果你关闭它并且类型不匹配,它将导致编译错误。)
这对于函数重载很有用,因为它可以由C预处理器插入,并根据传递给控制宏的参数类型选择结果表达式。因此(来自C标准的例子):
#define cbrt(X) _Generic((X), \
long double: cbrtl, \
default: cbrt, \
float: cbrtf \
)(X)
这个宏实现了一个重载的cbrt操作,通过将参数的类型分派给宏,选择一个适当的实现函数,然后将原始宏参数传递给该函数。
为了实现你原来的例子,我们可以这样做:
foo_int (int a)
foo_char (char b)
foo_float_int (float c , int d)
#define foo(_1, ...) _Generic((_1), \
int: foo_int, \
char: foo_char, \
float: _Generic((FIRST(__VA_ARGS__,)), \
int: foo_float_int))(_1, __VA_ARGS__)
#define FIRST(A, ...) A
在本例中,我们可以为第三种情况使用默认的:association,但这并没有演示如何将原则扩展到多个参数。最终的结果是,你可以在你的代码中使用foo(…),而不用担心它的参数类型。
对于更复杂的情况,例如函数重载更大数量的参数,或者变化的数量,你可以使用实用程序宏自动生成静态分派结构:
void print_ii(int a, int b) { printf("int, int\n"); }
void print_di(double a, int b) { printf("double, int\n"); }
void print_iii(int a, int b, int c) { printf("int, int, int\n"); }
void print_default(void) { printf("unknown arguments\n"); }
#define print(...) OVERLOAD(print, (__VA_ARGS__), \
(print_ii, (int, int)), \
(print_di, (double, int)), \
(print_iii, (int, int, int)) \
)
#define OVERLOAD_ARG_TYPES (int, double)
#define OVERLOAD_FUNCTIONS (print)
#include "activate-overloads.h"
int main(void) {
print(44, 47); // prints "int, int"
print(4.4, 47); // prints "double, int"
print(1, 2, 3); // prints "int, int, int"
print(""); // prints "unknown arguments"
}
因此,通过一些努力,您可以减少样板文件的数量,使其看起来非常像具有原生重载支持的语言。
顺便说一句,在C99中已经可以重载参数的数量(而不是类型)。
注意,C计算类型的方式可能会让您出错。这将选择foo_int,如果你试图传递它一个字符字面量,例如,如果你想让你的重载支持字符串字面量,你需要一点混乱。不过总体来说还是很酷的。
如前所述,c不支持重载。解决这个问题的常用方法是让函数接受带标签的联合。这是通过一个struct形参实现的,其中struct本身由某种类型指示符(例如枚举)和不同类型值的并集组成。例子:
#include <stdio.h>
typedef enum {
T_INT,
T_FLOAT,
T_CHAR,
} my_type;
typedef struct {
my_type type;
union {
int a;
float b;
char c;
} my_union;
} my_struct;
void set_overload (my_struct *whatever)
{
switch (whatever->type)
{
case T_INT:
whatever->my_union.a = 1;
break;
case T_FLOAT:
whatever->my_union.b = 2.0;
break;
case T_CHAR:
whatever->my_union.c = '3';
}
}
void printf_overload (my_struct *whatever) {
switch (whatever->type)
{
case T_INT:
printf("%d\n", whatever->my_union.a);
break;
case T_FLOAT:
printf("%f\n", whatever->my_union.b);
break;
case T_CHAR:
printf("%c\n", whatever->my_union.c);
break;
}
}
int main (int argc, char* argv[])
{
my_struct s;
s.type=T_INT;
set_overload(&s);
printf_overload(&s);
s.type=T_FLOAT;
set_overload(&s);
printf_overload(&s);
s.type=T_CHAR;
set_overload(&s);
printf_overload(&s);
}