首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何创建FFI绑定到C函数,期望使用OR-ed字节?

如何创建FFI绑定到C函数,期望使用OR-ed字节?
EN

Stack Overflow用户
提问于 2017-10-20 07:53:37
回答 2查看 545关注 0票数 0

我尝试创建到[医]二线菌的FFI绑定,用C编写,这里我偶然发现了这个函数

代码语言:javascript
复制
modbus_set_error_recovery(ctx,
                          MODBUS_ERROR_RECOVERY_LINK |
                          MODBUS_ERROR_RECOVERY_PROTOCOL);

第二个参数定义为

代码语言:javascript
复制
typedef enum
{
    MODBUS_ERROR_RECOVERY_NONE          = 0,
    MODBUS_ERROR_RECOVERY_LINK          = (1<<1),
    MODBUS_ERROR_RECOVERY_PROTOCOL      = (1<<2)
} modbus_error_recovery_mode;

我的bindgen-generated绑定如下:

代码语言:javascript
复制
#[repr(u32)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum modbus_error_recovery_mode {
    MODBUS_ERROR_RECOVERY_NONE = 0,
    MODBUS_ERROR_RECOVERY_LINK = 2,
    MODBUS_ERROR_RECOVERY_PROTOCOL = 4,
}

代码语言:javascript
复制
extern "C" {
    pub fn modbus_set_error_recovery(ctx: *mut modbus_t,
                                     error_recovery:
                                         modbus_error_recovery_mode)
     -> ::std::os::raw::c_int;
}

到目前为止,我的安全界面是这样的:

代码语言:javascript
复制
pub fn set_error_recovery(&mut self, error_recovery_mode: ErrorRecoveryMode) -> Result<()> {

    unsafe {
        match ffi::modbus_set_error_recovery(self.ctx, error_recovery_mode.to_c()) {
            -1 => bail!(Error::last_os_error()),
            0 => Ok(()),
            _ => panic!("libmodbus API incompatible response"),
        }
    }
}

代码语言:javascript
复制
use std::ops::BitOr;

#[derive(Clone, Copy, PartialEq, Eq)]
pub enum ErrorRecoveryMode {
    NONE = 0,
    Link = 2,
    Protocol = 4,
}

impl ErrorRecoveryMode {
    pub fn to_c(self) -> ffi::modbus_error_recovery_mode {
        match self {
            NONE => ffi::modbus_error_recovery_mode::MODBUS_ERROR_RECOVERY_NONE,
            Link => ffi::modbus_error_recovery_mode::MODBUS_ERROR_RECOVERY_LINK,
            Protocol => ffi::modbus_error_recovery_mode::MODBUS_ERROR_RECOVERY_PROTOCOL,
        }
    }
}

impl BitOr for ErrorRecoveryMode {
    type Output = Self;
    fn bitor(self, rhs: ErrorRecoveryMode) -> ErrorRecoveryMode {
        self | rhs
    }
}

如果我这样调用set_error_recovery,就会触发堆栈溢出。

代码语言:javascript
复制
assert!(modbus.set_error_recovery(ErrorRecoveryMode::Link | ErrorRecoveryMode::Protocol).is_ok())

错误是

代码语言:javascript
复制
thread 'set_error_recovery' has overflowed its stack
fatal runtime error: stack overflow
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2017-10-20 07:59:08

问题是C的enum和Rust的enum非常不同的。特别是,C允许enum具有绝对的任何值,无论该值是否与变体相对应。

铁锈不会。锈蚀依赖于enum,它只有定义变量的单个值,否则就会面临未定义行为的风险。

您拥有的不是枚举(在锈蚀意义上),而是位标志,您需要bitflags机箱。

至于堆栈溢出,这仅仅是因为您根据自身定义了BitOr实现;该代码是无条件递归的。

票数 2
EN

Stack Overflow用户

发布于 2017-10-20 13:07:16

作为DK。提到:

  • C's enum和Rust's enum有不同的限制。
  • 它是无效的,有一个锈菌枚举,而不是一个枚举变体。
  • 你所拥有的叫做“位标志”。

幸运的是,Bindgen理解位标志。如果在传递bitfield-enum标志时或通过使用Builder::bitfield_enum生成标头

代码语言:javascript
复制
bindgen --bitfield-enum modbus_error_recovery_mode fake-modbus.h

Bindgen将为每个C枚举值、一个新类型的包装器和Bit*特性的实现生成常量:

代码语言:javascript
复制
// Many implementation details removed 

pub struct modbus_error_recovery_mode(pub ::std::os::raw::c_uint);

pub const modbus_error_recovery_mode_MODBUS_ERROR_RECOVERY_NONE: modbus_error_recovery_mode =
    modbus_error_recovery_mode(0);    
pub const modbus_error_recovery_mode_MODBUS_ERROR_RECOVERY_LINK: modbus_error_recovery_mode =
    modbus_error_recovery_mode(2);
pub const modbus_error_recovery_mode_MODBUS_ERROR_RECOVERY_PROTOCOL: modbus_error_recovery_mode =
    modbus_error_recovery_mode(4);

impl ::std::ops::BitOr<modbus_error_recovery_mode> for modbus_error_recovery_mode {}
impl ::std::ops::BitOrAssign for modbus_error_recovery_mode {}
impl ::std::ops::BitAnd<modbus_error_recovery_mode> for modbus_error_recovery_mode {} 
impl ::std::ops::BitAndAssign for modbus_error_recovery_mode {}

extern "C" {
    pub fn modbus_set_error_recovery(
        ctx: *mut modbus_t,
        error_recovery: modbus_error_recovery_mode,
    ) -> ::std::os::raw::c_int;
}

如何向公众公开由bindgen生成的常量

当然,为非锈蚀代码创建一个惯用的Rust是很难的。我可能会尝试这样的方法:

代码语言:javascript
复制
#[derive(Debug)]
struct Modbus(*mut raw::modbus_t);

#[derive(Debug)]
struct Error;

#[derive(Debug, Copy, Clone)]
enum ErrorRecovery {
    Link,
    Protocol,
}

impl ErrorRecovery {
    fn as_raw(&self) -> raw::modbus_error_recovery_mode {
        use ErrorRecovery::*;

        match *self {
            Link => raw::modbus_error_recovery_mode_MODBUS_ERROR_RECOVERY_LINK,
            Protocol => raw::modbus_error_recovery_mode_MODBUS_ERROR_RECOVERY_PROTOCOL,
        }
    }
}

impl Modbus {
    fn set_error_recovery(&mut self, flags: Option<&[ErrorRecovery]>) -> Result<(), Error> {
        let flag = flags.unwrap_or(&[]).iter().fold(
            raw::modbus_error_recovery_mode_MODBUS_ERROR_RECOVERY_NONE,
            |acc, v| acc | v.as_raw(),
        );

        let res = unsafe { raw::modbus_set_error_recovery(self.0, flag) };
        Ok(()) // real error checking
    }
}
票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/46844611

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档