为C / FFI库调用分配一个对象 [英] Allocating an object for C / FFI library calls

查看:173
本文介绍了为C / FFI库调用分配一个对象的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个C库,它具有gpio的实现。有gpio_type是目标特定的,每个MCU对gpio_type有不同的定义。图书馆中的一个功能:

  void gpio_init(gpio_type * object,int32_t pin); 

我想在Rust中使用C库函数来编写Gpio对象的抽象。因此需要像不透明的指针类型(在C ++中我将创建一个类型为gpio_type的成员变量)。我想我会创建一个空的枚举(或结构),分配对象所需的空间,并将其转换为匹配C层中的类型。

  pub enum gpio_type {} 

#[link(name =gpio_lib,kind =静态)]
extern {
pub fn gpio_init(obj:* mut gpio_type,value:i32);
}

pub struct Gpio {
gpio:* mut gpio_type,
}

impl Gpio {
pub fn new (pin:u32) - > Gpio {
unsafe {
let mut gpio_ptr:&'static [u8; 4] = init(); // C中的gpio的大小是一个目标的4个字节,将被更改以后动态获取
let gpio_out = Gpio {gpio:transmute(gpio_ptr)};
gpio_init(gpio_out.gpio,pin);
gpio_out
}
}
}

目标嵌入式设备,因此没有标准,没有libc。我不想为生锈中的每个目标重新定义gpio_type(复制每个目标的C声明),寻找一些东西来为C处理的对象分配内存。



下面的代码段根据反汇编产生指向地址0的指针。拆卸Gpio新方法:

  45c:b580 push {r7,lr} 
45e:466f mov r7,sp
460:4601 mov r1,r0
462:2000 movs r0,#0
464:f000 fae6 bl a34< gpio_init>
468:2000 movs r0,#0
46a:bd80 pop {r7,pc}

任何想法为什么462是0?

解决方案


寻找一些东西来分配内存对于C将处理的对象


这样的东西呢?给结构体一个实际的大小(在这种情况下通过给它一个固定大小的数组的字节大小的项目),分配这个空间在堆上,然后把它当作一个原始的指针。

  use std :: mem; 

#[allow(missing_copy_implementations)]
pub struct Gpio([u8; 4]);

impl Gpio {
fn new() - > Gpio {Gpio([0,0,0,0])}
}

fn main(){
//分配一些字节并获得一个原始指针
let a:* mut u8 = unsafe {mem :: transmute(Box :: new(Gpio :: new())}}

//在这里使用

//完成后返回到框
let b:Box< Gpio> =不安全{mem :: transmute(a)};

//现在它将被自动删除(并释放分配的内存)

//或者你可以显式
drop(b);
}

但是,我建议做这样的事情;这很明显,不需要堆分配:

 #[allow(missing_copy_implementations)] 
pub struct Gpio([u8; 4]);

impl Gpio {
fn new() - > Gpio {Gpio([0,0,0,0])}

fn as_mut_ptr(& mut self) - > * mut u8 {
self.0.as_mut_ptr()
}
}

fn main(){
let mut g = Gpio :: new ();
let b = g.as_mut_ptr();
}

作为一个奖励,你会有一个很好的地方挂起一些方法。可能 as_mut_ptr 不需要公开,并且可能隐藏在 Gpio struct的公共方法之后。 p>

