Как найти количество аргументов варианта enum? - PullRequest
0 голосов
/ 28 января 2019

У меня есть 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 Ответ

0 голосов
/ 28 января 2019

Как уже упоминалось в комментариях, вы можете написать макрос для написания кода для вас.

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

См. Также:

...