这看起来很简单,但 COMMA 宏是一个沉重的宏;幸运的是,这个话题已经在blog of Jens Gustedt中提到了(谢谢,Jens) . 基本的技巧是,如果没有后跟括号,则不扩展函数宏,为了进一步解释,看看Jens ' blog... We just have to modify the macros a little to our needs (I' m将使用更短的名称和更少的参数以简洁起见) .
// we need `size_t`
#include <stddef.h>
// 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 <stdio.h>
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;
}
14 回答
通常会在名称后附加或附加一个表示类型的疣 . 你可以通过宏来逃避某些情况,但这取决于你想要做什么 . 没有C中的多态性,只有强制 .
可以使用宏完成简单的通用操作:
如果您的编译器支持typeof,则可以在宏中放置更复杂的操作 . 然后你可以使用符号foo(x)来支持不同类型的相同操作,但是你可以尝试) .
Leushenko's answer真的很酷 - 单独:
foo
示例不能用GCC编译,它在foo(7)
失败,绊倒了FIRST
宏和实际函数调用((_1, __VA_ARGS__)
,保留了剩余逗号 . 此外,如果我们想要的话,我们遇到麻烦提供额外的重载,例如foo(double)
.所以我决定进一步阐述答案,包括允许空载过载(
foo(void)
- 这引起了一些麻烦......) .现在的想法是:在不同的宏中定义多个通用,并根据参数的数量选择正确的通用!
根据this answer,参数数量非常简单:
这很好,我们解决了
SELECT_1
或SELECT_2
(或更多的参数,如果你想要/需要它们),所以我们只需要适当的定义:好吧,我已经添加了void重载 - 但是,这个实际上并没有被C标准覆盖,它不允许空的可变参数,i . 即然后我们依赖编译器扩展!
最初,空的宏调用(
foo()
)仍然会生成一个令牌,但是一个空令牌 . 因此,即使在空宏调用上,计数宏实际上返回1而不是0 . 我们可以"easily"消除这个问题,如果我们有条件地放置__VA_ARGS__
之后的逗号,取决于列表是否为空:这看起来很简单,但
COMMA
宏是一个沉重的宏;幸运的是,这个话题已经在blog of Jens Gustedt中提到了(谢谢,Jens) . 基本的技巧是,如果没有后跟括号,则不扩展函数宏,为了进一步解释,看看Jens ' blog... We just have to modify the macros a little to our needs (I' m将使用更短的名称和更少的参数以简洁起见) .现在我们很好......
一个块中的完整代码:
难道你不能只使用C而不使用除此之外的所有其他C功能吗?
如果仍然没有严格的C,那么我会推荐variadic functions .
如果编译器支持,请尝试将这些函数声明为
extern "C++"
,http://msdn.microsoft.com/en-us/library/s6y4zxec(VS.80).aspx可能性很小:
printf样式函数(作为参数输入)
opengl样式函数(输入函数名)
c c的子集(如果可以使用c编译器)
Yes!
自从提出这个问题以来,由于在C11中添加了
_Generic
关键字,标准C(无扩展)已经有效地获得了对函数重载(而不是运算符)的支持 . (自4.9版以来在GCC中受支持)(重载不是真正“内置”的问题所示的方式,但实现类似的东西很容易 . )
_Generic
是与sizeof
和_Alignof
属于同一系列的编译时运算符 . 它在标准的6.5.1.1节中描述 . 它接受两个主要参数:表达式(在运行时不会被计算),以及看起来有点像switch
块的类型/表达式关联列表 ._Generic
获取表达式的整体类型,然后在其上"switches"以在列表中为其类型选择最终结果表达式:上面的表达式求值为
2
- 控制表达式的类型是int
,因此它选择与int
关联的表达式作为值 . 这一切都没有在运行时 . (default
子句是可选的:如果将其关闭且类型不匹配,则会导致编译错误 . )这对于函数重载很有用,它可以由C预处理器插入,并根据传递给控制宏的参数类型选择结果表达式 . 所以(来自C标准的例子):
这个宏实现了一个重载的
cbrt
操作,通过调度宏的参数类型,选择适当的实现函数,然后将原始宏参数传递给该函数 .因此,为了实现您的原始示例,我们可以这样做:
在这种情况下,我们可以在第三种情况下使用
default:
关联,但这并未说明如何将原则扩展为多个参数 . 最终结果是您可以在代码中使用foo(...)
而不必担心(多[1])关于其参数的类型 .对于更复杂的情况,例如函数重载大量的参数或不同的数字,你可以使用实用程序宏来自动生成静态调度结构:
(implementation here)因此,通过一些努力,您可以减少样板量,使其看起来非常像具有本机支持重载的语言 .
顺便说一下,it was already possible要重载C99中的参数数量(不是类型) .
[1]请注意,C评估类型的方式可能会让你失望 . 如果你试图传递一个字符文字,这将选择
foo_int
,例如,如果你希望你的重载支持字符串文字,and you need to mess about a bit . 尽管如此,整体还是很酷 .正如已经说过的那样,C语言不支持您的意思重载 . 解决问题的一个常用习惯是使函数接受tagged union . 这是由
struct
参数实现的,其中struct
本身由某种类型指示符组成,例如enum
和union
的不同类型的值 . 例:如果您的编译器是gcc并且您不介意每次添加新的重载时都进行手动更新,那么您可以执行一些宏魔术并根据调用者获得所需的结果,但编写起来并不是很好...但它可能
看看__builtin_types_compatible_p,然后使用它来定义一个类似的宏
但是讨厌,只是不要
EDIT: C1X将获得类型通用表达式的支持,它们如下所示:
这是我发现在C中演示函数重载的最清晰,最简洁的例子:
https://gist.github.com/barosl/e0af4a92b2b8cabd05a7
是的,有点 .
在这里你举例:
它将从printA和printB输出0和hello ..
以下方法类似于a2800276,但添加了一些C99宏魔法:
这可能根本没用,但是如果你使用clang,你可以使用overloadable属性 - 这甚至在编译为C时也有效
http://clang.llvm.org/docs/AttributeReference.html#overloadable
头
履行
从某种意义上说,你的意思是 - 不,你不能 .
您可以声明
va_arg
函数void my_func(char* format, ...);
,但你需要在第一个参数中传递一些关于变量数量及其类型的信息 - 比如
printf()
.我希望下面的代码可以帮助您理解函数重载