macros可以通过参数的数量来重载吗?

这是如何工作的? 一个C99 / C ++ 11可变参数macros如何实现扩展到不同的事物,仅仅有多less个论点?

(编辑:查看现成解决scheme的结尾。)

为了得到一个超载的macros,首先我们需要一个macros来select几个实现。 这部分不使用可变macros。 然后一个可变的macros,通常统计其参数产生一个select器。 将参数计数插入调度程序会产生重载的macros。

注意:这个系统无法区分零和一个参数,因为没有参数和单个空参数没有区别。 他们都像MACRO()


要在实现之间进行select,请使用macros连接运算符和一系列类似函数的macros。

 #define select( selector, ... ) impl ## _ ## selector( __VA_ARGS__ ) #define impl_1() meh #define impl_2( abc, xyz ) # abc "wizza" xyz() //etc // usage: select( 1 ) => impl_1() => meh // select( 2, huz, bar ) => impl_2( huzza, bar ) => "huz" "wizza" bar() 

由于##运算符会抑制其参数的macros扩展,所以最好将其包含在另一个macros中。

 #define CAT( A, B ) A ## B #define SELECT( NAME, NUM ) CAT( NAME ## _, NUM ) 

为了计算参数,使用__VA_ARGS__来移动参数(这是一个聪明的部分):

 #define GET_COUNT( _1, _2, _3, _4, _5, _6 /* ad nauseam */, COUNT, ... ) COUNT #define VA_SIZE( ... ) GET_COUNT( __VA_ARGS__, 6, 5, 4, 3, 2, 1 ) 

图书馆编号:

 #define CAT( A, B ) A ## B #define SELECT( NAME, NUM ) CAT( NAME ## _, NUM ) #define GET_COUNT( _1, _2, _3, _4, _5, _6 /* ad nauseam */, COUNT, ... ) COUNT #define VA_SIZE( ... ) GET_COUNT( __VA_ARGS__, 6, 5, 4, 3, 2, 1 ) #define VA_SELECT( NAME, ... ) SELECT( NAME, VA_SIZE(__VA_ARGS__) )(__VA_ARGS__) 

用法:

 #define MY_OVERLOADED( ... ) VA_SELECT( MY_OVERLOADED, __VA_ARGS__ ) #define MY_OVERLOADED_1( X ) foo< X > #define MY_OVERLOADED_2( X, Y ) bar< X >( Y ) #define MY_OVERLOADED_3( X, Y, Z ) bang_ ## X< Y >.Z() 

我会张贴这篇评论Potatoswatter的post,但它太长,需要一个代码清单。

下面是一些Perl代码,用于生成一组意味着重载macros的macros。

 $ perl -le 'map{ $arity = $_; map { $ar = 2 + $arity + $_; $arm = $ar - 1; $arlist = join("", map{"A$_, "} 1..$arity); $warlist = "WHAT, $arlist"; @li = map {"_$_"} 0..$_; $lis = join(", ", @li); $lim = pop @li; $lims = join(", ", @li); print "#define FEI_${arity}A_$ar($warlist$lis) FEI_${arity}A_$arm($warlist$lims) WHAT($_, $arlist$lim)" } 1..3; print "" } 0..4' 

这是脚本的输出:

 #define FEI_0A_3(WHAT, _0, _1) FEI_0A_2(WHAT, _0) WHAT(1, _1) #define FEI_0A_4(WHAT, _0, _1, _2) FEI_0A_3(WHAT, _0, _1) WHAT(2, _2) #define FEI_0A_5(WHAT, _0, _1, _2, _3) FEI_0A_4(WHAT, _0, _1, _2) WHAT(3, _3) #define FEI_1A_4(WHAT, A1, _0, _1) FEI_1A_3(WHAT, A1, _0) WHAT(1, A1, _1) #define FEI_1A_5(WHAT, A1, _0, _1, _2) FEI_1A_4(WHAT, A1, _0, _1) WHAT(2, A1, _2) #define FEI_1A_6(WHAT, A1, _0, _1, _2, _3) FEI_1A_5(WHAT, A1, _0, _1, _2) WHAT(3, A1, _3) #define FEI_2A_5(WHAT, A1, A2, _0, _1) FEI_2A_4(WHAT, A1, A2, _0) WHAT(1, A1, A2, _1) #define FEI_2A_6(WHAT, A1, A2, _0, _1, _2) FEI_2A_5(WHAT, A1, A2, _0, _1) WHAT(2, A1, A2, _2) #define FEI_2A_7(WHAT, A1, A2, _0, _1, _2, _3) FEI_2A_6(WHAT, A1, A2, _0, _1, _2) WHAT(3, A1, A2, _3) #define FEI_3A_6(WHAT, A1, A2, A3, _0, _1) FEI_3A_5(WHAT, A1, A2, A3, _0) WHAT(1, A1, A2, A3, _1) #define FEI_3A_7(WHAT, A1, A2, A3, _0, _1, _2) FEI_3A_6(WHAT, A1, A2, A3, _0, _1) WHAT(2, A1, A2, A3, _2) #define FEI_3A_8(WHAT, A1, A2, A3, _0, _1, _2, _3) FEI_3A_7(WHAT, A1, A2, A3, _0, _1, _2) WHAT(3, A1, A2, A3, _3) #define FEI_4A_7(WHAT, A1, A2, A3, A4, _0, _1) FEI_4A_6(WHAT, A1, A2, A3, A4, _0) WHAT(1, A1, A2, A3, A4, _1) #define FEI_4A_8(WHAT, A1, A2, A3, A4, _0, _1, _2) FEI_4A_7(WHAT, A1, A2, A3, A4, _0, _1) WHAT(2, A1, A2, A3, A4, _2) #define FEI_4A_9(WHAT, A1, A2, A3, A4, _0, _1, _2, _3) FEI_4A_8(WHAT, A1, A2, A3, A4, _0, _1, _2) WHAT(3, A1, A2, A3, A4, _3) 