(也可以使用 未初始化 而不是 [0,0,0,0]



第二个建议的扩展示例



  //这取决于你的库,查看FFI指南了解详情
extern {
fn gpio_init(gpio:* mut u8,pin:u8);
fn gpio_pin_on(gpio:* mut u8);
fn gpio_pin_off(gpio:* mut u8);
}

#[allow(missing_copy_implementations)]
pub struct Gpio([u8; 4]);

impl Gpio {
fn new(pin:u8) - > Gpio {
let mut g = Gpio([0,0,0,0]);
g.init(pin);
g
}

fn as_mut_ptr(& mut self) - > * mut u8 {
self.0.as_mut_ptr()
}

fn init(& mut self,pin:u8){unsafe {gpio_init(self.as_mut_ptr() ,pin)}}
pub fn on(& mut self){unsafe {gpio_pin_on(self.as_mut_ptr())}}
pub fn off(& mut self){unsafe {gpio_pin_off(self .as_mut_ptr())}}
}

static BLUE_LED_PIN:u8 = 0x4;

fn main(){
let mut g = Gpio :: new(BLUE_LED_PIN);
g.on();
g.off();
}


I have a C library, which has gpio implementation. There's gpio_type which is target specific, each MCU has different definition for gpio_type. One of the functions in the library:

void gpio_init(gpio_type *object, int32_t pin);

I want to write abstraction of Gpio object in Rust, using C library functions. Therefore need something like opaque pointer type (in C++ I would just create a member variable with type: gpio_type). I figured I would create an empty enum (or struct), allocate a space needed for the object and transmute it to match the type in C layer.

pub enum gpio_type {}

#[link(name = "gpio_lib", kind = "static")]
extern {
    pub fn gpio_init(obj: *mut gpio_type, value: i32);
}

pub struct Gpio {
    gpio : *mut gpio_type,
}

impl Gpio {
    pub fn new(pin: u32) -> Gpio {
        unsafe {
            let mut gpio_ptr : &'static [u8; 4] = init(); // size of gpio in C is 4 bytes for one target, will be changed later to obtain it dynamically
            let gpio_out = Gpio { gpio: transmute(gpio_ptr)};
            gpio_init(gpio_out.gpio, pin);
            gpio_out
        }
    }
}

This targets embedded devices, therefore no std, no libc. I don't want to redefine gpio_type for each target in rust (copy the C declaration for each target), looking for something to just allocate memory for the object which C will handle.

The following snippet below produces pointer to address 0 according to disassembly. Disassembly for Gpio new method:

 45c:   b580        push    {r7, lr}
 45e:   466f        mov r7, sp
 460:   4601        mov r1, r0
 462:   2000        movs    r0, #0
 464:   f000 fae6   bl  a34 <gpio_init>
 468:   2000        movs    r0, #0
 46a:   bd80        pop {r7, pc}

Any ideas why 462 is 0 ?

解决方案

looking for something to just allocate memory for the object which C will handle

What about something like this? Give the struct an actual size (in this case by giving it a fixed-size array of byte-sized items), allocate that space on the heap, then treat that as a raw pointer.

use std::mem;

#[allow(missing_copy_implementations)]
pub struct Gpio([u8; 4]);

impl Gpio {
    fn new() -> Gpio { Gpio([0,0,0,0]) }
}

fn main() {
    // Allocate some bytes and get a raw pointer
    let a: *mut u8 = unsafe { mem::transmute(Box::new(Gpio::new())) };

    // Use it here!

    // When done... back to a box
    let b: Box<Gpio> = unsafe { mem::transmute(a) };

    // Now it will be dropped automatically (and free the allocated memory)

    // Or you can be explicit
    drop(b);
}

However, I'd suggest doing something like this; it's a lot more obvious and doesn't need a heap allocation:

#[allow(missing_copy_implementations)]
pub struct Gpio([u8; 4]);

impl Gpio {
    fn new() -> Gpio { Gpio([0,0,0,0]) }

    fn as_mut_ptr(&mut self) -> *mut u8 {
        self.0.as_mut_ptr()
    }
}

fn main() {
    let mut g = Gpio::new();
    let b = g.as_mut_ptr();
}

As a bonus, you get a nice place to hang some methods on. Potentially as_mut_ptr wouldn't need to be public, and could be hidden behind public methods on the Gpio struct.

(might also be able to use uninitialized instead of [0,0,0,0])

An expanded example of the second suggestion

// This depends on your library, check the FFI guide for details
extern {
    fn gpio_init(gpio: *mut u8, pin: u8);
    fn gpio_pin_on(gpio: *mut u8);
    fn gpio_pin_off(gpio: *mut u8);
}

#[allow(missing_copy_implementations)]
pub struct Gpio([u8; 4]);

impl Gpio {
    fn new(pin: u8) -> Gpio {
        let mut g = Gpio([0,0,0,0]);
        g.init(pin);
        g
    }

    fn as_mut_ptr(&mut self) -> *mut u8 {
        self.0.as_mut_ptr()
    }

    fn init(&mut self, pin: u8) { unsafe { gpio_init(self.as_mut_ptr(), pin) } }
    pub fn on(&mut self) { unsafe { gpio_pin_on(self.as_mut_ptr()) } }
    pub fn off(&mut self) { unsafe { gpio_pin_off(self.as_mut_ptr()) } }
}

static BLUE_LED_PIN: u8 = 0x4;

fn main() {
    let mut g = Gpio::new(BLUE_LED_PIN);
    g.on();
    g.off();
}

这篇关于为C / FFI库调用分配一个对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