【问题标题】:Counting length of repetition in macro计算宏中的重复长度
【发布时间】:2015-12-16 05:32:05
【问题描述】:

我正在尝试实现一个宏以允许创建 MATLAB 式矩阵。我有一个基本的工作宏,但我还有很长的路要走。

我希望能够强制执行正确的结构(每行中的元素数量相同),但我不确定如何在宏中执行此操作。我想我想强制每个内部重复具有相同的长度 - 这是我可以做的吗?

到目前为止,这是我的代码:

pub struct Matrix<T> {
    pub cols: usize,
    pub rows: usize,
    pub data: Vec<T>
}

macro_rules! mat {
    ( $($( $x:expr ),*);* ) => {
        {
            let mut vec = Vec::new();
            let mut rows = 0;

            $(
                $(
                    vec.push($x);
                )*
                rows += 1;
            )*
            Matrix { cols : vec.len()/rows, rows: rows, data: vec}
        }
    };
}

It works 但正如您所见,它不是很安全。它对结构没有限制。

我想用这个宏做更多事情,但我认为这是一个好的开始!

更新:

这里是some playground code 我制定的一个糟糕的实现。如果有人有更好的建议,请告诉我!否则我会自己关闭它。

【问题讨论】:

标签: macros rust


【解决方案1】:
macro_rules! count {
    () => (0usize);
    ( $x:tt $($xs:tt)* ) => (1usize + count!($($xs)*));
}

macro_rules! mat {
    ( $( $x:expr ),* ) => { {
        let vec = vec![$($x),*];
        Matrix { cols : vec.len(), rows: 1, data: vec }
    } };
    ( $( $x0:expr ),* ; $($( $x:expr ),*);* ) => { {
        let mut _assert_width0 = [(); count!($($x0)*)];
        let mut vec = Vec::new();
        let rows = 1usize;
        let cols = count!($($x0)*);

        $( vec.push($x0); )*

        $(
            let rows = rows + 1usize;
            let _assert_width = [(); count!($($x)*)];
            _assert_width0 = _assert_width;
            $( vec.push($x); )*
        )*

        Matrix { cols : cols, rows: rows, data: vec }
    } }
}

playground

count! 宏扩展为一个常量表达式,表示它作为输入获得的参数数量。它只是 mat! 宏的助手。如果你需要对很多项进行计数,而编译器无法处理,请参阅the Counting chapter in The Little Book of Rust Macros,其中有更复杂的计数宏。

我的宏版本使用虚拟变量和赋值来验证所有行的宽度是否相同。首先,我更改了宏的模式以将第一行与后续行分开处理。第一个变量_assert_width0用一个单元数组初始化((),这使得数组不占用内存),数组的大小是第一行中的项目数。然后,_assert_width 也被初始化为一个单元数组,数组的大小是每个后续行中的项目数。然后,_assert_width 被分配给_assert_width0。这里的神奇之处在于,如果一行的宽度与第一行的宽度不匹配,此行将引发编译器错误,因为数组的类型不匹配(例如,[(); 3] 和 @987654332 @)。但是,如果您不知道宏中发生了什么,则该错误不是很清楚:

<anon>:38:24: 38:37 error: mismatched types:
 expected `[(); 3]`,
    found `[(); 4]`
(expected an array with a fixed size of 3 elements,
    found one with 4 elements) [E0308]
<anon>:38           _assert_width0 = _assert_width;
                                     ^~~~~~~~~~~~~
<anon>:47:13: 47:44 note: in this expansion of mat! (defined in <anon>)
<anon>:38:24: 38:37 help: see the detailed explanation for E0308

【讨论】:

  • 感谢您的回复!这是一个非常聪明的答案,但它确实会产生一个问题 - 用户无法创建单行矩阵。那是可以修复的吗?我们可以将重复的行设为可选吗?
  • 您应该将count! 中的01 替换为0usize1usize,否则您将快速遇到指数编译时间问题整数推断。此外,count! 的这个定义只适用于大约 64 个元素;我的答案中的链接有更强大的定义。话虽如此,使用这样的数组长度很聪明;我以前没见过那个。
  • @user124784:这只是向宏添加另一个模式的问题,我已经在我的答案中添加了它(它更简单,因为我们不需要验证行的宽度)。
  • @FrancisGagné 太棒了!我认为我不需要担心用户超过 64 个元素计数限制。
【解决方案2】:

首先,快速解决您问题的标题:请参阅Counting chapter in The Little Book of Rust Macros。总结一下:没有直接的方法,你需要编写一个宏,扩展成你可以在常规代码中计算的东西。

现在,解决您的实际问题:哎呀。

这不是你想要的计数,如果子序列有不同的长度,它会在编译时失败。

首先,没有clean 方法可以从宏触发编译失败。您可以触发一些 other 预先存在的错误,但您无法控制实际的错误消息。

其次,没有简单的方法在宏中进行“变量”比较。您有时可以与固定的标记序列进行比较,但这里不是这样做的。

所以它是双重不可行的。

最简单的做法是在运行时检查构造期间的长度,如果不匹配则返回错误或恐慌。


真的不可能吗?我不相信。如果您愿意接受难以理解的错误消息和大量复杂性跳跃,您可以检查两个标记序列之间的长度相等性,如下所示:

macro_rules! tts_equal_len {
    (($_lhs:tt $($lhs_tail:tt)*), ($_rhs:tt $($rhs_tail:tt)*)) => {
        tts_equal_len!(($($lhs_tail)*), ($($rhs_tail)*))
    };
    (($($_lhs_tail:tt)+), ()) => { do_something_bad!() };
    ((), ($($_rhs_tail:tt)+)) => { do_something_bad!() };
    ((), ()) => { do_something_good!() };
}

macro_rules! do_something_bad { () => { { println!("kaboom!") } } }
macro_rules! do_something_good { () => { { println!("no kaboom!") } } }

fn main() {
    tts_equal_len!((,,,), (,,,));
    tts_equal_len!((,,,), (,,));
    tts_equal_len!((,), (,,));
}

同样,真正的问题是在编译时找到一些失败的方法,以便用户了解为什么编译失败。

【讨论】:

  • 我担心可能是这种情况。感谢您的实施 - 我不知道它是如何工作的,但我会弄清楚的!也许在这种情况下编写编译器插件更容易?我还没有看过它们,但从我读过的内容来看,这是一个不错的用例?
  • @user124784:你可以这样做,但请记住,编译器插件不稳定,经常中断,并且不能用于除夜间编译器构建之外的任何东西。
猜你喜欢
  • 2013-06-15
  • 2015-05-09
  • 2017-10-09
  • 2014-06-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多