Как найти количество аргументов варианта 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 макрос, который выполняет те же функции.

Смотрите также:

Другие вопросы по тегам