Why does Cargo incorrectly compile Rust WASM global variables? - rust

The following Rust compiled to WASM, based on the Wasmer exports_global.rs WASM example, produces incorrect WASM (with cargo build --target=wasm32-wasi):
// (module
// (global $one (export "one") f32 (f32.const 1))
// (global $some (export "some") (mut f32) (f32.const 0))
// (func (export "get_one") (result f32) (global.get $one))
// (func (export "get_some") (result f32) (global.get $some))
// (func (export "set_some") (param f32) (global.set $some (local.get 0))))
#[no_mangle]
pub static one: f32 = 1.0;
#[no_mangle]
pub static mut some: f32 = 0.0;
#[no_mangle]
pub extern "C" fn get_one() -> f32 {
one
}
#[no_mangle]
pub extern "C" fn get_some() -> f32 {
unsafe { some }
}
#[no_mangle]
pub extern "C" fn set_some(value: f32) {
unsafe {
some = value;
}
}
Inspecting the compiled module, I get:
wasmer inspect .\target\wasm32-wasi\debug\rust_wasm.wasm
Type: wasm
Size: 2.1 MB
Imports:
Functions:
Memories:
Tables:
Globals:
Exports:
Functions:
"get_one": [] -> [F32]
"get_some": [] -> [F32]
"set_some": [F32] -> []
Memories:
"memory": not shared (17 pages..)
Tables:
Globals:
"some": I32 (constant)
"one": I32 (constant)
Specifically, the Globals are wrong. They should both be F32 and some should be mutable. Why is this happening?

Related

Converting jni::sys::JNIEnv to JNINativeInterface defined in ffi

