Handle native callbacks with return value

This commit is contained in:
Olivier Goffart 2021-01-26 17:01:54 +01:00
parent e0e3a1aaad
commit 2e23b5bbdd
6 changed files with 77 additions and 48 deletions

View file

@ -28,9 +28,9 @@ struct Callback<Ret(Arg...)>
{ {
cbindgen_private::sixtyfps_callback_set_handler( cbindgen_private::sixtyfps_callback_set_handler(
&inner, &inner,
[](void *user_data, const void *arg) { [](void *user_data, const void *arg, void *ret) {
auto *p = reinterpret_cast<const Pair*>(arg); *reinterpret_cast<Ret*>(ret) = std::apply(*reinterpret_cast<F *>(user_data),
*p->first = std::apply(*reinterpret_cast<F *>(user_data), p->second); *reinterpret_cast<const Tuple*>(arg));
}, },
new F(std::move(binding)), new F(std::move(binding)),
[](void *user_data) { delete reinterpret_cast<F *>(user_data); }); [](void *user_data) { delete reinterpret_cast<F *>(user_data); });
@ -39,14 +39,13 @@ struct Callback<Ret(Arg...)>
Ret call(const Arg &...arg) const Ret call(const Arg &...arg) const
{ {
Ret r{}; Ret r{};
Pair p = std::pair{ &r, Tuple{arg...} }; Tuple tuple{arg...};
cbindgen_private::sixtyfps_callback_call(&inner, &p); cbindgen_private::sixtyfps_callback_call(&inner, &tuple, &r);
return r; return r;
} }
private: private:
using Tuple = std::tuple<Arg...>; using Tuple = std::tuple<Arg...>;
using Pair = std::pair<Ret *, Tuple>;
cbindgen_private::CallbackOpaque inner; cbindgen_private::CallbackOpaque inner;
}; };
@ -65,7 +64,7 @@ struct Callback<void(Arg...)>
{ {
cbindgen_private::sixtyfps_callback_set_handler( cbindgen_private::sixtyfps_callback_set_handler(
&inner, &inner,
[](void *user_data, const void *arg) { [](void *user_data, const void *arg, void *) {
std::apply(*reinterpret_cast<F *>(user_data), std::apply(*reinterpret_cast<F *>(user_data),
*reinterpret_cast<const Tuple*>(arg)); *reinterpret_cast<const Tuple*>(arg));
}, },
@ -76,7 +75,7 @@ struct Callback<void(Arg...)>
void call(const Arg &...arg) const void call(const Arg &...arg) const
{ {
Tuple tuple{arg...}; Tuple tuple{arg...};
cbindgen_private::sixtyfps_callback_call(&inner, &tuple); cbindgen_private::sixtyfps_callback_call(&inner, &tuple, reinterpret_cast<void *>(0x1));
} }
private: private:
@ -84,7 +83,11 @@ private:
cbindgen_private::CallbackOpaque inner; cbindgen_private::CallbackOpaque inner;
}; };
template<typename A, typename R> struct CallbackSignatureHelper { using Result = R(A); };
template<typename R> struct CallbackSignatureHelper<void, R> { using Result = R(); };
template<typename A, typename R = void> using CallbackHelper =
Callback<typename CallbackSignatureHelper<A, R>::Result>;
} } // namespace sixtyfps

View file

