有没有办法在C中实现函数重载?我正在寻找简单的函数来重载像
foo (int a) foo (char b) foo (float c , int d)
我认为没有直接的方式; 我正在寻找解决方法,如果存在的话.
是!
自问这个问题以来,由于在C11中添加了关键字,标准C(无扩展)已经有效地获得了对函数重载(而不是运算符)的支持_Generic
.(自4.9版以来在GCC中受支持)
(重载不是真正"内置"的问题所示的方式,但实现类似的东西很容易.)
_Generic
是sizeof
和_Alignof
.一样的系列中的编译时运算符.它在标准的6.5.1.1节中描述.它接受两个主要参数:表达式(在运行时不会被计算),以及看起来有点像switch
块的类型/表达式关联列表._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
在这种情况下,我们可以使用default:
第三种情况的关联,但这并没有证明如何将原则扩展到多个参数.最终的结果是你可以foo(...)
在你的代码中使用而不必担心(很多[1])它的参数类型.
对于更复杂的情况,例如重载大量参数的函数或不同的数字,您可以使用实用程序宏自动生成静态调度结构:
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中重载参数(不是类型)的数量.
[1]请注意,C评估类型的方式可能会让你失望.foo_int
例如,如果您尝试将字符文字传递给它,则会选择,如果您希望重载支持字符串文字,则需要稍微混淆一下.虽然整体还很酷.
可能性很小:
printf样式函数(作为参数输入)
opengl样式函数(在函数名中输入)
c c ++的子集(如果你可以使用c ++编译器)
如前所述,C语言不支持你所指的意义上的重载.解决问题的一个常用习惯是使函数接受标记的联合.这是通过struct
参数实现的,其中struct
自身由某种类型指示符组成,例如enum
a union
和不同类型的值.例:
#includetypedef 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); }
如果您的编译器是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)
这是我发现在C中演示函数重载的最清晰,最简洁的例子:
#include#include #include int addi(int a, int b) { return a + b; } char *adds(char *a, char *b) { char *res = malloc(strlen(a) + strlen(b) + 1); strcpy(res, a); strcat(res, b); return res; } #define add(a, b) _Generic(a, int: addi, char*: adds)(a, b) int main(void) { int a = 1, b = 2; printf("%d\n", add(a, b)); // 3 char *c = "hello ", *d = "world"; printf("%s\n", add(c, d)); // hello world return 0; }
https://gist.github.com/barosl/e0af4a92b2b8cabd05a7
是的,有点.
在这里你举例:
void printA(int a){ printf("Hello world from printA : %d\n",a); } void printB(const char *buff){ printf("Hello world from printB : %s\n",buff); } #define Max_ITEMS() 6, 5, 4, 3, 2, 1, 0 #define __VA_ARG_N(_1, _2, _3, _4, _5, _6, N, ...) N #define _Num_ARGS_(...) __VA_ARG_N(__VA_ARGS__) #define NUM_ARGS(...) (_Num_ARGS_(_0, ## __VA_ARGS__, Max_ITEMS()) - 1) #define CHECK_ARGS_MAX_LIMIT(t) if(NUM_ARGS(args)>t) #define CHECK_ARGS_MIN_LIMIT(t) if(NUM_ARGS(args) #define print(x , args ...) \ CHECK_ARGS_MIN_LIMIT(1) printf("error");fflush(stdout); \ CHECK_ARGS_MAX_LIMIT(4) printf("error");fflush(stdout); \ ({ \ if (__builtin_types_compatible_p (typeof (x), int)) \ printA(x, ##args); \ else \ printB (x,##args); \ }) int main(int argc, char** argv) { int a=0; print(a); print("hello"); return (EXIT_SUCCESS); }
它将从printA和printB输出0和hello ..
以下方法类似于a2800276,但添加了一些C99宏魔法:
// we need `size_t` #include// argument types to accept enum sum_arg_types { SUM_LONG, SUM_ULONG, SUM_DOUBLE }; // a structure to hold an argument struct sum_arg { enum sum_arg_types type; union { long as_long; unsigned long as_ulong; double as_double; } value; }; // determine an array's size #define count(ARRAY) ((sizeof (ARRAY))/(sizeof *(ARRAY))) // this is how our function will be called #define sum(...) _sum(count(sum_args(__VA_ARGS__)), sum_args(__VA_ARGS__)) // create an array of `struct sum_arg` #define sum_args(...) ((struct sum_arg []){ __VA_ARGS__ }) // create initializers for the arguments #define sum_long(VALUE) { SUM_LONG, { .as_long = (VALUE) } } #define sum_ulong(VALUE) { SUM_ULONG, { .as_ulong = (VALUE) } } #define sum_double(VALUE) { SUM_DOUBLE, { .as_double = (VALUE) } } // our polymorphic function long double _sum(size_t count, struct sum_arg * args) { long double value = 0; for(size_t i = 0; i < count; ++i) { switch(args[i].type) { case SUM_LONG: value += args[i].value.as_long; break; case SUM_ULONG: value += args[i].value.as_ulong; break; case SUM_DOUBLE: value += args[i].value.as_double; break; } } return value; } // let's see if it works #include int main() { unsigned long foo = -1; long double value = sum(sum_long(42), sum_ulong(foo), sum_double(1e10)); printf("%Le\n", value); return 0; }
这可能没有任何帮助,但如果您使用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) { ... }
从某种意义上说,你的意思是 - 不,你不能.
你可以声明一个va_arg
像这样的函数
void my_func(char* format, ...);
,但你需要在第一个参数中传递关于变量数量及其类型的某种信息 - 就像printf()
这样.
通常会在名称后附加或附加一个表示类型的疣.你可以在某些情况下使用宏,但它取决于你想要做什么.C中没有多态性,只有强制.
可以使用宏完成简单的通用操作:
#define max(x,y) ((x)>(y)?(x):(y))
如果您的编译器支持typeof,则可以在宏中放置更复杂的操作.然后,您可以使用符号foo(x)来支持不同类型的相同操作,但不能改变不同重载之间的行为.如果你想要实际的功能而不是宏,你可以将类型粘贴到名称并使用第二个粘贴来访问它(我还没有尝试过).