I am following up on Casting a borrowed reference with a lifetime to a raw pointer in Rust, which solved the wrong problem.
Please consider the following code:
extern crate jni;
extern crate ffi;
use jni::JNIEnv;
use jni::objects::JClass;
use jni::sys::{jint, jlong, jobject};
struct CameraAppEngine {
_env: *mut jni::sys::JNIEnv,
_width: i32,
_height: i32
}
impl CameraAppEngine {
pub fn new(_env: *mut jni::sys::JNIEnv, _width: i32, _height: i32) -> CameraAppEngine {
CameraAppEngine { _env, _width, _height }
}
pub fn create_camera_session(&mut self, surface: jobject) {
// error!
let window = ffi::ANativeWindow_fromSurface(self._env, surface);
}
}
fn app_engine_create(env: &JNIEnv, width: i32, height: i32) -> *mut CameraAppEngine {
let engine = CameraAppEngine::new(env.get_native_interface(), width, height);
Box::into_raw(Box::new(engine))
}
#[no_mangle]
pub extern "C" fn Java_io_waweb_cartoonifyit_MainActivity_createCamera(env: JNIEnv<'static>, _: JClass, width:jint, height:jint) -> jlong {
app_engine_create(&env, width, height) as jlong
}
#[no_mangle]
pub extern "C" fn Java_io_waweb_cartoonifyit_MainActivity_onPreviewSurfaceCreated(_: JNIEnv, _: JClass, engine_ptr:jlong, surface:jobject) {
let mut app = unsafe { Box::from_raw(engine_ptr as *mut CameraAppEngine) };
app.create_camera_session(surface);
}
And in the ffi crate we have:
extern "C" {
pub fn ANativeWindow_fromSurface(env: *mut JNIEnv, surface: jobject) -> *mut ANativeWindow;
}
This results in:
error[E0308]: mismatched types
--> native_app/src/lib.rs:24:53
|
| let window = ffi::ANativeWindow_fromSurface(self._env, surface);
| ^^^^^^^^^ expected struct `ffi::JNINativeInterface`, found struct `jni::sys::JNINativeInterface_`
|
= note: expected raw pointer `*mut *const ffi::JNINativeInterface`
found raw pointer `*mut *const jni::sys::JNINativeInterface_`
error[E0308]: mismatched types
--> native_app/src/lib.rs:24:64
|
| let window = ffi::ANativeWindow_fromSurface(self._env, surface);
| ^^^^^^^ expected enum `std::ffi::c_void`, found enum `jni::sys::_jobject`
|
= note: expected raw pointer `*mut std::ffi::c_void`
found raw pointer `*mut jni::sys::_jobject`
The problem is that the JNIEnv type expected by ANativeWindow_fromSurface is actually unrelated to jni::sys::JNIEnv entirely.
It's defined in ffi like so:
pub type JNIEnv = *const JNINativeInterface;
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct JNINativeInterface {
pub reserved0: *mut ::std::os::raw::c_void,
pub reserved1: *mut ::std::os::raw::c_void,
pub reserved2: *mut ::std::os::raw::c_void,
pub reserved3: *mut ::std::os::raw::c_void,
pub GetVersion: ::std::option::Option<unsafe extern "C" fn(arg1: *mut JNIEnv) -> jint>,
pub DefineClass: ::std::option::Option<
unsafe extern "C" fn(
arg1: *mut JNIEnv,
arg2: *const ::std::os::raw::c_char,
arg3: jobject,
arg4: *const jbyte,
arg5: jsize,
) -> jclass,
>,
pub FindClass: ::std::option::Option<
unsafe extern "C" fn(arg1: *mut JNIEnv, arg2: *const ::std::os::raw::c_char) -> jclass,
>,
pub FromReflectedMethod:
::std::option::Option<unsafe extern "C" fn(arg1: *mut JNIEnv, arg2: jobject) -> jmethodID>,
pub FromReflectedField:
::std::option::Option<unsafe extern "C" fn(arg1: *mut JNIEnv, arg2: jobject) -> jfieldID>,
pub ToReflectedMethod: ::std::option::Option<
unsafe extern "C" fn(
arg1: *mut JNIEnv,
arg2: jclass,
arg3: jmethodID,
arg4: jboolean,
) -> jobject,
>
// etc...
}
Given the glue code shown in the example, how do I get a valid reference to ffi::JNIEnv so that I may pass it to the ANativeWindow_fromSurface method. Bonus points if you give advice on converting jni::sys::jobject to *mut std::os::raw::c_void (lifetime concerns, null pointers, etc.).
Full source to ffi definitions
Here is the basic helloworld implementation I used to prove the concept in the accepted answer:
use std::ffi::{CString, CStr};
use std::os::raw::{c_char};
/// Expose the JNI interface for android below
#[cfg(target_os="android")]
#[allow(non_snake_case)]
pub mod android {
extern crate ffi;
use super::*;
use self::ffi::{JNIEnv, jclass, jstring, jlong};
#[derive(Debug)]
struct AppEngine {
greeting: *mut c_char
}
unsafe fn rust_greeting(app: *mut AppEngine) -> *mut c_char {
let app = Box::from_raw(app);
app.greeting
}
/// Constructs an AppEngine object.
fn rust_engine_create(to: *const c_char) -> *mut AppEngine {
let c_str = unsafe { CStr::from_ptr(to) };
let recipient = match c_str.to_str() {
Err(_) => "there",
Ok(string) => string,
};
let greeting = CString::new("Hello ".to_owned() + recipient).unwrap().into_raw();
let app = AppEngine{greeting: greeting};
Box::into_raw(Box::new(app))
}
/// Destroys an AppEngine object previously constructed using `rust_engine_create()`.
unsafe fn rust_engine_destroy(app: *mut AppEngine) {
drop(Box::from_raw(app))
}
#[no_mangle]
pub unsafe extern fn Java_io_waweb_cartoonifyit_MainActivity_greeting(env: &mut JNIEnv, _: jclass, app_ptr: jlong) -> jstring {
let app = app_ptr as *mut AppEngine;
let new_string = env.as_ref().unwrap().NewStringUTF.unwrap();
new_string(env, rust_greeting(app))
}
#[no_mangle]
pub unsafe extern fn Java_io_waweb_cartoonifyit_MainActivity_createNativeApp(env: &mut JNIEnv, _: jclass, java_pattern: jstring) -> jlong {
let get_string_chars = env.as_ref().unwrap().GetStringChars.unwrap();
let is_copy = 0 as *mut u8;
rust_engine_create(get_string_chars(env, java_pattern, is_copy) as *const c_char ) as jlong
}
#[no_mangle]
pub unsafe extern "C" fn Java_io_waweb_cartoonifyit_MainActivity_destroyNativeApp(_: JNIEnv, _: jclass, app_ptr: jlong) {
let app = app_ptr as *mut AppEngine;
rust_engine_destroy(app)
}
}
This is just a proof of concept. More care should be taken when casting raw pointers about. Also see notes about Box::leak in the accepted answer.
A JNIEnv is a pointer to a struct used for communication between Java and native code. This communication ABI is implemented by pretty much every JVM(and android). There are multiple versions of the aforementioned structs, which is what the GetVersion field is for.
It seems to me that you are using an external jni crate along with your own ffi crate generated from this wrapper. I would expect your ffi crate to be the most correct since it is using android headers, instead of the standard JVM headers which a most likely being used by the jni crate.
One last note Box::from_raw(engine_ptr as *mut CameraAppEngine), creates a box which will free the memory located at engine_ptr. This is likely not what you want. Consider using Box::leak to leak the created Box and avoid use after frees.

