【问题标题】:Writing const generic enum combinations using macro使用宏编写 const 通用枚举组合
【发布时间】:2021-04-10 20:10:53
【问题描述】:

考虑一个带有两个 const 泛型参数的玩具结构:

pub struct Foo<const N: usize, const M: usize>([usize; N], [usize; M]);

impl<const N: usize, const M: usize> Foo<N, M> {
    pub fn bar(&self) -> usize {
        N * M
    }
}

假设NM 在 1 和 5 之间的所有组合都是允许的,因此我们可以编写以下枚举:

pub enum FooEnum {
    Foo_1_1(Foo<1, 1>),
    Foo_1_2(Foo<1, 2>),
    Foo_2_1(Foo<2, 1>),
    Foo_2_2(Foo<2, 2>),
    // ... and so on.
}

impl FooEnum {
    pub fn bar(&self) -> usize {
        match self {
            Self::Foo_1_1(x) => x.bar(),
            Self::Foo_1_2(x) => x.bar(),
            Self::Foo_2_1(x) => x.bar(),
            Self::Foo_2_2(x) => x.bar(),
            // ... and so on.

        }
    }
}

我的问题是:我们可以编写一个声明性宏来生成它,而无需手动写出所有组合吗?也就是说,类似于impl_foo_enum!(1, 2, 3, 4, 5),而不是impl_foo_enum!(1;1, 1;2, 1;3, [...and so on])


我可以使用paste crate 编写后一个宏:

macro_rules! impl_foo_enum {
    ($($n:literal;$m:literal),+) => {
        paste::paste! {
            pub enum FooEnum2 {
                $(
                    [<Foo _ $n _ $m>](Foo<$n, $m>)
                ),+
            }

            impl FooEnum2 {
                pub fn bar(&self) -> usize {
                    match self {
                        $(Self::[<Foo _ $n _ $m>](x) => x.bar()),+
                    }
                }
            }
        }
    }
}

impl_foo_enum!(1;1, 1;2, 2;1, 2;2);

(Playground)

为了获得不那么乏味的宏,有几个相关的问题和有用的答案(12)我认为我可以适应,但在这两种情况下,函数调用都可以在宏内重复,这似乎简化了事情。例如,使用第一个链接示例中的方法,我开始:

macro_rules! for_all_pairs {
    ($mac:ident: $($x:literal)*) => {
        for_all_pairs!(@inner $mac: $($x)*; $($x)*);
    };
    (@inner $mac:ident: ; $($x:literal)*) => {};
    (@inner $mac:ident: $head:literal $($tail:literal)*; $($x:literal)*) => {
        $(
            $mac!($head $x);
        )*
        for_all_pairs!(@inner $mac: $($tail)*; $($x)*);
    };
}

macro_rules! impl_foo_enum {
    ($n:literal $m:literal) => {
        paste::paste! { [<Foo _ $n _ $m>](Foo<$n, $m>) }
    }
}

pub enum FooEnum3 {
    for_all_pairs!(impl_foo_enum: 1 2)
}

(Playground)

这不会编译,因为编译器不希望在枚举变量位置有一个宏(我相信)。

(需要说明的是,我不一定想将上述内容用于任何严重的事情,我只是在实验时遇到它并很好奇。)

【问题讨论】:

    标签: generics rust enums rust-macros


    【解决方案1】:

    Here你去:

    #![allow(non_camel_case_types)]
    pub struct Foo<const N: usize, const M: usize>([usize; N], [usize; M]);
    impl<const N: usize, const M: usize> Foo<N, M> {
        pub fn bar(&self) -> usize {
            N * M
        }
    }
    
    macro_rules! impl_foo_2{
        ($($n:literal)*) => {
            impl_foo_2!([] @orig($($n)*) ($($n)*) ($($n)*));
        };
        (
            [$(($n:literal $m:literal))*]
            @orig($($n_orig:literal)*)
            ($($n_unused:literal)*) ()
        ) => {
            paste::paste! {
                pub enum FooEnum2 {
                    $([<Foo _ $n _ $m>](Foo<$n, $m>)),+
                }
                impl FooEnum2 {
                    pub fn bar(&self) -> usize {
                        match self {
                            $(Self::[<Foo _ $n _ $m>](x) => x.bar()),+
                        }
                    }
                }
            }
        };
        (
            [$($t:tt)*]
            @orig($($n_orig:literal)*)
            () ($m0:literal $($m:literal)*)
        ) => {
            impl_foo_2!(
                [$($t)*]
                @orig($($n_orig)*)
                ($($n_orig)*) ($($m)*)
            );
        };
        (
            [$($t:tt)*]
            @orig($($n_orig:literal)*)
            ($n0:literal $($n:literal)*) ($m0:literal $($m:literal)*)
        ) => {
            impl_foo_2!(
                [$($t)* ($n0 $m0)]
                @orig($($n_orig)*)
                ($($n)*) ($m0 $($m)*)
            );
        }
    }
    
    impl_foo_2!(1 2 3 4 5);
    

    impl_foo_2 在内部生成两个相同的号码列表副本。然后它继续一次处理一个m,将它与每个n 结合起来(它通过反复切断第一个n 来实现)。如果n-list 用完,它会重置n-list,并砍掉第一个m。所有这些都完成,直到所有nm 都用完为止。

    中间结果被收集到宏的第一个参数中 - 最后 - 被传递给您的impl_foo_enum

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-09-17
      • 1970-01-01
      • 2013-01-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多