use libc;
use td_clua::{self, lua_State};
use LuaPush;
use LuaRead;
use std::marker::PhantomData;
use std::mem;
use std::ptr;
macro_rules! impl_function {
($name:ident, $($p:ident),*) => (
pub fn $name<Z, R $(, $p)*>(f: Z) -> Function<Z, ($($p,)*), R> where Z: FnMut($($p),*) -> R {
Function {
function: f,
marker: PhantomData,
}
}
)
}
impl_function!(function0,);
impl_function!(function1, A);
impl_function!(function2, A, B);
impl_function!(function3, A, B, C);
impl_function!(function4, A, B, C, D);
impl_function!(function5, A, B, C, D, E);
impl_function!(function6, A, B, C, D, E, F);
impl_function!(function7, A, B, C, D, E, F, G);
impl_function!(function8, A, B, C, D, E, F, G, H);
impl_function!(function9, A, B, C, D, E, F, G, H, I);
impl_function!(function10, A, B, C, D, E, F, G, H, I, J);
pub struct Function<F, P, R> {
function: F,
marker: PhantomData<(P, R)>,
}
pub trait FunctionExt<P> {
type Output;
fn call_mut(&mut self, params: P) -> Self::Output;
}
macro_rules! impl_function_ext {
() => (
impl<Z, R> FunctionExt<()> for Function<Z, (), R> where Z: FnMut() -> R {
type Output = R;
#[allow(non_snake_case)]
fn call_mut(&mut self, _: ()) -> Self::Output {
(self.function)()
}
}
impl<Z, R> LuaPush for Function<Z, (), R>
where Z: FnMut() -> R,
R: LuaPush + 'static
{
fn push_to_lua(self, lua: *mut lua_State) -> i32 {
unsafe {
let lua_data = td_clua::lua_newuserdata(lua, mem::size_of::<Z>() as libc::size_t);
let lua_data: *mut Z = mem::transmute(lua_data);
ptr::write(lua_data, self.function);
let wrapper: extern fn(*mut td_clua::lua_State) -> libc::c_int = wrapper::<Self, _, R>;
td_clua::lua_pushcclosure(lua, wrapper, 1);
1
}
}
}
);
($($p:ident),+) => (
impl<Z, R $(,$p)*> FunctionExt<($($p,)*)> for Function<Z, ($($p,)*), R> where Z: FnMut($($p),*) -> R {
type Output = R;
#[allow(non_snake_case)]
fn call_mut(&mut self, params: ($($p,)*)) -> Self::Output {
let ($($p,)*) = params;
(self.function)($($p),*)
}
}
impl<Z, R $(,$p: 'static)+> LuaPush for Function<Z, ($($p,)*), R>
where Z: FnMut($($p),*) -> R,
($($p,)*): LuaRead,
R: LuaPush + 'static
{
fn push_to_lua(self, lua: *mut lua_State) -> i32 {
unsafe {
let lua_data = td_clua::lua_newuserdata(lua, mem::size_of::<Z>() as libc::size_t);
let lua_data: *mut Z = mem::transmute(lua_data);
ptr::write(lua_data, self.function);
let wrapper: extern fn(*mut td_clua::lua_State) -> libc::c_int = wrapper::<Self, _, R>;
td_clua::lua_pushcclosure(lua, wrapper, 1);
1
}
}
}
)
}
impl_function_ext!();
impl_function_ext!(A);
impl_function_ext!(A, B);
impl_function_ext!(A, B, C);
impl_function_ext!(A, B, C, D);
impl_function_ext!(A, B, C, D, E);
impl_function_ext!(A, B, C, D, E, F);
impl_function_ext!(A, B, C, D, E, F, G);
impl_function_ext!(A, B, C, D, E, F, G, H);
impl_function_ext!(A, B, C, D, E, F, G, H, I);
impl_function_ext!(A, B, C, D, E, F, G, H, I, J);
extern "C" fn wrapper<T, P, R>(lua: *mut td_clua::lua_State) -> libc::c_int
where
T: FunctionExt<P, Output = R>,
P: LuaRead + 'static,
R: LuaPush,
{
let data_raw = unsafe { td_clua::lua_touserdata(lua, td_clua::lua_upvalueindex(1)) };
let data: &mut T = unsafe { mem::transmute(data_raw) };
let arguments_count = unsafe { td_clua::lua_gettop(lua) } as i32;
let args = match LuaRead::lua_read_at_position(lua, -arguments_count as libc::c_int) {
Some(a) => a,
_ => {
let err_msg = format!(
"wrong parameter types for callback function arguments_count \
is {}",
arguments_count
);
err_msg.push_to_lua(lua);
unsafe {
td_clua::lua_error(lua);
}
unreachable!()
}
};
let ret_value = data.call_mut(args);
let nb = ret_value.push_to_lua(lua);
nb as libc::c_int
}