首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >宏有没有可能接受一个常量表达式并“内联”它来生成一个有效的LiteralPattern?

宏有没有可能接受一个常量表达式并“内联”它来生成一个有效的LiteralPattern?
EN

Stack Overflow用户
提问于 2021-07-30 11:22:14
回答 1查看 155关注 0票数 4

我想在复数常量的一个字段上进行模式匹配。您可以使用PathPattern匹配一个常量,但不允许选择另一个常量的常量字段。

是否可以编写一个宏来获取常量表达式的结果,并在编译时对其进行“内联”,这样生成的代码就是一个有效的LiteralPattern

请考虑下面的工作代码:

代码语言:javascript
复制
pub struct Instruction {
    code: u8,
    width: usize,
}

pub const CMP_REG_U8: Instruction = Instruction { code: 3, width: 3 };
pub const CMP_REG_U32: Instruction = Instruction { code: 4, width: 6 };
pub const INVALID: Instruction = Instruction { code: 0, width: 0 };

fn main() {
    let opcode = 3u8;
    
    let inst = match opcode {
        3 => CMP_REG_U8,
        4 => CMP_REG_U32,
        _ => INVALID,
    };

    println!("inst.code: {} inst.width: {}", inst.code, inst.width);
}

我希望能够写出这样的内容:

代码语言:javascript
复制
let inst = match opcode {
    CMP_REG_U8.code => CMP_REG_U8,
    CMP_REG_U32.code => CMP_REG_U32,
    _ => INVALID,
};

这样,我就不必在match语句中放入幻数,也不必第二次重复它们。

我确实意识到我可以使用守卫:

代码语言:javascript
复制
let inst = match opcode {
    x if x == CMP_REG_U8.code => CMP_REG_U8,
    x if x == CMP_REG_U32.code => CMP_REG_U32,
    _ => INVALID,
};

...but这看起来有点冗长,我关心的是如何说服编译器给我一个跳转表实现。(想象一下,与仅仅返回指令不同,我们希望执行一段代码,并且能够引用相关数据,如inst.width,与将值硬编码为文字相比,没有性能损失。)

所以底线是,有没有可能用一个宏来解决这个问题,这个宏将常量“字面化”?

代码语言:javascript
复制
let inst = match opcode {
    as_literal!(CMP_REG_U8.code) => exec_op(CMP_REG_U8),
    as_literal!(CMP_REG_U32.code) => exec_op(CMP_REG_U32),
    _ => INVALID,
};

这样,匹配臂都将是硬编码的整数,这将使编译器非常容易内联exec_op所做的任何事情,将width等识别为该上下文中的常量,并执行计算goto或其他操作。

我听说Rust中的宏只允许你做你可以自己输入的事情。但是,我当然可以输入3而不是CMP_REG_U8.code --问题是Rust是否能为我做这件事。它需要在编译时解决这个问题,以使生成的模式在匹配中有效。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2021-07-30 13:12:45

您可以使用一个宏来生成另一个宏:

代码语言:javascript
复制
macro_rules! define_consts {
    ($(
        $vis: vis const $name: ident : Instruction = Instruction { code: $code: literal, width: $width: literal };
    )*) => {
        $(
            $vis const $name: Instruction = Instruction { code: $code, width: $width };
        )*
        
        macro_rules! as_literal {
            $(($name . code) => { $code });*
        }
    }
}

使用您的常量定义调用此函数:

代码语言:javascript
复制
define_consts! {
    pub const CMP_REG_U8: Instruction = Instruction { code: 3, width: 3 };
    pub const CMP_REG_U32: Instruction = Instruction { code: 4, width: 6 };
    pub const INVALID: Instruction = Instruction { code: 0, width: 0 };
}

将按照编写的方式生成常量,并生成一个宏as_literal!,它可以执行您想要的操作:

代码语言:javascript
复制
fn exec_op(inst: Instruction) -> Instruction {
    inst
}

fn main() {
    let opcode = 3u8;
    
    let inst = match opcode {
        as_literal!(CMP_REG_U8.code) => exec_op(CMP_REG_U8),
        as_literal!(CMP_REG_U32.code) => exec_op(CMP_REG_U32),
        _ => INVALID,
    };

    println!("inst.code: {} inst.width: {}", inst.code, inst.width); // 3
}

不过,我不确定这有多有价值!由于这些模式都是文字,所以使用if...else if...而不是match最终很可能会产生相同的程序集。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/68590130

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档