Rust宏:从可变数量的输入生成if-else-if链

yzxexxkh  于 2023-03-08  发布在  其他
关注(0)|答案(2)|浏览(157)

我需要一个宏,以防止需要写下面的代码超过2的十二次幂:

pub(crate) fn calc(samples: &[f32]) -> Vec<Complex32> {
        let buffer = samples;
        let mut res = {
            if buffer.len() == 2 {
                let mut buffer: [_; 2] = buffer.try_into().unwrap();
                real::rfft_2(&mut buffer).to_vec()
            } else if buffer.len() == 4 {
                let mut buffer: [_; 4] = buffer.try_into().unwrap();
                real::rfft_4(&mut buffer).to_vec()
            /// ..
            } else if buffer.len() == 16384 {
                let mut buffer: [_; 16384] = buffer.try_into().unwrap();
                real::rfft_16384(&mut buffer).to_vec()
            } else {
                panic!(
                    "`microfft::real` only supports powers of 2 between 2 and 16384 for the amount of samples!"
                );
            }

然而,我不知道如何在Rust宏定义中表达这一点。宏将被如下调用:
generate_ifs!(buffer, 2, 4, 8, 16, 32, 64, 128, 256, ..., 16384)
可能,我需要这样的:

macro_rules! generate_ifs {
    ($buffer:ident, $e:literal) => {
        {
            
        }
    };

    ($buffer:ident, $e:literal, $($es:literal),+) => {{
        generate_ifs! { $buffer:ident, $e }
        // do something special to generate "else if"
        generate_ifs! { $buffer:ident, $($es),+ }
    }};
}

但我看不出最后会有什么结果。

svmlkihl

svmlkihl1#

如果你已经枚举了2的所有幂次,你也可以写出函数的标识符(rfft_2)来得到一个简短的解决方案:

use microfft::{Complex32, real};

fn calc(samples: &[f32]) -> Vec<Complex32> {
    let mut buffer = samples.to_vec();

    macro_rules! gen_ifs {
        ($($size:literal => $func:ident),*) => {
            if false {
                unreachable!();
            }
            $(
                else if buffer.len() == $size {
                    real::$func(<&mut [_; $size]>::try_from(&mut buffer[..]).unwrap()).to_vec()
                }
            )*
            else {
                panic!();
            }
        };
    }

    gen_ifs!(2 => rfft_2, 4 => rfft_4, 8 => rfft_8)
}

我使用了一个技巧,通过在开始时添加一个if false分支来解决ifelse if的问题。
如果您想在宏调用中省略函数名,这会变得更加困难(如果使用macro_rules!不是不可能的话),例如,请参见以下问题:Interpolate ident in string literal in macro_rules!

au9on6nz

au9on6nz2#

你可以在这里使用闭包,在你的宏调用中创建闭包,然后立即调用它,返回它的结果,这允许你在每个if-语句中依赖return,所以你不需要else块:

macro_rules! generate_ifs {
    ($buffer:ident, $($e:literal),+) => {
        {
           let anonymous_function = |buffer: &[f32]| -> Vec<num::complex::Complex32> {
                $(
                    if $buffer.len() == $e {
                        return buffer.iter().map(crate::as_complex).collect();
                    }
                )+

                panic!(
                    "`microfft::real` only supports powers of 2 between 2 and 16384 for the amount of samples!"
                );
            };

            anonymous_function(&$buffer)
        }
    };
}

您可以找到完整的示例here.

相关问题