自动生成匹配的过程宏

huangapple go评论62阅读模式
英文:

Procedural macro to auto-generate match

问题

I am writing a toy virtual machine, where I need to decode instructions. I have an Instruction trait with some methods and concrete instructions implement this trait. Also, I have a decode function which just takes a byte and matches it to return a specific instruction corresponding to the byte given.

pub struct FirstInstruction { ... }

pub struct SecondInstruction { ... }

pub struct ThirdInstruction { ... }

pub fn decode(byte: u8) -> Box<dyn Instruction> {
  match byte {
    0x1 => Box::new(FirstInstruction::new()),
    0x2 => Box::new(SecondInstruction::new()),
    0x3 => Box::new(ThirdInstruction::new()),
  }
}

I was wondering how to write a procedural macro that would allow me to auto-generate the decode function. At the same time, it will get the byte to match in its arguments like this:

#[instruction(0x1)]
pub struct FirstInstruction { ... }

#[instruction(0x2)]
pub struct SecondInstruction { ... }

#[instruction(0x3)]
pub struct ThirdInstruction { ... }
  
pub fn decode(byte: u8) -> Box<dyn Instruction> {
  // Autogenerated
}

I understand that from the practical standpoint this whole thing can be useless. I am just curious about how it can be implemented using macros, if it is even possible.

I tried to read about how to write procedural macros, but I don't understand how one can accumulate information about all structs with the specific attribute and then generate a whole new function.

英文:

I am writing a toy virtual machine, where I need to decode instructions. I have an Instruction trait with some methods and concrete instructions implement this trait. Also, I have a decode function which just takes a byte and matches it to return a specific instruction corresponding to the byte given.

pub struct FirstInstruction { ... }

pub struct SecondInstruction { ... }

pub struct ThirdInstruction { ... }

pub fn decode(byte: u8) -&gt; Box&lt;dyn Instruction&gt; {
  match byte {
    0x1 =&gt; Box::new(FirstInstruction::new()),
    0x2 =&gt; Box::new(SecondInstruction::new()),
    0x3 =&gt; Box::new(ThirdInstruction::new()),
  }
}

I was wondering how to write a procedural macro that would allow me to auto-generate the decode function. At the same time, it will get the byte to match in its arguments like this:

#[instruction(0x1)]
pub struct FirstInstruction { ... }

#[instruction(0x2)]
pub struct SecondInstruction { ... }

#[instruction(0x3)]
pub struct ThirdInstruction { ... }
  
pub fn decode(byte: u8) -&gt; Box&lt;dyn Instruction&gt; {
  // Autogenerated
}

I understand that from the practical standpoint this whole thing can be useless. I am just curious about how it can be implemented using macros, if it is even possible.

I tried to read about how to write procedural macros, but I don't understand how one can accumulate information about all structs with the specific attribute and then generate a whole new function.

答案1

得分: 1

在一天结束时,我发现这很难实现。
我决定坚持使用一个简单的宏来生成`decode`函数:

```Rust
macro_rules! register_instructions {
    {
        $(
            $code_value:expr => $struct_name:ty
        ),*
    } => {
        pub fn decode(code: &[u8]) -> Box<dyn Instruction> {
            let instruction_code = code[0];
            match instruction_code {
                $($code_value => Box::new(<$struct_name>::new(code))),*,
                _ => panic!("Invalid instruction"),
            }
        }
    }
}

现在它看起来是这样的:

register_instructions! {
    0x01 => AddInstruction,
    0x02 => SubInstruction,
    0x03 => MulInstruction,
    0x04 => DivInstruction,
    0x05 => JumpInstruction,
    0x06 => LoadInstruction,
    0x07 => FinishInstruction,
    0x08 => OutInstruction,
    0x09 => EqualInstruction,
    0x0A => LessInstruction,
    0x0B => LessEqualInstruction,
    0x0C => LoadAbsoluteInstruction
}
英文:

At the end of the day I found out that this is pretty hard to implement.
I decided to stick to a simple macro that would generate the decode function:

macro_rules! register_instructions {
    {
        $(
            $code_value:expr =&gt; $struct_name:ty
        ),*
    } =&gt; {
        pub fn decode(code: &amp;[u8]) -&gt; Box&lt;dyn Instruction&gt; {
            let instruction_code = code[0];
            match instruction_code {
                $($code_value =&gt; Box::new(&lt;$struct_name&gt;::new(code))),*,
                _ =&gt; panic!(&quot;Invalid instruction&quot;),
            }
        }
    }
}

Now it looks like this:

register_instructions! {
    0x01 =&gt; AddInstruction,
    0x02 =&gt; SubInstruction,
    0x03 =&gt; MulInstruction,
    0x04 =&gt; DivInstruction,
    0x05 =&gt; JumpInstruction,
    0x06 =&gt; LoadInstruction,
    0x07 =&gt; FinishInstruction,
    0x08 =&gt; OutInstruction,
    0x09 =&gt; EqualInstruction,
    0x0A =&gt; LessInstruction,
    0x0B =&gt; LessEqualInstruction,
    0x0C =&gt; LoadAbsoluteInstruction
}

huangapple
  • 本文由 发表于 2023年2月19日 18:50:43
  • 转载请务必保留本文链接:https://go.coder-hub.com/75499590.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定