Pot*_*ter 41 c c++ overloading c-preprocessor variadic-macros
如何做这项工作?如何实现C99/C++ 11可变参数宏以根据给出多少参数来扩展到不同的东西?
Pot*_*ter 58
(编辑:查看现成解决方案的结尾.)
要获得重载的宏,首先我们需要一个宏,它可以在多个实现之间进行选择.这部分不使用可变参数宏.然后,通常对其参数进行计数的可变参数宏会生成一个选择器.将参数计数插入调度程序会产生重载宏.
警告:该系统无法分辨零个一个参数之间的差别,因为是和一个空参数没有参数之间没有区别.它们看起来都像MACRO().
要在实现之间进行选择,请将宏连接运算符与一系列类似函数的宏一起使用.
#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()
Run Code Online (Sandbox Code Playgroud)
因为##运算符会抑制其参数的宏扩展,所以最好将其包装在另一个宏中.
#define CAT( A, B ) A ## B
#define SELECT( NAME, NUM ) CAT( NAME ## _, NUM )
Run Code Online (Sandbox Code Playgroud)
要计算参数,请使用__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 )
Run Code Online (Sandbox Code Playgroud)
#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__)
Run Code Online (Sandbox Code Playgroud)
#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()
Run Code Online (Sandbox Code Playgroud)
我会将其作为对 Potatoswatter 帖子的评论发布,但它太长并且需要代码列表。
下面是一些 Perl 代码,用于生成一组宏,这些宏是重载宏。
$ 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'
Run Code Online (Sandbox Code Playgroud)
这是脚本的输出:
#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)
Run Code Online (Sandbox Code Playgroud)
这些是宏重载组(的规则结构部分),用于生成FOR_EACH(又名FE)宏,除了任意数量的参数之外,还可以WHAT选择性地使用任意数量的常量参数(...)来调度宏。一个列表,以及正确排序的索引(不使用重载之类的简单实现会产生反向索引)。A1A2SELECT
例如,剩余部分(第二个块的非常规“基本情况”部分)如下所示:
#define FE_INDEXED_1ARG(...) VA_SELECT(FEI_1A, __VA_ARGS__)
#define FEI_1A_3(WHAT, A1, _0) WHAT(0, A1, _0)
Run Code Online (Sandbox Code Playgroud)
它的实用性也许会受到质疑(我构建它是因为我看到了它的用途......),并且这也没有直接回答OP的问题(事实上,它做了相反的事情 - foreach 构造对所有可变参数做同样的事情......),但我只是认为该技术非常有趣(并且在某些方面非常可怕)并且允许使用预处理器具有相当大的表达能力,并且可以生成以这种方式非常有效的机器代码。我认为它也是一个令人心酸的例子,说明了为什么我个人认为 C 预处理器仍然有改进的空间。
我的意思是,C 预处理器绝对是令人厌恶的,我们可能应该废弃它并从头开始:)
以下是改进 Potatoswatter 的回答,它可以区分零和一个参数。
简而言之,什么时候__VA_ARGS__是空的,EXPAND __VA_ARGS__ ()里面VA_SIZE宏变成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, 5,EXPAND __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)并返回 2,如 Potatoswatter 的回答中所述。
我拿到 EXPAND从Jason Dang 的回答中想法。
#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__)
Run Code Online (Sandbox Code Playgroud)
#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()
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
10947 次 |
| 最近记录: |