Returning array from Rust to FFI

I need to write a function that returns array of u16 integers in Rust. This function then should be used by FFI.
extern crate libc;
use libc::{uint16_t};
#[no_mangle]
pub extern fn ffi_test() -> *const uint16_t {
let test: [u16;4] = [1,2,3,4];
test.as_ptr()
}
Rust code compiles without errors. I used Ruby to test the ffi call:
# coding: utf-8
require 'ffi'
module MyMod
extend FFI::Library
ffi_lib 'my_ffi_test_lib'
attach_function :ffi_test, [], :pointer
end
a_ptr = MyMod.ffi_test
size = 4
result_array = a_ptr.read_array_of_uint16(size)
p result_array
But the results are totally wrong (expected: [1, 2, 3, 4]):
$ ruby ffi_test.rb
[57871, 25191, 32767, 0]
As if I am reading totally diffirent memory addr. I assume maybe that I should not use #as_ptr() on Rust array?
EDIT
As per recommendation of #FrenchBoiethios I tried to box the array:
extern crate libc;
use libc::{uint16_t};
#[no_mangle]
pub extern fn ffi_test() -> *mut uint16_t {
let test: [u16;4] = [1,2,3,4];
let b = Box::new(test);
Box::into_raw(b)
}
This gives compile error:
note: expected type `std::boxed::Box<u16>`
found type `std::boxed::Box<[u16; 4]>`
Your array is on the stack, so there is a lifetime issue when you returns it as a pointer (returned pointer to a local variable). You must allocate it in the heap:
#[no_mangle]
pub extern "C" fn ffi_test() -> *mut u16 {
let mut test = vec![1, 2, 3, 4];
let ptr = test.as_mut_ptr();
std::mem::forget(test); // so that it is not destructed at the end of the scope
ptr
}
or
#[no_mangle]
pub extern "C" fn ffi_test() -> *mut u16 {
let test = Box::new([1u16, 2, 3, 4]); // type must be explicit here...
Box::into_raw(test) as *mut _ // ... because this cast can convert
// *mut [i32; 4] to *mut u16
}
I am trying to learn Rust ffi, those implementations are a frankenstein creation from different sources in internet. So take it with a grain of salt.
Currently I am with two approaches:
a) Remove the array from rust GC and return the point. User need to promise to call free later.
#[repr(C)]
pub struct V2 {
pub x: i32,
pub y: i32,
}
#[repr(C)]
struct Buffer {
len: i32,
data: *mut V2,
}
#[no_mangle]
extern "C" fn generate_data() -> Buffer {
let mut buf = vec![V2 { x: 1, y: 0 }, V2 { x: 2, y: 0}].into_boxed_slice();
let data = buf.as_mut_ptr();
let len = buf.len() as i32;
std::mem::forget(buf);
Buffer { len, data }
}
#[no_mangle]
extern "C" fn free_buf(buf: Buffer) {
let s = unsafe { std::slice::from_raw_parts_mut(buf.data, buf.len as usize) };
let s = s.as_mut_ptr();
unsafe {
Box::from_raw(s);
}
}
b) Send the array through FFI callback function. User need to promise to not keep references, but dont need to call free.
#[no_mangle]
pub extern "C" fn context_get_byte_responses(callback: extern "stdcall" fn (*mut u8, i32)) -> bool {
let bytes: Vec<u8> = vec![];
callback(bytes.as_mut_ptr(), bytes.len() as i32);
true
}

Passing references as a parameter indirectly via global variable [duplicate]