这些是用来生成FOR_EACH (又称为FE )macros的macros组重载(的定期结构化的部分),它可以用一个任意数量的常量参数( A1A2 …)列表中的任意数量的参数,以及正确顺序的索引(一个天真的实现,而不使用类似SELECT的重载会产生反转的索引)。

例如,剩下的部分(第二个块的非常规“基本情况”部分)如下所示:

 #define FE_INDEXED_1ARG(...) VA_SELECT(FEI_1A, __VA_ARGS__) #define FEI_1A_3(WHAT, A1, _0) WHAT(0, A1, _0) 

这可能会带来问题(我build立它,因为我看到了它的用途…),这也没有直接回答OP的问题(事实上,这是相反的 – 一个foreach构造对所有可变参数做同样的事情……),但我只是觉得这个技术是非常有趣的(在某些方面也是非常可怕的),并且使用预处理器可以产生一定的performance力,并且可以产生这种方式非常有效的机器码。 我认为这也是为什么我个人认为C预处理器还有改进空间的一个讽刺。

我的意思是说,C预处理器是一个绝对的憎恶,我们应该废弃它,并从头开始:)

以下是Potatoswatter的答案的改进, 可以区分零和一个论点。

简而言之,当__VA_ARGS__为空时, VA_SIZEmacros内的EXPAND __VA_ARGS__ ()变为EXPAND ()并用6个逗号代替。 所以, VA_SIZE...变成了COMPOSE( GET_COUNT, (,,,,,, , 0, 6, 5, 4, 3, 2, 1) )变成了GET_COUNT (,,,,,, , 0, 6, 5, 4, 3, 2, 1)并返回0。

另一方面,当__VA_ARGS__是例如int, 5EXPAND __VA_ARGS__ ()变为EXPAND int, 5 () 。 所以, VA_SIZE...变成了COMPOSE( GET_COUNT, (EXPAND int, 5 (), 0, 6, 5, 4, 3, 2, 1) ) GET_COUNT (EXPAND int, 5 (), 0, 6, 5, 4, 3, 2, 1) COMPOSE( GET_COUNT, (EXPAND int, 5 (), 0, 6, 5, 4, 3, 2, 1) ) ,它变成了GET_COUNT (EXPAND int, 5 (), 0, 6, 5, 4, 3, 2, 1) 5,4,3,2,1 GET_COUNT (EXPAND int, 5 (), 0, 6, 5, 4, 3, 2, 1)并返回2,如Potatoswatter的答案中所述。

我从Jason Dang的回答中得到了EXPAND想法。

图书馆编号:

 #define CAT( A, B ) A ## B #define SELECT( NAME, NUM ) CAT( NAME ## _, NUM ) #define COMPOSE( NAME, ARGS ) NAME ARGS #define GET_COUNT( _0, _1, _2, _3, _4, _5, _6 /* ad nauseam */, COUNT, ... ) COUNT #define EXPAND() ,,,,,, // 6 commas (or 7 empty tokens) #define VA_SIZE( ... ) COMPOSE( GET_COUNT, (EXPAND __VA_ARGS__ (), 0, 6, 5, 4, 3, 2, 1) ) #define VA_SELECT( NAME, ... ) SELECT( NAME, VA_SIZE(__VA_ARGS__) )(__VA_ARGS__) 

用法:

 #define MY_OVERLOADED( ... ) VA_SELECT( MY_OVERLOADED, __VA_ARGS__ ) #define MY_OVERLOADED_0( ) meh() #define MY_OVERLOADED_1( X ) foo< X > #define MY_OVERLOADED_2( X, Y ) bar< X >( Y ) #define MY_OVERLOADED_3( X, Y, Z ) bang_ ## X< Y >.Z() MY_OVERLOADED() // meh() MY_OVERLOADED(bool) // foo< bool > MY_OVERLOADED(int, 5) // bar< int >( 5 ) MY_OVERLOADED(me, double, now) // bang_me< double >.now() 

虽然已经回答了,但我已经准备了一个非常简短的版本。 希望它可以帮助。

履行

 // Variable Argument Macro (VA_MACRO) upto 6 arguments #define NUM_ARGS_(_1, _2, _3, _4, _5, _6, TOTAL, ...) TOTAL #define NUM_ARGS(...) NUM_ARGS_(__VA_ARGS__, 6, 5, 4, 3, 2, 1) #define CONCATE_(X, Y) X##Y // Fixed the double '_' from previous code #define CONCATE(MACRO, NUMBER) CONCATE_(MACRO, NUMBER) #define VA_MACRO(MACRO, ...) CONCATE(MACRO, NUM_ARGS(__VA_ARGS__))(__VA_ARGS__) 

定制

 // This is how user may define own set of variadic macros #define MY_MACRO(...) VA_MACRO(MY_MACRO, __VA_ARGS__) #define MY_MACRO1(_1) "One" #define MY_MACRO2(_1, _2) "Two" #define MY_MACRO3(_1, _2, _3) "Three" 

用法

 // While using those, user needs to use only the main macro int main () { auto one = MY_MACRO(1); auto two = MY_MACRO(1, 2); auto three = MY_MACRO(1, 2, 3); }