基于 macro_rules 生成元组索引!重复扩展
Generating tuple indices based on macro_rules! repetition expansion
给定一个带有任意数量参数的声明性 macro_rules!
宏,我如何根据重复扩展生成元组索引?
在下面的示例中,我希望生成第 6 行和第 7 行的 .0
。
- 我试过使用空的“替换”内部宏,例如
(@replace $x:tt) => { $x; }
,但这在元组构造中不起作用。
- 我意识到我可以将三个语句合并为一个,但在我的实际宏中我还需要中间变量。
- 我基本上是在寻找与 C++ 的
std::integer_sequence
等效的东西
macro_rules! the_action {
(@replace $x:tt) => {$x;};
($($queue:expr),+) => {
let iters = ($($queue.iter()),+);
let options = ($(iters.0.next())*);
let values = ($(options.0.unwrap_or_default())*);
}
}
fn main() {
let a = [1, 4];
let b = [3, 2];
let c = [5, -1];
the_action!(a);
the_action!(a, b);
the_action!(a, b, c);
}
奖金问题:鉴于 Rust 中缺少可变参数泛型,我希望这在 Rust 中很常见。为什么 macro_rules 中没有用于重复索引的内置语法?编译器肯定有可用的信息。这是否曾在 RFC 中讨论过?
编辑:显然 it has,但看起来它已被搁置。
据我所知,使用声明性宏无法生成它们。
这取决于您的用例,但就目前而言,您可以从以下开始,每次都解包元组:
macro_rules! the_action {
(@replace $x:tt) => {$x;};
($($queue:ident),+) => {
let ($(mut $queue,)+) = ($($queue.iter().copied(),)+);
let ($($queue,)+) = ($($queue.next(),)+);
let values = ($($queue.unwrap_or_default(),)*);
}
}
fn main() {
let a = [1, 4];
let b = [3, 2];
let c = [5, -1];
the_action!(a);
the_action!(a, b);
the_action!(a, b, c);
}
如果您需要访问中间步骤,您可以将它们打包到不同的结构中,如:
macro_rules! the_action {
(@replace $x:tt) => {$x;};
($($queue:ident),+) => {
{
struct Step1<$($queue,)+> {
$($queue: $queue,)+
}
let mut step1 = Step1{$($queue: $queue.iter().copied(),)+};
struct Step2<$($queue,)+> {
$($queue: $queue,)+
}
let step2 = Step2{$($queue: step1.$queue.next(),)+};
let values = ($(step2.$queue.unwrap_or_default(),)*);
}
}
}
fn main() {
let a = [1, 4];
let b = [3, 2];
let c = [5, -1];
the_action!(a);
the_action!(a, b);
the_action!(a, b, c);
}
一种替代方法是将元组解压缩到一个数组中,并使用索引——这甚至不需要在宏观上创建。
这是另一个例子,即一个宏 tpl_map
,它对每个元组元素应用一个操作,产生另一个元组。它期望:
- 一系列
expr
(称为 queue
以匹配您的示例)以确定元组组件的数量。
- 标识符
tpl
表示要映射其元素的元组。
- 描述元组操作的标识符
fn
(应该引用接受一个参数的宏)。
我尝试在那里接受一个闭包,但这导致我出现“需要类型注释”错误,所以我使用了一个宏。
由于很难(不可能?)在声明性宏中生成元组索引,目前它被限制为最多具有 5 个元素的元组,但这可以通过更改一行来扩展(参见 [(0) (1) (2) (3) (4)]
).
此外,我不得不求助于 apply
拐杖(它 - 天真地 - 应该是内联的)来编译它。
macro_rules! apply {
($f:ident, $e:expr) => {$f!($e)}
}
macro_rules! tpl_map {
(@, [], [$(($idx:tt))*], $tpl:ident, $fn:ident, ($($result:tt)*)) => {($($result)*)};
(@, [$queue0:expr, $($queue:expr,)*], [($idx0:tt) $(($idx:tt))*], $tpl:ident, $fn:ident, ($($result:tt)*)) => {
tpl_map!(@,
[$($queue,)*],
[$(($idx))*],
$tpl,
$fn,
($($result)* apply!($fn, ($tpl . $idx0)), )
)
};
([$($queue:expr,)*], $tpl:ident, $fn:ident) => {
tpl_map!(@,
[$($queue,)*],
[(0) (1) (2) (3) (4)],
$tpl,
$fn,
()
)
}
}
macro_rules! the_action { ($($queue:expr,)+) => {
let mut iters = ($($queue.iter(),)+);
macro_rules! iter_next{($elem:expr) => {
$elem .next()
}}
let options = tpl_map!([$($queue,)*], iters, iter_next);
macro_rules! unwrap_or_default{($elem:expr) => {
$elem .copied() .unwrap_or_default()
}}
let values = tpl_map!([$($queue,)*], options, unwrap_or_default);
}}
fn main() {
let a = [1usize, 4];
let b = [3u8, 2];
let c = [true, false];
the_action!(a, b, c,);
}
给定一个带有任意数量参数的声明性 macro_rules!
宏,我如何根据重复扩展生成元组索引?
在下面的示例中,我希望生成第 6 行和第 7 行的 .0
。
- 我试过使用空的“替换”内部宏,例如
(@replace $x:tt) => { $x; }
,但这在元组构造中不起作用。 - 我意识到我可以将三个语句合并为一个,但在我的实际宏中我还需要中间变量。
- 我基本上是在寻找与 C++ 的
std::integer_sequence
等效的东西
macro_rules! the_action {
(@replace $x:tt) => {$x;};
($($queue:expr),+) => {
let iters = ($($queue.iter()),+);
let options = ($(iters.0.next())*);
let values = ($(options.0.unwrap_or_default())*);
}
}
fn main() {
let a = [1, 4];
let b = [3, 2];
let c = [5, -1];
the_action!(a);
the_action!(a, b);
the_action!(a, b, c);
}
奖金问题:鉴于 Rust 中缺少可变参数泛型,我希望这在 Rust 中很常见。为什么 macro_rules 中没有用于重复索引的内置语法?编译器肯定有可用的信息。这是否曾在 RFC 中讨论过?
编辑:显然 it has,但看起来它已被搁置。
据我所知,使用声明性宏无法生成它们。
这取决于您的用例,但就目前而言,您可以从以下开始,每次都解包元组:
macro_rules! the_action {
(@replace $x:tt) => {$x;};
($($queue:ident),+) => {
let ($(mut $queue,)+) = ($($queue.iter().copied(),)+);
let ($($queue,)+) = ($($queue.next(),)+);
let values = ($($queue.unwrap_or_default(),)*);
}
}
fn main() {
let a = [1, 4];
let b = [3, 2];
let c = [5, -1];
the_action!(a);
the_action!(a, b);
the_action!(a, b, c);
}
如果您需要访问中间步骤,您可以将它们打包到不同的结构中,如:
macro_rules! the_action {
(@replace $x:tt) => {$x;};
($($queue:ident),+) => {
{
struct Step1<$($queue,)+> {
$($queue: $queue,)+
}
let mut step1 = Step1{$($queue: $queue.iter().copied(),)+};
struct Step2<$($queue,)+> {
$($queue: $queue,)+
}
let step2 = Step2{$($queue: step1.$queue.next(),)+};
let values = ($(step2.$queue.unwrap_or_default(),)*);
}
}
}
fn main() {
let a = [1, 4];
let b = [3, 2];
let c = [5, -1];
the_action!(a);
the_action!(a, b);
the_action!(a, b, c);
}
一种替代方法是将元组解压缩到一个数组中,并使用索引——这甚至不需要在宏观上创建。
这是另一个例子,即一个宏 tpl_map
,它对每个元组元素应用一个操作,产生另一个元组。它期望:
- 一系列
expr
(称为queue
以匹配您的示例)以确定元组组件的数量。 - 标识符
tpl
表示要映射其元素的元组。 - 描述元组操作的标识符
fn
(应该引用接受一个参数的宏)。 我尝试在那里接受一个闭包,但这导致我出现“需要类型注释”错误,所以我使用了一个宏。
由于很难(不可能?)在声明性宏中生成元组索引,目前它被限制为最多具有 5 个元素的元组,但这可以通过更改一行来扩展(参见 [(0) (1) (2) (3) (4)]
).
此外,我不得不求助于 apply
拐杖(它 - 天真地 - 应该是内联的)来编译它。
macro_rules! apply {
($f:ident, $e:expr) => {$f!($e)}
}
macro_rules! tpl_map {
(@, [], [$(($idx:tt))*], $tpl:ident, $fn:ident, ($($result:tt)*)) => {($($result)*)};
(@, [$queue0:expr, $($queue:expr,)*], [($idx0:tt) $(($idx:tt))*], $tpl:ident, $fn:ident, ($($result:tt)*)) => {
tpl_map!(@,
[$($queue,)*],
[$(($idx))*],
$tpl,
$fn,
($($result)* apply!($fn, ($tpl . $idx0)), )
)
};
([$($queue:expr,)*], $tpl:ident, $fn:ident) => {
tpl_map!(@,
[$($queue,)*],
[(0) (1) (2) (3) (4)],
$tpl,
$fn,
()
)
}
}
macro_rules! the_action { ($($queue:expr,)+) => {
let mut iters = ($($queue.iter(),)+);
macro_rules! iter_next{($elem:expr) => {
$elem .next()
}}
let options = tpl_map!([$($queue,)*], iters, iter_next);
macro_rules! unwrap_or_default{($elem:expr) => {
$elem .copied() .unwrap_or_default()
}}
let values = tpl_map!([$($queue,)*], options, unwrap_or_default);
}}
fn main() {
let a = [1usize, 4];
let b = [3u8, 2];
let c = [true, false];
the_action!(a, b, c,);
}