I'm trying to write a Rusty wrapper for a piece of C API. There is one C construct I struggle with:
typedef bool (*listener_t) (int, int);
bool do_it(int x1, int y1, int x2, int y2, listener_t listener)
The function does its job for a range of numbers unless the listener returns false. In that case it aborts computation. I want to have a Rust wrapper like this:
fn do_with_callback<F>(start: (i32, i32), end: (i32, i32), callback: F)
where F: Fn(i32, i32) -> bool
rust-bindgen created this for me, slightly edited for clarity:
pub type listener_t = Option<extern "C" fn(x: c_int, y: c_int) -> c_bool>;
pub fn TCOD_line(xFrom: c_int, yFrom: c_int,
xTo: c_int, yTo: c_int,
listener: listener_t) -> c_bool;
How should I convert a closure or a trait reference to a C-style callback in my do_with functions:
pub fn do_with_callback<F>(start: (i32, i32), end: (i32, i32), callback: F) -> Self
where F: Fn(i32, i32) -> bool
{
let wrapper = ???;
unsafe {
ffi::do_it(start.0, start.1, end.0, end.1, Some(wrapper))
};
}
You cannot do it unless the C API allows passing a user-provided callback parameter. If it does not, you can only use static functions.
The reason is that closures are not "just" functions. As their name implies, closures "close over" variables from their lexical scope. Each closure has an associated piece of data which holds either values of captured variables (if the move keyword is used) or references to them. This data can be thought of as some unnamed, anonymous struct.
The compiler automatically adds an implementation of the corresponding Fn* traits for these anonymous structs. As you can see, methods on these traits accept self in addition to the closure arguments. In this context, self is the struct on which the trait is implemented. This means that each function which corresponds to a closure also has an additional parameter which contains the closure environment.
If your C API only allows you to pass functions without any user-defined parameters, you cannot write a wrapper which would allow you to use closures. I guess it may be possible to write some global holder for the closures environment, but I doubt it would be easy and safe.
If your C API does allow passing a user-defined argument, then it is possible to do what you want with trait objects:
extern crate libc;
use std::mem;
use libc::{c_int, c_void};
extern "C" {
fn do_something(f: Option<extern "C" fn(x: c_int, arg: *mut c_void) -> c_int>, arg: *mut c_void) -> c_int;
}
extern "C" fn do_something_handler(x: c_int, arg: *mut c_void) -> c_int {
let closure: &mut &mut dyn FnMut(i32) -> bool = unsafe { mem::transmute(arg) };
closure(x as i32) as c_int
}
pub fn do_with_callback<F>(x: i32, mut callback: F) -> bool
where F: FnMut(i32) -> bool
{
// reason for double indirection is described below
let mut cb: &mut dyn FnMut(i32) -> bool = &mut callback;
let cb = &mut cb;
unsafe { do_something(Some(do_something_handler), cb as *mut _ as *mut c_void) > 0 }
}
This will only work if do_something does not store the pointer to the callback somewhere. If it does, you need to use a Box<Fn(..) -> ..> trait object and leak it after you pass it to the function. Then, if possible, it should be obtained back from your C library and disposed of. It could look like this:
extern crate libc;
use std::mem;
use libc::{c_int, c_void};
extern "C" {
fn set_handler(f: Option<extern "C" fn(x: c_int, arg: *mut c_void) -> c_int>, arg: *mut c_void);
fn invoke_handler(x: c_int) -> c_int;
fn unset_handler() -> *mut c_void;
}
extern "C" fn do_something_handler(x: c_int, arg: *mut c_void) -> c_int {
let closure: &mut Box<dyn FnMut(i32) -> bool> = unsafe { mem::transmute(arg) };
closure(x as i32) as c_int
}
pub fn set_callback<F>(callback: F)
where F: FnMut(i32) -> bool,
F: 'static
{
let cb: Box<Box<dyn FnMut(i32) -> bool>> = Box::new(Box::new(callback));
unsafe {
set_handler(Some(do_something_handler), Box::into_raw(cb) as *mut _);
}
}
pub fn invoke_callback(x: i32) -> bool {
unsafe { invoke_handler(x as c_int) > 0 }
}
pub fn unset_callback() {
let ptr = unsafe { unset_handler() };
// drop the callback
let _: Box<Box<dyn FnMut(i32) -> bool>> = unsafe { Box::from_raw(ptr as *mut _) };
}
fn main() {
let mut y = 0;
set_callback(move |x| {
y += 1;
x > y
});
println!("First: {}", invoke_callback(2));
println!("Second: {}", invoke_callback(2));
unset_callback();
}
Double indirection (i.e. Box<Box<...>>) is necessary because Box<Fn(..) -> ..> is a trait object and therefore a fat pointer, incompatible with *mut c_void because of different size.
The first snippet from Vladimir Matveev no longer works as written. The size of &mut FnMut(i32) -> bool and *mut c_void is different and such casts lead to a crash. Corrected example (playpen):
extern crate libc;
use std::mem::*;
use libc::c_void;
pub fn run<F>(mut callback: F) -> bool
where F: FnMut(i32) -> bool
{
let mut cb: &mut FnMut(i32) -> bool = &mut callback;
println!("sizeof(cb/*-ptr): {}/{}",
size_of::<*mut FnMut(i32) -> bool>(),
size_of::<*mut c_void>());
let ctx = &mut cb as *mut &mut FnMut(i32) -> bool as *mut c_void;
println!("ctx: {:?}", ctx);
//----------------------------------------------------------
// Convert backward
let cb2: *mut *mut FnMut(i32) -> bool = unsafe { transmute(ctx) };
println!("cb2: {:?}", cb2);
// this is more useful, but can't be printed, because not implement Debug
let closure: &mut &mut FnMut(i32) -> bool = unsafe { transmute(ctx) };
closure(0xDEAD)
}
fn main() {
println!("answer: {}",
run(|x| {
println!("What can change nature of a man?");
x > 42
}));
}
In C, a function pointer does not have associated context, which is why usually a C callback function usually carry an extra void* argument pass the context...
typedef bool (*listener_t)(int, int, void* user_data);
bool do_it(void* user_data, int x1, int y1, int x2, int y2, listener_t listener)
... or have an API to let to store the user data...
void api_set_user_data(void* user_data); // <-- caller set the context
void* api_get_user_data(); // <-- callback use this to retrieve context.
If the library you want to wrap does not provide any of the above, you will need to pass the context via other channels, e.g. via a global variable, though that context will be shared across the whole process:
lazy_static! {
static ref REAL_CALLBACK: Mutex<Option<Box<FnMut(c_int, c_int) -> bool + Send>>> = Default::default();
}
extern "C" fn callback(x: c_int, y: c_int) -> bool {
if let Some(ref mut real_callback) = *REAL_CALLBACK.lock().unwrap() {
real_callback(x, y)
} else {
panic!("<handle error here>");
}
}
fn main() {
*REAL_CALLBACK.lock().unwrap() = Some(Box::new(move |x, y| {
println!("...");
true
}));
unsafe {
do_it(callback);
}
}
It is also possible to create a trampoline function to stick the context directly in the function, but it is extremely difficult and unsafe.
Answer manually migrated from https://stackoverflow.com/a/42597209/224671

Is it possible for an extern "C" function to have access to a Rust closure? [duplicate]

I'm trying to write a Rusty wrapper for a piece of C API. There is one C construct I struggle with:
typedef bool (*listener_t) (int, int);
bool do_it(int x1, int y1, int x2, int y2, listener_t listener)
The function does its job for a range of numbers unless the listener returns false. In that case it aborts computation. I want to have a Rust wrapper like this:
fn do_with_callback<F>(start: (i32, i32), end: (i32, i32), callback: F)
where F: Fn(i32, i32) -> bool
rust-bindgen created this for me, slightly edited for clarity:
pub type listener_t = Option<extern "C" fn(x: c_int, y: c_int) -> c_bool>;
pub fn TCOD_line(xFrom: c_int, yFrom: c_int,
xTo: c_int, yTo: c_int,
listener: listener_t) -> c_bool;
How should I convert a closure or a trait reference to a C-style callback in my do_with functions:
pub fn do_with_callback<F>(start: (i32, i32), end: (i32, i32), callback: F) -> Self
where F: Fn(i32, i32) -> bool
{
let wrapper = ???;
unsafe {
ffi::do_it(start.0, start.1, end.0, end.1, Some(wrapper))
};
}
You cannot do it unless the C API allows passing a user-provided callback parameter. If it does not, you can only use static functions.
The reason is that closures are not "just" functions. As their name implies, closures "close over" variables from their lexical scope. Each closure has an associated piece of data which holds either values of captured variables (if the move keyword is used) or references to them. This data can be thought of as some unnamed, anonymous struct.
The compiler automatically adds an implementation of the corresponding Fn* traits for these anonymous structs. As you can see, methods on these traits accept self in addition to the closure arguments. In this context, self is the struct on which the trait is implemented. This means that each function which corresponds to a closure also has an additional parameter which contains the closure environment.
If your C API only allows you to pass functions without any user-defined parameters, you cannot write a wrapper which would allow you to use closures. I guess it may be possible to write some global holder for the closures environment, but I doubt it would be easy and safe.
If your C API does allow passing a user-defined argument, then it is possible to do what you want with trait objects:
extern crate libc;
use std::mem;
use libc::{c_int, c_void};
extern "C" {
fn do_something(f: Option<extern "C" fn(x: c_int, arg: *mut c_void) -> c_int>, arg: *mut c_void) -> c_int;
}
extern "C" fn do_something_handler(x: c_int, arg: *mut c_void) -> c_int {
let closure: &mut &mut dyn FnMut(i32) -> bool = unsafe { mem::transmute(arg) };
closure(x as i32) as c_int
}
pub fn do_with_callback<F>(x: i32, mut callback: F) -> bool
where F: FnMut(i32) -> bool
{
// reason for double indirection is described below
let mut cb: &mut dyn FnMut(i32) -> bool = &mut callback;
let cb = &mut cb;
unsafe { do_something(Some(do_something_handler), cb as *mut _ as *mut c_void) > 0 }
}
This will only work if do_something does not store the pointer to the callback somewhere. If it does, you need to use a Box<Fn(..) -> ..> trait object and leak it after you pass it to the function. Then, if possible, it should be obtained back from your C library and disposed of. It could look like this:
extern crate libc;
use std::mem;
use libc::{c_int, c_void};
extern "C" {
fn set_handler(f: Option<extern "C" fn(x: c_int, arg: *mut c_void) -> c_int>, arg: *mut c_void);
fn invoke_handler(x: c_int) -> c_int;
fn unset_handler() -> *mut c_void;
}
extern "C" fn do_something_handler(x: c_int, arg: *mut c_void) -> c_int {
let closure: &mut Box<dyn FnMut(i32) -> bool> = unsafe { mem::transmute(arg) };
closure(x as i32) as c_int
}
pub fn set_callback<F>(callback: F)
where F: FnMut(i32) -> bool,
F: 'static
{
let cb: Box<Box<dyn FnMut(i32) -> bool>> = Box::new(Box::new(callback));
unsafe {
set_handler(Some(do_something_handler), Box::into_raw(cb) as *mut _);
}
}
pub fn invoke_callback(x: i32) -> bool {
unsafe { invoke_handler(x as c_int) > 0 }
}
pub fn unset_callback() {
let ptr = unsafe { unset_handler() };
// drop the callback
let _: Box<Box<dyn FnMut(i32) -> bool>> = unsafe { Box::from_raw(ptr as *mut _) };
}
fn main() {
let mut y = 0;
set_callback(move |x| {
y += 1;
x > y
});
println!("First: {}", invoke_callback(2));
println!("Second: {}", invoke_callback(2));
unset_callback();
}
Double indirection (i.e. Box<Box<...>>) is necessary because Box<Fn(..) -> ..> is a trait object and therefore a fat pointer, incompatible with *mut c_void because of different size.
The first snippet from Vladimir Matveev no longer works as written. The size of &mut FnMut(i32) -> bool and *mut c_void is different and such casts lead to a crash. Corrected example (playpen):
extern crate libc;
use std::mem::*;
use libc::c_void;
pub fn run<F>(mut callback: F) -> bool
where F: FnMut(i32) -> bool
{
let mut cb: &mut FnMut(i32) -> bool = &mut callback;
println!("sizeof(cb/*-ptr): {}/{}",
size_of::<*mut FnMut(i32) -> bool>(),
size_of::<*mut c_void>());
let ctx = &mut cb as *mut &mut FnMut(i32) -> bool as *mut c_void;
println!("ctx: {:?}", ctx);
//----------------------------------------------------------
// Convert backward
let cb2: *mut *mut FnMut(i32) -> bool = unsafe { transmute(ctx) };
println!("cb2: {:?}", cb2);
// this is more useful, but can't be printed, because not implement Debug
let closure: &mut &mut FnMut(i32) -> bool = unsafe { transmute(ctx) };
closure(0xDEAD)
}
fn main() {
println!("answer: {}",
run(|x| {
println!("What can change nature of a man?");
x > 42
}));
}
In C, a function pointer does not have associated context, which is why usually a C callback function usually carry an extra void* argument pass the context...
typedef bool (*listener_t)(int, int, void* user_data);
bool do_it(void* user_data, int x1, int y1, int x2, int y2, listener_t listener)
... or have an API to let to store the user data...
void api_set_user_data(void* user_data); // <-- caller set the context
void* api_get_user_data(); // <-- callback use this to retrieve context.
If the library you want to wrap does not provide any of the above, you will need to pass the context via other channels, e.g. via a global variable, though that context will be shared across the whole process:
lazy_static! {
static ref REAL_CALLBACK: Mutex<Option<Box<FnMut(c_int, c_int) -> bool + Send>>> = Default::default();
}
extern "C" fn callback(x: c_int, y: c_int) -> bool {
if let Some(ref mut real_callback) = *REAL_CALLBACK.lock().unwrap() {
real_callback(x, y)
} else {
panic!("<handle error here>");
}
}
fn main() {
*REAL_CALLBACK.lock().unwrap() = Some(Box::new(move |x, y| {
println!("...");
true
}));
unsafe {
do_it(callback);
}
}
It is also possible to create a trampoline function to stick the context directly in the function, but it is extremely difficult and unsafe.
Answer manually migrated from https://stackoverflow.com/a/42597209/224671

How do I convert a Rust closure to a C-style callback?

I'm trying to write a Rusty wrapper for a piece of C API. There is one C construct I struggle with:
typedef bool (*listener_t) (int, int);
bool do_it(int x1, int y1, int x2, int y2, listener_t listener)
The function does its job for a range of numbers unless the listener returns false. In that case it aborts computation. I want to have a Rust wrapper like this:
fn do_with_callback<F>(start: (i32, i32), end: (i32, i32), callback: F)
where F: Fn(i32, i32) -> bool
rust-bindgen created this for me, slightly edited for clarity:
pub type listener_t = Option<extern "C" fn(x: c_int, y: c_int) -> c_bool>;
pub fn TCOD_line(xFrom: c_int, yFrom: c_int,
xTo: c_int, yTo: c_int,
listener: listener_t) -> c_bool;
How should I convert a closure or a trait reference to a C-style callback in my do_with functions:
pub fn do_with_callback<F>(start: (i32, i32), end: (i32, i32), callback: F) -> Self
where F: Fn(i32, i32) -> bool
{
let wrapper = ???;
unsafe {
ffi::do_it(start.0, start.1, end.0, end.1, Some(wrapper))
};
}
You cannot do it unless the C API allows passing a user-provided callback parameter. If it does not, you can only use static functions.
The reason is that closures are not "just" functions. As their name implies, closures "close over" variables from their lexical scope. Each closure has an associated piece of data which holds either values of captured variables (if the move keyword is used) or references to them. This data can be thought of as some unnamed, anonymous struct.
The compiler automatically adds an implementation of the corresponding Fn* traits for these anonymous structs. As you can see, methods on these traits accept self in addition to the closure arguments. In this context, self is the struct on which the trait is implemented. This means that each function which corresponds to a closure also has an additional parameter which contains the closure environment.
If your C API only allows you to pass functions without any user-defined parameters, you cannot write a wrapper which would allow you to use closures. I guess it may be possible to write some global holder for the closures environment, but I doubt it would be easy and safe.
If your C API does allow passing a user-defined argument, then it is possible to do what you want with trait objects:
extern crate libc;
use std::mem;
use libc::{c_int, c_void};
extern "C" {
fn do_something(f: Option<extern "C" fn(x: c_int, arg: *mut c_void) -> c_int>, arg: *mut c_void) -> c_int;
}
extern "C" fn do_something_handler(x: c_int, arg: *mut c_void) -> c_int {
let closure: &mut &mut dyn FnMut(i32) -> bool = unsafe { mem::transmute(arg) };
closure(x as i32) as c_int
}
pub fn do_with_callback<F>(x: i32, mut callback: F) -> bool
where F: FnMut(i32) -> bool
{
// reason for double indirection is described below
let mut cb: &mut dyn FnMut(i32) -> bool = &mut callback;
let cb = &mut cb;
unsafe { do_something(Some(do_something_handler), cb as *mut _ as *mut c_void) > 0 }
}
This will only work if do_something does not store the pointer to the callback somewhere. If it does, you need to use a Box<Fn(..) -> ..> trait object and leak it after you pass it to the function. Then, if possible, it should be obtained back from your C library and disposed of. It could look like this:
extern crate libc;
use std::mem;
use libc::{c_int, c_void};
extern "C" {
fn set_handler(f: Option<extern "C" fn(x: c_int, arg: *mut c_void) -> c_int>, arg: *mut c_void);
fn invoke_handler(x: c_int) -> c_int;
fn unset_handler() -> *mut c_void;
}
extern "C" fn do_something_handler(x: c_int, arg: *mut c_void) -> c_int {
let closure: &mut Box<dyn FnMut(i32) -> bool> = unsafe { mem::transmute(arg) };
closure(x as i32) as c_int
}
pub fn set_callback<F>(callback: F)
where F: FnMut(i32) -> bool,
F: 'static
{
let cb: Box<Box<dyn FnMut(i32) -> bool>> = Box::new(Box::new(callback));
unsafe {
set_handler(Some(do_something_handler), Box::into_raw(cb) as *mut _);
}
}
pub fn invoke_callback(x: i32) -> bool {
unsafe { invoke_handler(x as c_int) > 0 }
}
pub fn unset_callback() {
let ptr = unsafe { unset_handler() };
// drop the callback
let _: Box<Box<dyn FnMut(i32) -> bool>> = unsafe { Box::from_raw(ptr as *mut _) };
}
fn main() {
let mut y = 0;
set_callback(move |x| {
y += 1;
x > y
});
println!("First: {}", invoke_callback(2));
println!("Second: {}", invoke_callback(2));
unset_callback();
}
Double indirection (i.e. Box<Box<...>>) is necessary because Box<Fn(..) -> ..> is a trait object and therefore a fat pointer, incompatible with *mut c_void because of different size.
The first snippet from Vladimir Matveev no longer works as written. The size of &mut FnMut(i32) -> bool and *mut c_void is different and such casts lead to a crash. Corrected example (playpen):
extern crate libc;
use std::mem::*;
use libc::c_void;
pub fn run<F>(mut callback: F) -> bool
where F: FnMut(i32) -> bool
{
let mut cb: &mut FnMut(i32) -> bool = &mut callback;
println!("sizeof(cb/*-ptr): {}/{}",
size_of::<*mut FnMut(i32) -> bool>(),
size_of::<*mut c_void>());
let ctx = &mut cb as *mut &mut FnMut(i32) -> bool as *mut c_void;
println!("ctx: {:?}", ctx);
//----------------------------------------------------------
// Convert backward
let cb2: *mut *mut FnMut(i32) -> bool = unsafe { transmute(ctx) };
println!("cb2: {:?}", cb2);
// this is more useful, but can't be printed, because not implement Debug
let closure: &mut &mut FnMut(i32) -> bool = unsafe { transmute(ctx) };
closure(0xDEAD)
}
fn main() {
println!("answer: {}",
run(|x| {
println!("What can change nature of a man?");
x > 42
}));
}
In C, a function pointer does not have associated context, which is why usually a C callback function usually carry an extra void* argument pass the context...
typedef bool (*listener_t)(int, int, void* user_data);
bool do_it(void* user_data, int x1, int y1, int x2, int y2, listener_t listener)
... or have an API to let to store the user data...
void api_set_user_data(void* user_data); // <-- caller set the context
void* api_get_user_data(); // <-- callback use this to retrieve context.
If the library you want to wrap does not provide any of the above, you will need to pass the context via other channels, e.g. via a global variable, though that context will be shared across the whole process:
lazy_static! {
static ref REAL_CALLBACK: Mutex<Option<Box<FnMut(c_int, c_int) -> bool + Send>>> = Default::default();
}
extern "C" fn callback(x: c_int, y: c_int) -> bool {
if let Some(ref mut real_callback) = *REAL_CALLBACK.lock().unwrap() {
real_callback(x, y)
} else {
panic!("<handle error here>");
}
}
fn main() {
*REAL_CALLBACK.lock().unwrap() = Some(Box::new(move |x, y| {
println!("...");
true
}));
unsafe {
do_it(callback);
}
}
It is also possible to create a trampoline function to stick the context directly in the function, but it is extremely difficult and unsafe.
Answer manually migrated from https://stackoverflow.com/a/42597209/224671

Resources