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(
&inner,
[](void *user_data, const void *arg) {
auto *p = reinterpret_cast<const Pair*>(arg);
*p->first = std::apply(*reinterpret_cast<F *>(user_data), p->second);
[](void *user_data, const void *arg, void *ret) {
*reinterpret_cast<Ret*>(ret) = std::apply(*reinterpret_cast<F *>(user_data),
*reinterpret_cast<const Tuple*>(arg));
},
new F(std::move(binding)),
[](void *user_data) { delete reinterpret_cast<F *>(user_data); });
@ -39,14 +39,13 @@ struct Callback<Ret(Arg...)>
Ret call(const Arg &...arg) const
{
Ret r{};
Pair p = std::pair{ &r, Tuple{arg...} };
cbindgen_private::sixtyfps_callback_call(&inner, &p);
Tuple tuple{arg...};
cbindgen_private::sixtyfps_callback_call(&inner, &tuple, &r);
return r;
}
private:
using Tuple = std::tuple<Arg...>;
using Pair = std::pair<Ret *, Tuple>;
cbindgen_private::CallbackOpaque inner;
};
@ -65,7 +64,7 @@ struct Callback<void(Arg...)>
{
cbindgen_private::sixtyfps_callback_set_handler(
&inner,
[](void *user_data, const void *arg) {
[](void *user_data, const void *arg, void *) {
std::apply(*reinterpret_cast<F *>(user_data),
*reinterpret_cast<const Tuple*>(arg));
},
@ -76,7 +75,7 @@ struct Callback<void(Arg...)>
void call(const Arg &...arg) const
{
Tuple tuple{arg...};
cbindgen_private::sixtyfps_callback_call(&inner, &tuple);
cbindgen_private::sixtyfps_callback_call(&inner, &tuple, reinterpret_cast<void *>(0x1));
}
private:
@ -84,7 +83,11 @@ private:
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)]
pub struct Callback<Arg: ?Sized, Ret = ()> {
/// 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> {
@ -37,21 +37,20 @@ impl<Arg: ?Sized, Ret> Default for Callback<Arg, Ret> {
impl<Arg: ?Sized, Ret: Default> Callback<Arg, Ret> {
/// Call the callback with the given argument.
pub fn call(&self, a: &Arg) -> Ret {
let mut r = Ret::default();
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");
self.handler.set(Some(h));
r
} else {
Default::default()
}
r
}
/// Set an handler to be called when the callback is called
///
/// There can only be one single handler per callback.
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(
sig: *const CallbackOpaque,
arg: *const c_void,
ret: *mut 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.
@ -110,7 +114,7 @@ pub(crate) mod ffi {
#[no_mangle]
pub unsafe extern "C" fn sixtyfps_callback_set_handler(
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,
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 real_binding = move |arg: &()| {
binding(ud.user_data, arg as *const c_void);
};
sig.set_handler(real_binding);
sig.handler.set(Some(Box::new(move |a: &(), r: &mut ()| {
binding(ud.user_data, a as *const c_void, r as *mut c_void)
})));
}
/// Destroy callback

View file

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

View file

@ -80,12 +80,14 @@ pub fn sixtyfps_element(input: TokenStream) -> TokenStream {
let mut callback_field_names = Vec::new();
let mut callback_args = Vec::new();
let mut callback_rets = Vec::new();
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(_)) {
let name = field.ident.as_ref().unwrap();
callback_field_names.push(name);
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>)> {
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;
(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
}
// Try to match `Callback<Args>` on the syn tree and return Args if found
fn callback_arg(ty: &syn::Type) -> Option<&syn::Type> {
// 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, Option<&syn::Type>)> {
if let syn::Type::Path(syn::TypePath { path: syn::Path { segments, .. }, .. }) = ty {
if let Some(syn::PathSegment {
ident,
@ -163,14 +165,19 @@ fn callback_arg(ty: &syn::Type) -> Option<&syn::Type> {
syn::PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments { args, .. }),
}) = segments.first()
{
match args.first() {
Some(syn::GenericArgument::Type(property_type))
if ident.to_string() == "Callback" =>
{
return Some(property_type)
}
_ => {}
if ident != "Callback" {
return None;
}
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

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)]
enum ComponentInstance<'a, 'id> {
InstanceRef(InstanceRef<'a, 'id>),

View file

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