【问题标题】:Is it possible to create a macro which counts the number of expanded items?是否可以创建一个计算扩展项目数量的宏?
【发布时间】:2015-07-21 01:54:48
【问题描述】:

是否可以创建一个计算展开项数的宏?

macro_rules! count {
    ($($name:ident),*) => {
        pub enum Count {
           $(
               $name = 1 << $i // $i is the current expansion index
            ),*
        }
    }
}

count!(A, B, C);

【问题讨论】:

    标签: macros rust


    【解决方案1】:

    在这种情况下,不。宏可以创建一个表达式来计算传递给它的标识符的数量,但它只会在运行时计算。我在短短几分钟内创建了this example,但我意识到它不适用于你正在做的事情。

    Compiler plugins,然而,特别适合这种工作。虽然它们实施起来并不简单,但我认为为此目的创建一个并不太困难。也许看看,尝试一下,如果卡住了再回来?

    【讨论】:

    • 您也可以在枚举上下文中计数,但不幸的是,一个枚举变体作为 usize 不是另一个有效的常量表达式。 enum Count { $( $name ),* }
    【解决方案2】:

    是的,如果你把它打包成 idents 数组

    macro_rules! count {
        ($($name:ident),*) => {
            {
                let counter = [$(stringify!($name),)*];
                counter.len()
            }
        }
    }
    

    计数、名称、名称倒序可用。之后,您可以使用它来构建一些东西。对于enum 建筑物,您必须使用this 之类的东西加入它。

    【讨论】:

      【解决方案3】:

      这是一个计算匹配项数的宏:

      macro_rules! count_items {
          ($name:ident) => { 1 };
          ($first:ident, $($rest:ident),*) => {
              1 + count_items!($($rest),*)
          }
      }
      
      fn main() {
          const X: usize = count_items!(a);
          const Y: usize = count_items!(a, b);
          const Z: usize = count_items!(a, b, c);
          assert_eq!(1, X);
          assert_eq!(2, Y);
          assert_eq!(3, Z);
      }
      

      请注意,计数是在编译时计算的。


      对于您的示例,您可以使用accumulation

      macro_rules! count {
          ($first:ident, $($rest:ident),*) => (
              count!($($rest),+ ; 0; $first = 0)
          );
          ($cur:ident, $($rest:ident),* ; $last_index: expr ; $($var:ident = $index:expr)+) => (
              count!($($rest),* ; $last_index + 1; $($var = $index)* $cur = $last_index + 1)
          );
          ($cur:ident; $last_index:expr ; $($var:ident = $index:expr)+) => (
              #[repr(C)]
              enum Count {
                  $($var = 1 << $index),*,
                  $cur = 1 << ($last_index + 1),
              }
          );
      }
      
      pub fn main() {
          count!(A, B, C, D);
          assert_eq!(1, Count::A as usize);
          assert_eq!(2, Count::B as usize);
          assert_eq!(4, Count::C as usize);
          assert_eq!(8, Count::D as usize);
      }
      

      【讨论】:

        【解决方案4】:

        由于这个问题很笼统,因此发布一个计算参数由空格(不是逗号)分隔的位置的示例。

        虽然回想起来似乎很明显,但我花了一段时间才弄清楚:

        /// Separated by white-space.
        #[macro_export]
        macro_rules! count_args_space {
            ($name:ident) => { 1 };
            ($first:ident $($rest:ident) *) => {
                1 + count_args_space!($($rest) *)
            }
        }
        
        /// Separated by commas.
        #[macro_export]
        macro_rules! count_args_comma {
            ($name:ident) => { 1 };
            ($first:ident, $($rest:ident),*) => {
                1 + count_args_comma!($($rest),*)
            }
        }
        

        第二个例子来自@malbarbo,只是发布到这样你就可以看到所需的 2 倍更改。

        【讨论】:

          猜你喜欢
          • 2011-12-18
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-03-25
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多