宏内常量自增

问题描述 投票:0回答:1

我尝试定义宏,用于定义一堆常量和一个映射。我想用法会是这样的:

options_filter_consts!(
  MEAL_TYPE => "meal_type" // filter_id
  options { // filter options (const_name => const_value)
     MEAL_NO   => "meal_no"
     BREAKFAST => "breakfast"
     FULLBOARD => "fullboard"
     HALFBOARD => "halfboard"
  }
);

然后它会产生以下代码:

// Filter ID
const MEAL_TYPE:&str = "meal_type";

// Filter Options
const NO_MEAL  :&str = "no_meal";
const BREAKFAST:&str = "breakfast";
const FULLBOARD:&str = "fullboard";
const HALFBOARD:&str = "halfboard";

// Option indexes
const NO_MEAL_IDX  :usize = 0;
const BREAKFAST_IDX:usize = 1;
const FULLBOARD_IDX:usize = 2;
const HALFBOARD_IDX:usize = 3;

// Mapping indexes -> options
// Naming: <filter_id> + _MAPPING
const MEAL_TYPE_MAPPING:[&str; 4] = [
  /* NO_MEAL_IDX:   */ NO_MEAL,
  /* BREAKFAST_IDX: */ BREAKFAST,
  /* FULLBOARD_IDX: */ FULLBOARD,
  /* HALFBOARD_IDX: */ HALFBOARD,
];

这些常量将在主函数中使用,例如:

fn main() {
  println!("filter_id: {}", MEAL_TYPE);
  println!("mapping_len: {}", MEAL_TYPE_MAPPING.len());
  println!("mapping_item_value: {}", MEAL_TYPE_MAPPING[MEAL_NO_IDX]);
  println!("item_value: {}", BREAKFAST);
}

我开始写宏:

macro_rules! options_filter_consts {
  ($filt_const:ident => $filt_value:expr, options { $($name:ident => $value:expr)+ }) => {
    const $filt_const: &str = $filt_value;

    $(
        const $name : &str = $value;
    )+

    const IOTA:usize = 0; // how to increment inside marco ?

    $(
      paste! {
        const [<$name _IDX>]:usize = IOTA;
      }
    )+
  };
}

但是我遇到了一些错误,例如我无法增加 IOTA 常量值并陷入困境。

rust
1个回答
0
投票

目前,Rust 宏不支持宏内部的任何代码执行。 宏语言基于模式匹配和变量替换,并且仅评估宏。

在这种情况下,可以用宏递归来覆盖某些情况。例如计数

对于您的情况,您也可以使用类似的方法。

/// A macro that generates a sequence of constants with incrementing index values.
macro_rules! increment_consts {
    ($name:ident, $value:expr) => {
        paste! {
            const [<$name _IDX>] : usize = $value;
        }
    };
    ($name:ident, $value: expr, $($rest:ident, $new_value:expr),*) => {
        paste! {
            const [<$name _IDX>] : usize = $value;
        }
        increment_consts!($($rest, $value + 1),*);
    };
}

/// A macro that generates a sequence of constants with incrementing index values.
/// The `create_consts!` macro takes a list of identifiers as input and generates
/// a sequence of constants with incrementing index values for each identifier.
macro_rules! create_consts {
    ($($rest:ident),* $(,)?) => {
        increment_consts!($($rest, 0),*);
    };
}

然后在

main.rs
中让我们使用一个新的宏。

fn main() {
    create_consts!(A, B, C, D);
}

让我们用以下方式扩展宏:

cargo +nightly rustc --profile=check -- -Zunpretty=expanded

正在工作;我们正是需要的。

...
fn main() {
    const A_IDX: usize = 0;
    const B_IDX: usize = 0 + 1;
    const C_IDX: usize = 0 + 1 + 1;
    const D_IDX: usize = 0 + 1 + 1 + 1;
}

让我们将其添加到您的解决方案中。

use paste::paste;

/// A macro that generates a sequence of constants with incrementing index values.
macro_rules! increment_consts {
    ($name:ident, $value:expr) => {
        paste! {
            const [<$name _IDX>] : usize = $value;
        }
        const _ARR_LEN: usize = $value;
    };
    ($name:ident, $value: expr, $($rest:ident, $new_value:expr),*) => {
        paste! {
            const [<$name _IDX>] : usize = $value;
        }
        increment_consts!($($rest, $value + 1),*);
    };
}

/// A macro that generates a sequence of constants with incrementing index values.
/// The `create_consts!` macro takes a list of identifiers as input and generates
/// a sequence of constants with incrementing index values for each identifier.
macro_rules! create_consts {
    ($($rest:ident),* $(,)?) => {
        increment_consts!($($rest, 0),*);
    };
}

macro_rules! options_filter_consts {
    ($filt_const:ident => $filt_value:expr, options { $($name:ident => $value:expr)+ }) => {
        const $filt_const: &str = $filt_value;

        $(
            const $name : &str = $value;
        )+

        create_consts!($($name),+);

        paste! {
            const [<$filt_const _MAPPING>] : [&str; _ARR_LEN + 1] = [$($name,)+];
        }
    };
}

fn main() {
    options_filter_consts!(
        MEAL_TYPE => "meal_type", // filter_id
        options { // filter options (const_name => const_value)
           MEAL_NO   => "meal_no"
           BREAKFAST => "breakfast"
           FULLBOARD => "fullboard"
           HALFBOARD => "halfboard"
        }
    );

    println!("{}", HALFBOARD_IDX);
    println!("{}", MEAL_NO_IDX);
    println!("{}", FULLBOARD_IDX);
    println!("{:?}", MEAL_TYPE_MAPPING);
}

再次展开宏,看看这次我们有什么:

...
fn main() {
    const MEAL_TYPE: &str = "meal_type";
    const MEAL_NO: &str = "meal_no";
    const BREAKFAST: &str = "breakfast";
    const FULLBOARD: &str = "fullboard";
    const HALFBOARD: &str = "halfboard";
    const MEAL_NO_IDX: usize = 0;
    const BREAKFAST_IDX: usize = 0 + 1;
    const FULLBOARD_IDX: usize = 0 + 1 + 1;
    const HALFBOARD_IDX: usize = 0 + 1 + 1 + 1;
    const _ARR_LEN: usize = 0 + 1 + 1 + 1;
    const MEAL_TYPE_MAPPING: [&str; _ARR_LEN + 1] =
        [MEAL_NO, BREAKFAST, FULLBOARD, HALFBOARD];

    { ::std::io::_print(format_args!("{0}\n", HALFBOARD_IDX)); };
    { ::std::io::_print(format_args!("{0}\n", MEAL_NO_IDX)); };
    { ::std::io::_print(format_args!("{0}\n", FULLBOARD_IDX)); };
    { ::std::io::_print(format_args!("{0:?}\n", MEAL_TYPE_MAPPING)); };
}
© www.soinside.com 2019 - 2024. All rights reserved.