Как преобразовать перечисление в стиле C, созданное bindgen, в другое перечисление? - PullRequest
1 голос
/ 18 марта 2020

Я создаю привязки в Rust для библиотеки C и сгенерированных Bindgen перечислений, таких как:

// Rust
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum rmw_qos_history_policy_t {
    RMW_QOS_POLICY_HISTORY_SYSTEM_DEFAULT = 0,
    RMW_QOS_POLICY_HISTORY_KEEP_LAST = 1,
    RMW_QOS_POLICY_HISTORY_KEEP_ALL = 2,
    RMW_QOS_POLICY_HISTORY_UNKNOWN = 3,
}

Мне нужно преобразовать их в:

// Rust
pub enum QoSHistoryPolicy {
    SystemDefault = 0,
    KeepLast = 1,
    KeepAll = 2,
    Unknown = 3,
}

При импорте константных значений из этой C библиотеки:

// C library
const rmw_qos_history_policy_t some_value_from_C = RMW_QOS_POLICY_HISTORY_SYSTEM_DEFAULT;

Я хотел бы сделать что-то вроде:

let some_value: QoSHistoryPolicy = some_value_from_C;

Как я могу go об этом?

Ответы [ 2 ]

7 голосов
/ 18 марта 2020

Компилятор не проверяет перечисления на совместимость с ABI и, как таковой, не обеспечивает прямой способ преобразования значений между этими типами. Далее следуют несколько возможных решений.

1. Индивидуальное сопоставление

Это тривиально и безопасно, хотя и приводит к исчерпывающему коду.

impl From<rmw_qos_history_policy_t> for QoSHistoryPolicy {
    fn from(x: rmw_qos_history_policy_t) -> Self {
        use rmw_qos_history_policy_t::*;
        match x {
            RMW_QOS_POLICY_HISTORY_SYSTEM_DEFAULT => QoSHistoryPolicy::SystemDefault,
            RMW_QOS_POLICY_HISTORY_KEEP_LAST => QoSHistoryPolicy::KeepLast,
            RMW_QOS_POLICY_HISTORY_KEEP_ALL => QoSHistoryPolicy::KeepAll,
            RMW_QOS_POLICY_HISTORY_UNKNOWN => QoSHistoryPolicy::Unknown,
        }
    }
}

2. Casting + FromPrimitive

Rust позволяет преобразовывать перечисления без полей в целочисленный тип с помощью оператора as. Однако обратное преобразование не всегда безопасно. Получите FromPrimitive, используя ящик num, чтобы получить недостающую часть.

#[derive(FromPrimitive)]
pub enum QoSHistoryPolicy { ... }

impl From<rmw_qos_history_policy_t> for QoSHistoryPolicy {
    fn from(x: rmw_qos_history_policy_t) -> Self {
        FromPrimitive::from_u32(x as _).expect("1:1 enum variant matching, all good")
    }
}

3. Нужно перечисление?

В случае, если вам нужна абстракция для низкоуровневых привязок, вы можете go без нового типа перечисления.

#[repr(transparent)]
pub struct QoSHistoryPolicy(rmw_qos_history_policy_t);

Тип выше содержит та же информация и двоичное представление, но может предоставить инкапсулированный API. Преобразование из типа низкого уровня в тип высокого уровня становится тривиальным. Основным недостатком является то, что вы теряете сопоставление с образцом по его вариантам.

4. Вы сами по себе

Когда абсолютно уверены , что два перечисления эквивалентны в их двоичном представлении, вы можете transmute между ними. Компилятор вам здесь не поможет, это далеко не рекомендуется.

unsafe {
    let policy: QoSHistoryPolicy = std::mem::transmute(val);
}

См. Также:

3 голосов
/ 18 марта 2020

Похоже, что это хороший кандидат на From черту на QoSHistoryPolicy.

impl From<rmw_qos_history_policy_t> for QoSHistoryPolicy {
    fn from(raw: rmw_qos_history_policy_t) -> Self {
        match raw {
            rmw_qos_history_policy_t::RMW_QOS_POLICY_HISTORY_SYSTEM_DEFAULT => QoSHistoryPolicy::SystemDefault,
            rmw_qos_history_policy_t::RMW_QOS_POLICY_HISTORY_KEEP_LAST => QoSHistoryPolicy::KeepLast,
            rmw_qos_history_policy_t::RMW_QOS_POLICY_HISTORY_KEEP_ALL => QoSHistoryPolicy::KeepAll,
            rmw_qos_history_policy_t::RMW_QOS_POLICY_HISTORY_UNKNOWN => QoSHistoryPolicy::Unknown
        }
    }
}

, так что теперь это должно работать

let some_value: QoSHistoryPolicy = some_value_from_C.into();
...