@ -25,7 +25,7 @@ use core::cell::Cell;
#[repr(C)] #[repr(C)]
pub struct Callback<Arg: ?Sized, Ret = ()> { pub struct Callback<Arg: ?Sized, Ret = ()> {
/// FIXME: Box<dyn> is a fat object and we probaly want to put an erased type in there /// FIXME: Box<dyn> is a fat object and we probaly want to put an erased type in there
handler: Cell<Option<Box<dyn Fn(&Arg) -> Ret>>>, handler: Cell<Option<Box<dyn Fn(&Arg, &mut Ret)>>>,
} }
impl<Arg: ?Sized, Ret> Default for Callback<Arg, Ret> { impl<Arg: ?Sized, Ret> Default for Callback<Arg, Ret> {
@ -37,21 +37,20 @@ impl<Arg: ?Sized, Ret> Default for Callback<Arg, Ret> {
impl<Arg: ?Sized, Ret: Default> Callback<Arg, Ret> { impl<Arg: ?Sized, Ret: Default> Callback<Arg, Ret> {
/// Call the callback with the given argument. /// Call the callback with the given argument.
pub fn call(&self, a: &Arg) -> Ret { pub fn call(&self, a: &Arg) -> Ret {
let mut r = Ret::default();
if let Some(h) = self.handler.take() { if let Some(h) = self.handler.take() {
let r = h(a); h(a, &mut r);
assert!(self.handler.take().is_none(), "Callback Handler set while callted"); assert!(self.handler.take().is_none(), "Callback Handler set while callted");
self.handler.set(Some(h)); self.handler.set(Some(h));
r
} else {
Default::default()
} }
r
} }
/// Set an handler to be called when the callback is called /// Set an handler to be called when the callback is called
/// ///
/// There can only be one single handler per callback. /// There can only be one single handler per callback.
pub fn set_handler(&self, f: impl Fn(&Arg) -> Ret + 'static) { pub fn set_handler(&self, f: impl Fn(&Arg) -> Ret + 'static) {
self.handler.set(Some(Box::new(f))); self.handler.set(Some(Box::new(move |a: &Arg, r: &mut Ret| *r = f(a))));
} }
} }
@ -99,9 +98,14 @@ pub(crate) mod ffi {
pub unsafe extern "C" fn sixtyfps_callback_call( pub unsafe extern "C" fn sixtyfps_callback_call(
sig: *const CallbackOpaque, sig: *const CallbackOpaque,
arg: *const c_void, arg: *const c_void,
ret: *mut c_void,
) { ) {
let sig = &*(sig as *const Callback<c_void>); let sig = &*(sig as *const Callback<c_void>);
sig.call(&*arg); if let Some(h) = sig.handler.take() {
h(&*arg, &mut *ret);
assert!(sig.handler.take().is_none(), "Callback Handler set while callted");
sig.handler.set(Some(h));
}
} }
/// Set callback handler. /// Set callback handler.
@ -110,7 +114,7 @@ pub(crate) mod ffi {
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn sixtyfps_callback_set_handler( pub unsafe extern "C" fn sixtyfps_callback_set_handler(
sig: *const CallbackOpaque, sig: *const CallbackOpaque,
binding: extern "C" fn(user_data: *mut c_void, arg: *const c_void), binding: extern "C" fn(user_data: *mut c_void, arg: *const c_void, ret: *mut c_void),
user_data: *mut c_void, user_data: *mut c_void,
drop_user_data: Option<extern "C" fn(*mut c_void)>, drop_user_data: Option<extern "C" fn(*mut c_void)>,
) { ) {
@ -129,11 +133,9 @@ pub(crate) mod ffi {
} }
} }
let ud = UserData { user_data, drop_user_data }; let ud = UserData { user_data, drop_user_data };
sig.handler.set(Some(Box::new(move |a: &(), r: &mut ()| {
let real_binding = move |arg: &()| { binding(ud.user_data, a as *const c_void, r as *mut c_void)
binding(ud.user_data, arg as *const c_void); })));
};
sig.set_handler(real_binding);
} }
/// Destroy callback /// Destroy callback

View file

@ -23,6 +23,7 @@ macro_rules! declare_ValueType {
}; };
} }
declare_ValueType![ declare_ValueType![
(),
bool, bool,
u32, u32,
u64, u64,
@ -228,12 +229,14 @@ pub trait CallbackInfo<Item, Value> {
) -> Result<(), ()>; ) -> Result<(), ()>;
} }
impl<Item, Value: Default + 'static> CallbackInfo<Item, Value> impl<Item, Value: Default + 'static, Ret: Default> CallbackInfo<Item, Value>
for FieldOffset<Item, crate::Callback<()>> for FieldOffset<Item, crate::Callback<(), Ret>>
where
Value: TryInto<Ret>,
Ret: TryInto<Value>,
{ {
fn call(&self, item: Pin<&Item>, _args: &[Value]) -> Result<Value, ()> { fn call(&self, item: Pin<&Item>, _args: &[Value]) -> Result<Value, ()> {
self.apply_pin(item).call(&()); self.apply_pin(item).call(&()).try_into().map_err(|_| ())
Ok(Default::default())
} }
fn set_handler( fn set_handler(
@ -241,24 +244,23 @@ impl<Item, Value: Default + 'static> CallbackInfo<Item, Value>
item: Pin<&Item>, item: Pin<&Item>,
handler: Box<dyn Fn(&[Value]) -> Value>, handler: Box<dyn Fn(&[Value]) -> Value>,
) -> Result<(), ()> { ) -> Result<(), ()> {
self.apply_pin(item).set_handler(move |()| { self.apply_pin(item).set_handler(move |()| handler(&[]).try_into().ok().unwrap());
handler(&[]);
});
Ok(()) Ok(())
} }
} }
impl<Item, Value: Clone + Default + 'static, T: Clone> CallbackInfo<Item, Value> impl<Item, Value: Clone + Default + 'static, T: Clone, Ret: Default> CallbackInfo<Item, Value>
for FieldOffset<Item, crate::Callback<(T,)>> for FieldOffset<Item, crate::Callback<(T,), Ret>>
where where
Value: TryInto<T>, Value: TryInto<T>,
T: TryInto<Value>, T: TryInto<Value>,
Value: TryInto<Ret>,
Ret: TryInto<Value>,
{ {
fn call(&self, item: Pin<&Item>, args: &[Value]) -> Result<Value, ()> { fn call(&self, item: Pin<&Item>, args: &[Value]) -> Result<Value, ()> {
let value = args.first().ok_or(())?; let value = args.first().ok_or(())?;
let value = value.clone().try_into().map_err(|_| ())?; let value = value.clone().try_into().map_err(|_| ())?;
self.apply_pin(item).call(&(value,)); self.apply_pin(item).call(&(value,)).try_into().map_err(|_| ())
Ok(Value::default())
} }
fn set_handler( fn set_handler(
@ -268,8 +270,7 @@ where
) -> Result<(), ()> { ) -> Result<(), ()> {
self.apply_pin(item).set_handler(move |(val,)| { self.apply_pin(item).set_handler(move |(val,)| {
let val: Value = val.clone().try_into().ok().unwrap(); let val: Value = val.clone().try_into().ok().unwrap();
handler(&[val]); handler(&[val]).try_into().ok().unwrap()
Value::default();
}); });
Ok(()) Ok(())
} }

View file

@ -80,12 +80,14 @@ pub fn sixtyfps_element(input: TokenStream) -> TokenStream {
let mut callback_field_names = Vec::new(); let mut callback_field_names = Vec::new();
let mut callback_args = Vec::new(); let mut callback_args = Vec::new();
let mut callback_rets = Vec::new();
for field in fields { for field in fields {
if let Some(arg) = callback_arg(&field.ty) { if let Some((arg, ret)) = callback_arg(&field.ty) {
if matches!(field.vis, syn::Visibility::Public(_)) { if matches!(field.vis, syn::Visibility::Public(_)) {
let name = field.ident.as_ref().unwrap(); let name = field.ident.as_ref().unwrap();
callback_field_names.push(name); callback_field_names.push(name);
callback_args.push(arg); callback_args.push(arg);
callback_rets.push(ret);
} }
} }
} }
@ -114,7 +116,7 @@ pub fn sixtyfps_element(input: TokenStream) -> TokenStream {
} }
fn callbacks<Value: ValueType>() -> Vec<(&'static str, &'static dyn CallbackInfo<Self, Value>)> { fn callbacks<Value: ValueType>() -> Vec<(&'static str, &'static dyn CallbackInfo<Self, Value>)> {
vec![#( { vec![#( {
const O : const_field_offset::FieldOffset<#item_name, Callback<#callback_args>, const_field_offset::AllowPin> = const O : const_field_offset::FieldOffset<#item_name, Callback<#callback_args, #callback_rets>, const_field_offset::AllowPin> =
#item_name::FIELD_OFFSETS.#callback_field_names; #item_name::FIELD_OFFSETS.#callback_field_names;
(stringify!(#callback_field_names), &O as &'static dyn CallbackInfo<Self, Value>) (stringify!(#callback_field_names), &O as &'static dyn CallbackInfo<Self, Value>)
} ),*] } ),*]
@ -154,8 +156,8 @@ fn property_type(ty: &syn::Type) -> Option<&syn::Type> {
None None
} }
// Try to match `Callback<Args>` on the syn tree and return Args if found // Try to match `Callback<Args, Ret>` on the syn tree and return Args and Ret if found
fn callback_arg(ty: &syn::Type) -> Option<&syn::Type> { fn callback_arg(ty: &syn::Type) -> Option<(&syn::Type, Option<&syn::Type>)> {
if let syn::Type::Path(syn::TypePath { path: syn::Path { segments, .. }, .. }) = ty { if let syn::Type::Path(syn::TypePath { path: syn::Path { segments, .. }, .. }) = ty {
if let Some(syn::PathSegment { if let Some(syn::PathSegment {
ident, ident,
@ -163,14 +165,19 @@ fn callback_arg(ty: &syn::Type) -> Option<&syn::Type> {
syn::PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments { args, .. }), syn::PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments { args, .. }),
}) = segments.first() }) = segments.first()
{ {
match args.first() { if ident != "Callback" {
Some(syn::GenericArgument::Type(property_type)) return None;
if ident.to_string() == "Callback" =>
{
return Some(property_type)
}
_ => {}
} }
let mut it = args.iter();
let first = match it.next() {
Some(syn::GenericArgument::Type(ty)) => ty,
_ => return None,
};
let sec = match it.next() {
Some(syn::GenericArgument::Type(ty)) => Some(ty),
_ => None,
};
return Some((first, sec));
} }
} }
None None

View file

@ -264,6 +264,21 @@ impl TryInto<corelib::animations::Instant> for Value {
} }
} }
impl TryFrom<()> for Value {
type Error = ();
#[inline]
fn try_from(_: ()) -> Result<Self, ()> {
Ok(Value::Void)
}
}
impl TryInto<()> for Value {
type Error = ();
#[inline]
fn try_into(self) -> Result<(), ()> {
Ok(())
}
}
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
enum ComponentInstance<'a, 'id> { enum ComponentInstance<'a, 'id> {
InstanceRef(InstanceRef<'a, 'id>), InstanceRef(InstanceRef<'a, 'id>),

View file

@ -33,8 +33,9 @@ fn default_config() -> cbindgen::Config {
documentation: true, documentation: true,
export: cbindgen::ExportConfig { export: cbindgen::ExportConfig {
rename: [ rename: [
("VoidArg".into(), "void()".into()), ("Callback".into(), "CallbackHelper".into()),
("KeyEventArg".into(), "void(KeyEvent)".into()), ("VoidArg".into(), "void".into()),
("KeyEventArg".into(), "KeyEvent".into()),
] ]
.iter() .iter()
.cloned() .cloned()