You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Rust中包装pub extern "C" fn以调用结构体方法?

问题

需要包装一个pub extern "C" fn on_sync() { ... },使其被调用时能触发BluetoothDevice结构体impl中定义的pub fn on_sync(&self) { ... }方法。

现有代码

BluetoothDevice 结构体及实现

use super::helpers;
use crate::lib::log::log::Logger;
use esp_idf_sys;

extern crate alloc;

pub struct BluetoothDevice<'a> {
    logger: &'a Logger,
}

impl<'a> BluetoothDevice<'a> {
    pub fn new(logger: &'a Logger) -> Self {
        BluetoothDevice { logger }
    }

    pub fn init(&self) {
        unsafe {
            // ...

            esp_idf_sys::ble_hs_cfg.sync_cb = Some(helpers::on_sync);
            esp_idf_sys::ble_hs_cfg.reset_cb = Some(helpers::on_reset);

            // ...
        }
    }

    pub fn on_sync(&self) {}

    pub fn on_reset(&self) {}
}

helpers 模块代码

use super::device;

struct BluetoothGAPWorkaround<'a> {
    instance: Option<&'a device::BluetoothDevice<'a>>,
}

static mut BLUETOOTH_GAP_WORKAROUND: BluetoothGAPWorkaround =
    BluetoothGAPWorkaround { instance: None };

pub extern "C" fn on_sync() {
    unsafe {
        match BLUETOOTH_GAP_WORKAROUND.instance {
            Some(device) => device.on_sync(),
            None => return,
        }
    }
}

pub extern "C" fn on_reset() {
    unsafe {
        match BLUETOOTH_GAP_WORKAROUND.instance {
            Some(device) => device.on_reset(),
            None => return,
        }
    }
}

pub fn set_device<'a>(bluetoothDevice: &'a device::BluetoothDevice<'a>) {
    unsafe {
        if let None = BLUETOOTH_GAP_WORKAROUND.instance {
            BLUETOOTH_GAP_WORKAROUND.instance = Some(bluetoothDevice);
        };
    }
}

当前错误

helpers模块的set_device函数报错:lifetime may not live long enough assignment requires that "'a" must outlive "'static"。

需求

希望在Rust中实现类似C的解决方案(尽量避免将device设为static变量),无需通过C薄包装调用。C++参考代码如下:

#include "bluetoothGAPWorkarounds.h"
#include <host/ble_hs.h>

struct BluetoothGAPWorkaround
{
    boundOnSyncCallback *onSyncCallback;
    boundOnResetCallback *onResetCallback;
    void *onSyncCallbackArg;
    void *onResetCallbackArg;
} workaround;

static void unboundOnSyncCallback(void)
{
    if (!workaround.onSyncCallback)
    {
        return;
    }

    workaround.onSyncCallback(&workaround.onSyncCallbackArg);
}

static void unboundOnResetCallback(int reason)
{
    if (!workaround.onResetCallback)
    {
        return;
    }

    workaround.onResetCallback(reason, &workaround.onResetCallbackArg);
}

void setOnSyncCallback(boundOnSyncCallback callback, void *arg)
{
    workaround.onSyncCallback = callback;
    workaround.onSyncCallbackArg = arg;
    ble_hs_cfg.sync_cb = unboundOnSyncCallback;
}

void setOnResetCallback(boundOnResetCallback callback, void *arg)
{
    workaround.onResetCallback = callback;
    workaround.onResetCallbackArg = arg;
    ble_hs_cfg.reset_cb = unboundOnResetCallback;
}
解决方案

方案1:模仿C++的回调+上下文参数模式

核心思路是存储回调函数与对应上下文(BluetoothDevice实例),C回调触发时通过上下文调用实例方法,用裸指针绕过Rust生命周期限制(需自行保证实例有效性)。

修改helpers模块如下:

use super::device;
use core::ptr;

// 定义适配C调用约定的回调类型
type SyncCallback = unsafe extern "C" fn(*const ());
type ResetCallback = unsafe extern "C" fn(i32, *const ());

struct CallbackStore {
    sync_cb: Option<SyncCallback>,
    sync_arg: *const (),
    reset_cb: Option<ResetCallback>,
    reset_arg: *const (),
}

static mut CALLBACK_STORE: CallbackStore = CallbackStore {
    sync_cb: None,
    sync_arg: ptr::null(),
    reset_cb: None,
    reset_arg: ptr::null(),
};

// 包装BluetoothDevice的on_sync方法为C兼容函数
unsafe extern "C" fn wrap_on_sync(arg: *const ()) {
    let device = arg as *const device::BluetoothDevice<'static>;
    (*device).on_sync();
}

// 包装BluetoothDevice的on_reset方法为C兼容函数
unsafe extern "C" fn wrap_on_reset(reason: i32, arg: *const ()) {
    let device = arg as *const device::BluetoothDevice<'static>;
    (*device).on_reset();
}

pub extern "C" fn on_sync() {
    unsafe {
        if let Some(cb) = CALLBACK_STORE.sync_cb {
            cb(CALLBACK_STORE.sync_arg);
        }
    }
}

pub extern "C" fn on_reset(reason: i32) {
    unsafe {
        if let Some(cb) = CALLBACK_STORE.reset_cb {
            cb(reason, CALLBACK_STORE.reset_arg);
        }
    }
}

pub fn set_callbacks(device: &device::BluetoothDevice<'_>) {
    unsafe {
        CALLBACK_STORE.sync_cb = Some(wrap_on_sync);
        CALLBACK_STORE.sync_arg = device as *const _ as *const ();
        
        CALLBACK_STORE.reset_cb = Some(wrap_on_reset);
        CALLBACK_STORE.reset_arg = device as *const _ as *const ();
    }
}

修改BluetoothDevice的init方法,添加回调设置:

pub fn init(&self) {
    unsafe {
        helpers::set_callbacks(self);
        esp_idf_sys::ble_hs_cfg.sync_cb = Some(helpers::on_sync);
        esp_idf_sys::ble_hs_cfg.reset_cb = Some(helpers::on_reset);
        // ...
    }
}

注意:必须确保BluetoothDevice实例的生命周期覆盖所有可能触发C回调的时间段,否则会出现悬垂指针问题。

方案2:全局静态+原子指针(允许全局变量场景)

若可接受全局变量,用原子指针安全管理设备实例,规避生命周期报错:

use super::device;
use core::sync::atomic::{AtomicPtr, Ordering};

static DEVICE_INSTANCE: AtomicPtr<device::BluetoothDevice<'static>> = AtomicPtr::new(core::ptr::null_mut());

pub extern "C" fn on_sync() {
    unsafe {
        let ptr = DEVICE_INSTANCE.load(Ordering::Acquire);
        if !ptr.is_null() {
            (*ptr).on_sync();
        }
    }
}

pub extern "C" fn on_reset() {
    unsafe {
        let ptr = DEVICE_INSTANCE.load(Ordering::Acquire);
        if !ptr.is_null() {
            (*ptr).on_reset();
        }
    }
}

pub fn set_device(device: &'static device::BluetoothDevice<'static>) {
    DEVICE_INSTANCE.store(device as *const _ as *mut _, Ordering::Release);
}

说明:需将BluetoothDevice的生命周期改为'static,或通过Arc包裹内部数据延长生命周期,此方式安全性更高但依赖全局变量。

内容的提问来源于stack exchange,提问作者Mihai Bojescu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 07:05:19