Type-safe signals by Bromeon · Pull Request #1000 · godot-rust/gdext (original) (raw)
impl<R: ToGodot + FromGodot + Debug, Ps: InParams + OutParams> VarcallSignatureTuple for SignatureThing<R, Ps> { const PARAM_COUNT: usize = ::PARAM_LEN;
fn param_property_info(index: usize, param_name: &str) -> PropertyInfo {
todo!()
}
fn param_info(index: usize, param_name: &str) -> Option<MethodParamOrReturnInfo> {
todo!()
}
fn return_info() -> Option<MethodParamOrReturnInfo> {
todo!()
}
unsafe fn in_varcall(
instance_ptr: godot_ffi::GDExtensionClassInstancePtr,
call_ctx: &CallContext,
args_ptr: *const godot_ffi::GDExtensionConstVariantPtr,
arg_count: i64,
ret: godot_ffi::GDExtensionVariantPtr,
err: *mut godot_ffi::GDExtensionCallError,
func: fn(godot_ffi::GDExtensionClassInstancePtr, Self::Params) -> Self::Ret,
) -> Result<(), CallError> {
//$crate::out!("in_varcall: {call_ctx}");
CallError::check_arg_count(
call_ctx,
arg_count as usize,
<Self::Params as Params>::PARAM_LEN,
)?;
#[cfg(feature = "trace")]
trace::push(true, false, &call_ctx);
let args = unsafe { <Self::Params as InParams>::varcall_args(args_ptr, call_ctx)? };
let rust_result = func(instance_ptr, args);
varcall_return::<R>(rust_result, ret, err);
Ok(())
}
unsafe fn out_class_varcall(
method_bind: ClassMethodBind,
// Separate parameters to reduce tokens in generated class API.
class_name: &'static str,
method_name: &'static str,
object_ptr: godot_ffi::GDExtensionObjectPtr,
maybe_instance_id: Option<InstanceId>, // if not static
args: Self::Params,
varargs: &[Variant],
) -> Result<Self::Ret, CallError> {
let call_ctx = CallContext::outbound(class_name, method_name);
//$crate::out!("out_class_varcall: {call_ctx}");
// Note: varcalls are not safe from failing, if they happen through an object pointer -> validity check necessary.
if let Some(instance_id) = maybe_instance_id {
crate::classes::ensure_object_alive(instance_id, object_ptr, &call_ctx);
}
let class_fn = sys::interface_fn!(object_method_bind_call);
let variant: Result<Variant, CallError> = args.with_variant_array(|explicit_args| {
let mut variant_ptrs = Vec::with_capacity(explicit_args.len() + varargs.len());
variant_ptrs.extend(explicit_args.iter().map(Variant::var_sys));
variant_ptrs.extend(varargs.iter().map(Variant::var_sys));
Variant::new_with_var_uninit_result(|return_ptr| {
let mut err = sys::default_call_error();
class_fn(
method_bind.0,
object_ptr,
variant_ptrs.as_ptr(),
variant_ptrs.len() as i64,
return_ptr,
std::ptr::addr_of_mut!(err),
);
sys::CallError::try_from_sys(err).map_err(|err| {
CallError::check_out_varcall(&call_ctx, err, &explicit_args, varargs)
})
})
});
variant.and_then(|v| {
v.try_to::<Self::Ret>()
.map_err(|e| CallError::failed_return_conversion::<Self::Ret>(&call_ctx, e))
})
}
#[cfg(since_api = "4.3")]
unsafe fn out_script_virtual_call(
// Separate parameters to reduce tokens in macro-generated API.
class_name: &'static str,
method_name: &'static str,
method_sname_ptr: godot_ffi::GDExtensionConstStringNamePtr,
object_ptr: godot_ffi::GDExtensionObjectPtr,
args: Self::Params,
) -> Self::Ret {
// Assumes that caller has previously checked existence of a virtual method.
let call_ctx = CallContext::outbound(class_name, method_name);
//$crate::out!("out_script_virtual_call: {call_ctx}");
let object_call_script_method = sys::interface_fn!(object_call_script_method);
let variant = args.with_variant_array(|explicit_args| {
let variant_ptrs = explicit_args
.iter()
.map(Variant::var_sys)
.collect::<Vec<_>>();
Variant::new_with_var_uninit(|return_ptr| {
let mut err = sys::default_call_error();
object_call_script_method(
object_ptr,
method_sname_ptr,
variant_ptrs.as_ptr(),
variant_ptrs.len() as i64,
return_ptr,
std::ptr::addr_of_mut!(err),
);
})
});
let result = <Self::Ret as FromGodot>::try_from_variant(&variant);
result.unwrap_or_else(|err| return_error::<Self::Ret>(&call_ctx, err))
}
unsafe fn out_utility_ptrcall_varargs(
utility_fn: UtilityFunctionBind,
function_name: &'static str,
args: Self::Params,
varargs: &[Variant],
) -> Self::Ret {
let call_ctx = CallContext::outbound("", function_name);
//$crate::out!("out_utility_ptrcall_varargs: {call_ctx}");
let result = args.with_ptr_array(|explicit_args| {
let mut type_ptrs = Vec::with_capacity(explicit_args.len() + varargs.len());
type_ptrs.extend(explicit_args);
type_ptrs.extend(varargs.iter().map(sys::GodotFfi::sys));
// Important: this calls from_sys_init_default().
new_from_ptrcall::<Self::Ret>(|return_ptr| {
utility_fn(return_ptr, type_ptrs.as_ptr(), type_ptrs.len() as i32);
})
});
result.unwrap_or_else(|err| return_error::<Self::Ret>(&call_ctx, err))
}
unsafe fn out_builtin_ptrcall_varargs(
builtin_fn: BuiltinMethodBind,
class_name: &'static str,
method_name: &'static str,
type_ptr: godot_ffi::GDExtensionTypePtr,
args: Self::Params,
varargs: &[Variant],
) -> Self::Ret {
let call_ctx = CallContext::outbound(class_name, method_name);
//$crate::out!("out_builtin_ptrcall_varargs: {call_ctx}");
let result = args.with_ptr_array(|explicit_args| {
let mut type_ptrs = Vec::with_capacity(explicit_args.len() + varargs.len());
type_ptrs.extend(explicit_args);
type_ptrs.extend(varargs.iter().map(sys::GodotFfi::sys));
// Important: this calls from_sys_init_default().
new_from_ptrcall::<Self::Ret>(|return_ptr| {
builtin_fn(
type_ptr,
type_ptrs.as_ptr(),
return_ptr,
type_ptrs.len() as i32,
);
})
});
result.unwrap_or_else(|err| return_error::<Self::Ret>(&call_ctx, err))
}
fn format_args(args: &Self::Params) -> String {
todo!()
}
}