Как найти количество аргументов варианта e num?
У меня есть enum, который представляет все возможные инструкции для процессора 8080. Инструкция может иметь длину 1, 2 или 3 байта, в зависимости от того, имеет ли она информацию, связанную с ней, и ее объем. Например:
#[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 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),
// ...
}
Это некрасиво, но я не хочу связывать это число длины с типом, потому что это действительно важно только при анализе файла.
Кажется, что я должен иметь возможность просто определить длину инструкции по форме варианта. Все без аргументов имеет длину 1, все с одним аргументом u8 - это длина 2, а все с одним аргументом u16 или двумя аргументами u8 - это длина 3.
Я не смог понять, как получить эту форму программно. Я не могу позвонить len()
например, массив или вектор.
Я не думаю, что это дубликат Как получить количество элементов в перечислении как постоянное значение? так как я ищу не способ получения количества вариантов в перечислении, а количество аргументов для каждого отдельного варианта.
1 ответ
Как уже упоминалось в комментариях, вы можете написать макрос, чтобы написать код для вас.
В интересах лени я упростил определение enum, чтобы всегда запрашивать скобки. Это не обязательно, это просто упростило мою работу.
После того как перечисление будет проанализировано макросом, мы можем сгенерировать блок impl с функцией, соответствующей каждому варианту. Мы передаем аргументы каждого варианта внутреннему макросу, который выполняет подсчет для нас. Эта функция возвращает количество элементов в каждом варианте:
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
макрос, который выполняет те же функции.
Смотрите также: