如何找到枚举变量的参数数量? [英] How to find the number of arguments of an enum variant?

查看:120
本文介绍了如何找到枚举变量的参数数量?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个枚举,表示8080处理器上的所有可能指令。一条指令的长度可以为1、2或3个字节,具体取决于它是否具有与之关联的信息以及信息量。例如:

I have an enum which represents every possible instruction on an 8080 processor. An instruction can be 1, 2 or 3 bytes long, depending on whether it has information associated with it and how much. For example:

#[allow(non_camel_case_types)]
enum Instruction {
    None,
    NOP,
    LXI_B_D16(u8, u8),
    STAX_B,
    INX_B,
    MVI_B_D8(u8),
    MVI_C_D8(u8),
    RRC,
    LXI_D_D16(u8, u8),
    MVI_D_D8(u8),
    RAL,
    DCR_E,
    MVI_E_D8(u8),
    LXI_H_D16(u8, u8),
    SHLD(u16),
    LHLD(u16),
    // ...
}

在为指令分配内存地址时,我遍历二进制文件逐条指令,使用每条指令的长度确保循环不会到达进行指令的一半,给我垃圾。我使用一个巨大的match表达式来执行此操作,该表达式返回一个包含正确指令及其长度的元组:

When it comes to assigning memory addresses to instructions, I iterate instruction-by-instruction through the binary file, using the length of each instruction to make sure my loop doesn't land halfway through an instruction and give me garbage. I do this with a huge match expression, which returns a tuple containing the correct instruction and its length:

match value {
    0x00 => (Instruction::NOP, 1),
    0x01 => (Instruction::LXI_B_D16(d1, d2), 3),
    0x02 => (Instruction::STAX_B, 1),
    0x05 => (Instruction::DCR_B, 1),
    0x06 => (Instruction::MVI_B_D8(d1), 2),
    0x07 => (Instruction::RLC, 1),
    0x0e => (Instruction::MVI_C_D8(d1), 2),
    0x0f => (Instruction::RRC, 1),
    0x11 => (Instruction::LXI_D_D16(d1, d2), 3),
    0x19 => (Instruction::DAD_D, 1),
    // ...
}

这很丑陋,但是我不想将此长度数字关联到类型中,因为它仅在解析文件时才重要。

This is ugly, but I don't want to associate this length number into the type because it's really only important when I'm parsing the file.

似乎我应该能够从变体的形状中推断出一条指令的长度。没有参数的任何东西的长度都是1,有一个u8参数的东西都是长度2,有一个u16或两个u8参数的东西都是长度3。

It seems like I should be able to just infer the length of an instruction from the shape of the variant. Anything with no argument is length 1, anything with one u8 argument is length 2, and anything with one u16 or two u8 arguments is length 3.

能够找出如何以编程方式获得此形状。例如,我不能像数组或向量那样调用 len()

I haven't been able to work out how to get this shape programatically. I can't call len() on it like an array or vector, for example.

我不认为这与如何将枚举中的元素数量获取为常数值?,因为我不是在寻找一种方法来获取枚举中的变体数量,而是找到参数的数量任何单个变体

I don't think this is a duplicate of How to get the number of elements in an enum as a constant value? as I'm not looking for a way to get the number of variants in the enum, but the number of arguments of any individual variant.

推荐答案

如注释中所述,您可以编写一个宏来为您编写代码

As mentioned in the comments, you can write a macro to write the code for you.

为了懒惰,我将枚举定义简化为总是需要括号。这不是必需的,只是简化了我的工作。

In the interest of laziness, I've simplified the enum definition to always rquire parenthesis. This isn't required, it just simplified my job.

一旦通过宏解析了枚举,我们就可以生成一个impl块,其功能与每个变体匹配。我们将每个变量的参数传递给内部宏,该宏为我们执行计数。此函数返回每个变量中的元素数:

Once the enum is parsed by the macro, we can generate an impl block with a function matching each variant. We pass the arguments of each variant to an inner macro that performs the count for us. This function returns the number of elements in each variant:

macro_rules! instructions {
    (enum $ename:ident {
        $($vname:ident ( $($vty: ty),* )),*
    }) => {
        enum $ename {
            $($vname ( $($vty),* )),*
        }

        impl $ename {
            fn len(&self) -> usize {
                match self {
                    $($ename::$vname(..) => instructions!(@count ($($vty),*))),*
                }
            }
        }
    };

    (@count ()) => (0);
    (@count ($a:ty)) => (1);
    (@count ($a:ty, $b:ty)) => (2);
    (@count ($a:ty, $b:ty, $c:ty)) => (3);
}

instructions! {
    enum Instruction {
        None(),
        One(u8),
        Two(u8, u8),
        Three(u8, u8, u8)
    }
}

fn main() {
    println!("{}", Instruction::None().len());
    println!("{}", Instruction::One(1).len());
    println!("{}", Instruction::Two(1,2).len());
    println!("{}", Instruction::Three(1,2,3).len());

}

您也可以写自定义 derive ,其功能相同。

You could also write a custom derive macro that does the same functionality.

另请参见:

  • Building an enum inside a macro
  • How can I use a macro to generate enum variants that may or may not contain associated values?
  • The Little Book of Rust Macros

这篇关于如何找到枚举变量的参数数量?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