我想拥有可以包含可选参数的函数。当然这不能用C来完成,但它可能有一些宏观的魔力:
#define _macroWith1Arg(_0, _1, macroName, ...) _ ## macroName
#define _macroWith2Args(_0, _1, _2, macroName, ...) _ ## macroName
#define _macroWith3Args(_0, _1, _2, _3, macroName, ...) _ ## macroName
#define macroWith1Arg(macroName, ...) _macroWith1Arg(_0, __VA_ARGS__, macroName ## _1, macroName ## _0)(__VA_ARGS__)
#define macroWith2Args(macroName, ...) _macroWith2Args(_0, __VA_ARGS__, macroName ## _2, macroName ## _1, macroName ## _0)(__VA_ARGS__)
#define macroWith3Args(macroName, ...) _macroWith3Args(_0, __VA_ARGS__, macroName ## _3, macroName ## _2, macroName ## _1, macroName ## _0)(__VA_ARGS__)
#define _sum_1(_1) (_1)
#define _sum_2(_1, _2) (_1) + (_2)
#define sum(...) macroWith2Args(sum, __VA_ARGS__)
fprintf(stderr, "%d ", sum(1)); // Prints 1
fprintf(stderr, "%d ", sum(1, 2)); // Prints 3
我喜欢它看起来的样子:它看起来像带有可选参数的普通函数。但缺点是你不能有像sum
这样的宏的函数指针。所以我听说你应该把这个名字大写,把它显式地标记为一个宏(比如“SUM”),以避免混淆。
现在我的问题是:这个命名约定是否真的有必要,我的意思是标准C用errno
作为例子(至少在我的平台上)?我认为大多数IDE都可以识别宏并将其突出显示。我真的很想听听你对此的看法。
修改:
我找到了一种方法来实现你可以使用函数指针:
int my_sum(int, int);
#define _sum_1(_1) my_sum(_1, 0)
#define _sum_2(_1, _2) my_sum(_1, _2)
#define sum(...) macroWith2Args(sum, __VA_ARGS__)
int (*sum)(int, int) = my_sum;
// Use it as a "function":
fprintf(stderr, "%d ", sum(1, 2)); // Prints 3
// Take a function pointer:
int (*sumptr)(int, int) = sum;
fprintf(stderr, "%d ", sumptr(1, 0)); // Prints 1
答案 0 :(得分:5)
在C中(预处理器是其中的一部分),您可以在编辑中执行更多操作:宏和函数可以具有相同的名称。虽然在应用程序代码中很少这样做,但C库本身可以拥有它。这里标准函数可以作为宏实现,但也必须提供具有相同名称的符号。
因此标准本身并不遵守宏名称应全部为大写的规则。
我认为如果你在实施中小心,
然后使用小写标识符应该没有问题。
由此确保
并且在您的所有实现中都表现为就好像您的宏所在的函数。
顺便说一下,您发布的代码不符合标准。以下划线开头的标识符保留在文件范围中。这不是惯例,而是必要的。
答案 1 :(得分:4)
命名约定通常不是必要。它们的存在是为了向其他程序员传达信息(包括将来几周/几个月)。当另一个程序员看到
sum(a, b, c, d);
他们会立即认为这是一个需要4个参数的正常功能。但是,如果他们看到
SUM(a, b, c, d);
然后他们会知道它是一个宏(假设他们熟悉C命名约定)。毕竟,您并不总是在IDE中查看知道您已定义的宏和功能的文件。人们将在diff工具和文本编辑器中看到您的代码。他们通常会期望所有大写字母中的宏都不是宏以外的所有大写字母。
答案 2 :(得分:2)
约定在C中工作较少,因为C工具较少,编译器工作较少,程序员必须灵活地实现某些目的。说宏的约定是大写+下划线,但如果你想让人们认为某些宏是函数,那么你最好不遵循这个约定。实际上,有些库假定不遵循这个约定而在struct中伪造一个字段。
你提到了errno
。这是一个很好的例子。它本质上是一个宏(glibc),返回一个左值,所以你可以写errno = 0;
。所以通过这种方式,图书馆提供商让你认为errno
是一个全局变量(这曾经是我认为的那样。),但事实并非如此。 C程序员倾向于灵活并且发挥一些合理的技巧。它与Java或C#不同,这种面向企业的语言需要程序员严格遵循惯例。
答案 3 :(得分:0)
// notice that the handling of a variable argument list
// is simple, built into C, and does not require a bunch of
// home grown macros.
// this function will take the number of values to average
// followed by all of the numbers to average
double average ( int num, ... )
{
va_list arguments; // A place to store the list of arguments
double sum = 0;
va_start ( arguments, num ); // Initializing arguments to store all values after num
for ( int x = 0; x < num; x++ ) // Loop until all numbers are added
{
sum += va_arg ( arguments, double ); // Adds the next value in argument list to sum.
}
va_end ( arguments ); // Cleans up the list
return sum / num; // Returns the average
}