简体   繁体   English

如何将 bindgen 生成的 C 样式枚举转换为另一个枚举?

[英]How do I convert a C-style enum generated by bindgen to another enum?

I am creating bindings in Rust for a C library and Bindgen generated enums like:我正在为 C 库和 Bindgen 生成的枚举在 Rust 中创建绑定,例如:

// 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,
}

I need to convert these to:我需要将这些转换为:

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

When importing constant values from this C library:从这个 C 库导入常量值时:

// C library
const rmw_qos_history_policy_t some_value_from_C = RMW_QOS_POLICY_HISTORY_SYSTEM_DEFAULT;

I would like to do something like:我想做类似的事情:

let some_value: QoSHistoryPolicy = some_value_from_C;

How can I go about it?我该怎么办?

The compiler does not inspect enums for ABI compatibility, and as such does not provide a direct way to convert values between these types.编译器不检查枚举的 ABI 兼容性,因此不提供在这些类型之间转换值的直接方法。 A few possible solutions follow.以下是一些可能的解决方案。

1. One-by-one matching 1.一对一匹配

This is trivial and safe, albeit leading to exhaustive code.这是微不足道的和安全的,尽管会导致详尽的代码。

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 2. Casting + FromPrimitive

Rust allows you to convert field-less enums into an integer type using the as operator. Rust 允许您使用as运算符将无字段枚举转换为整数类型。 The opposite conversion is not always safe however.然而,相反的转换并不总是安全的。 Derive FromPrimitive using the num crate to obtain the missing piece.使用num crate 派生FromPrimitive以获得丢失的部分。

#[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. Need an enum? 3. 需要枚举吗?

In the event that you just want an abstraction to low-level bindings, you might go without a new enum type.如果您只想抽象到低级绑定,您可能不需要新的枚举类型。

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

The type above contains the same information and binary representation, but can expose an encapsulated API.上面的类型包含相同的信息和二进制表示,但可以公开封装的 API。 The conversion from the low-level type to the high-level type becomes trivial.从低级类型到高级类型的转换变得微不足道。 The main downside is that you lose pattern matching over its variants.主要的缺点是你失去了与其变体的模式匹配。

4. You're on your own 4. 靠你自己

When absolutely sure that the two enums are equivalent in their binary representation, you can transmute between them.绝对确保两个枚举二进制表示是等价的,你可以transmute它们之间。 The compiler won't help you here, this is far from recommended.编译器不会在这里帮助您,这远非推荐。

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

See also:也可以看看:

It looks to be a good candidate for the From trait on QoSHistoryPolicy .它看起来是QoSHistoryPolicyFrom特征的一个很好的候选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
        }
    }
}

so this should now work所以这现在应该可以工作了

let some_value: QoSHistoryPolicy = some_value_from_C.into();

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM