有没有一种方法可以对宏进行计数? [英] Is there a way to count with macros?

查看:138
本文介绍了有没有一种方法可以对宏进行计数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想创建一个宏,该宏打印"Hello"指定的次数.就像这样:

I want to create a macro that prints "Hello" a specified number of times. It's used like:

many_greetings!(3);  // expands to three `println!("Hello");` statements

创建该宏的简单方法是:

The naive way to create that macro is:

macro_rules! many_greetings {
    ($times:expr) => {{
        println!("Hello");
        many_greetings!($times - 1);
    }};
    (0) => ();
}

但是,这不起作用,因为编译器不对表达式求值. $times - 1不是计算出来的,而是作为新的表达式输入到宏中的.

However, this doesn't work because the compiler does not evaluate expressions; $times - 1 isn't calculated, but fed as a new expression into the macro.

推荐答案

虽然普通的宏系统无法使您重复多次宏扩展,但是在宏中使用for循环没有问题:

While the ordinary macro system does not enable you to repeat the macro expansion many times, there is no problem with using a for loop in the macro:

macro_rules! many_greetings {
    ($times:expr) => {{
        for _ in 0..$times {
            println!("Hello");
        }
    }};
}

如果您确实需要重复该宏,则必须查看程序宏/编译器插件(从1.4版本开始是不稳定的,并且编写起来有些困难).

If you really need to repeat the macro, you have to look into procedural macros/compiler plugins (which as of 1.4 are unstable, and a bit harder to write).

可能有更好的方法来实现此目的,但今天我已经花了足够的时间在这里,所以去吧. repeat!,实际上是多次复制代码块的宏:

There are probably better ways of implementing this, but I've spent long enough on this for today, so here goes. repeat!, a macro that actually duplicates a block of code a number of times:

#![feature(plugin)]
#![plugin(repeat)]

fn main() {
    let mut n = 0;
    repeat!{ 4 {
        println!("hello {}", n);
        n += 1;
    }};
}

lib.rs

#![feature(plugin_registrar, rustc_private)]

extern crate syntax;
extern crate rustc;

use syntax::codemap::Span;
use syntax::ast::TokenTree;
use syntax::ext::base::{ExtCtxt, MacResult, MacEager, DummyResult};
use rustc::plugin::Registry;
use syntax::util::small_vector::SmallVector;
use syntax::ast::Lit_;
use std::error::Error;

fn expand_repeat(cx: &mut ExtCtxt, sp: Span, tts: &[TokenTree]) -> Box<MacResult + 'static> {
    let mut parser = cx.new_parser_from_tts(tts);
    let times = match parser.parse_lit() {
        Ok(lit) => match lit.node {
            Lit_::LitInt(n, _) => n,
            _ => {
                cx.span_err(lit.span, "Expected literal integer");
                return DummyResult::any(sp);
            }
        },
        Err(e) => {
            cx.span_err(sp, e.description());
            return DummyResult::any(sp);
        }
    };
    let res = parser.parse_block();

    match res {
        Ok(block) => {
            let mut stmts = SmallVector::many(block.stmts.clone());
            for _ in 1..times {
                let rep_stmts = SmallVector::many(block.stmts.clone());
                stmts.push_all(rep_stmts);
            }
            MacEager::stmts(stmts)
        }
        Err(e) => {
            cx.span_err(sp, e.description());
            DummyResult::any(sp)
        }
    }
}

#[plugin_registrar]
pub fn plugin_registrar(reg: &mut Registry) {
    reg.register_macro("repeat", expand_repeat);
}

添加到Cargo.toml

[lib]
name = "repeat"
plugin = true

请注意,如果我们真的不想执行循环,而是在编译时进行扩展,则必须执行诸如要求输入实数的操作.毕竟,我们无法在编译时评估引用程序其他部分的变量和函数调用.

Note that if we really don't want to do looping, but expanding at compile-time, we have to do things like requiring literal numbers. After all, we are not able to evaluate variables and function calls that reference other parts of the program at compile time.

这篇关于有没有一种方法可以对宏进行计数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