有没有办法用宏来计数? [英] Is there a way to count with macros?

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

问题描述

我想创建一个宏,以指定的次数打印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天全站免登陆