I have hit a wall recently. The summary would be the following: I would like to wrap a pub extern "C" fn on_sync() { ... }
such that when it is called I can also call a method named pub fn on_sync(&self) { ... }
defined in an impl
of a struct BluetoothDevice
.
The struct BluetoothDevice
and its impl
can be found below:
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) {}
}
And the helpers
module can be found below:
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);
};
}
}
The problem with the Rust helpers
module is the fact that I keep hitting a wall in the pub fn set_device<'a>(...) { ... }
function due to the following error: lifetime may not live long enough assignment requires that "'a" must outlive "'static"
.
Would there be any solution of wrapping pub extern "C" on_sync() { ... }
such that I can call device.on_sync()
, preferrably without having to set the device
as a static
variable?
Previously I hit this issue in C++ too and I fixed it with the following and would like to perform the same thing in Rust:
#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;
}
I needed I can use the code above in a thin C++ wrapper that I can access from the Rust code, but that would not be ideal.