自动生成匹配的过程宏

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

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.

  1. pub struct FirstInstruction { ... }
  2. pub struct SecondInstruction { ... }
  3. pub struct ThirdInstruction { ... }
  4. pub fn decode(byte: u8) -> Box<dyn Instruction> {
  5. match byte {
  6. 0x1 => Box::new(FirstInstruction::new()),
  7. 0x2 => Box::new(SecondInstruction::new()),
  8. 0x3 => Box::new(ThirdInstruction::new()),
  9. }
  10. }

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:

  1. #[instruction(0x1)]
  2. pub struct FirstInstruction { ... }
  3. #[instruction(0x2)]
  4. pub struct SecondInstruction { ... }
  5. #[instruction(0x3)]
  6. pub struct ThirdInstruction { ... }
  7. pub fn decode(byte: u8) -> Box<dyn Instruction> {
  8. // Autogenerated
  9. }

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.

  1. pub struct FirstInstruction { ... }
  2. pub struct SecondInstruction { ... }
  3. pub struct ThirdInstruction { ... }
  4. pub fn decode(byte: u8) -&gt; Box&lt;dyn Instruction&gt; {
  5. match byte {
  6. 0x1 =&gt; Box::new(FirstInstruction::new()),
  7. 0x2 =&gt; Box::new(SecondInstruction::new()),
  8. 0x3 =&gt; Box::new(ThirdInstruction::new()),
  9. }
  10. }

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:

  1. #[instruction(0x1)]
  2. pub struct FirstInstruction { ... }
  3. #[instruction(0x2)]
  4. pub struct SecondInstruction { ... }
  5. #[instruction(0x3)]
  6. pub struct ThirdInstruction { ... }
  7. pub fn decode(byte: u8) -&gt; Box&lt;dyn Instruction&gt; {
  8. // Autogenerated
  9. }

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

  1. 在一天结束时,我发现这很难实现。
  2. 我决定坚持使用一个简单的宏来生成`decode`函数:
  3. ```Rust
  4. macro_rules! register_instructions {
  5. {
  6. $(
  7. $code_value:expr => $struct_name:ty
  8. ),*
  9. } => {
  10. pub fn decode(code: &[u8]) -> Box<dyn Instruction> {
  11. let instruction_code = code[0];
  12. match instruction_code {
  13. $($code_value => Box::new(<$struct_name>::new(code))),*,
  14. _ => panic!("Invalid instruction"),
  15. }
  16. }
  17. }
  18. }

现在它看起来是这样的:

  1. register_instructions! {
  2. 0x01 => AddInstruction,
  3. 0x02 => SubInstruction,
  4. 0x03 => MulInstruction,
  5. 0x04 => DivInstruction,
  6. 0x05 => JumpInstruction,
  7. 0x06 => LoadInstruction,
  8. 0x07 => FinishInstruction,
  9. 0x08 => OutInstruction,
  10. 0x09 => EqualInstruction,
  11. 0x0A => LessInstruction,
  12. 0x0B => LessEqualInstruction,
  13. 0x0C => LoadAbsoluteInstruction
  14. }
英文:

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:

  1. macro_rules! register_instructions {
  2. {
  3. $(
  4. $code_value:expr =&gt; $struct_name:ty
  5. ),*
  6. } =&gt; {
  7. pub fn decode(code: &amp;[u8]) -&gt; Box&lt;dyn Instruction&gt; {
  8. let instruction_code = code[0];
  9. match instruction_code {
  10. $($code_value =&gt; Box::new(&lt;$struct_name&gt;::new(code))),*,
  11. _ =&gt; panic!(&quot;Invalid instruction&quot;),
  12. }
  13. }
  14. }
  15. }

Now it looks like this:

  1. register_instructions! {
  2. 0x01 =&gt; AddInstruction,
  3. 0x02 =&gt; SubInstruction,
  4. 0x03 =&gt; MulInstruction,
  5. 0x04 =&gt; DivInstruction,
  6. 0x05 =&gt; JumpInstruction,
  7. 0x06 =&gt; LoadInstruction,
  8. 0x07 =&gt; FinishInstruction,
  9. 0x08 =&gt; OutInstruction,
  10. 0x09 =&gt; EqualInstruction,
  11. 0x0A =&gt; LessInstruction,
  12. 0x0B =&gt; LessEqualInstruction,
  13. 0x0C =&gt; LoadAbsoluteInstruction
  14. }

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:

确定