Skip to content

Commit 64f1093

Browse files
committed
Add sentinel structs to lua_fns
Fixes #1
1 parent ae8ed58 commit 64f1093

File tree

5 files changed

+129
-3
lines changed

5 files changed

+129
-3
lines changed

src/lib.rs

+29-2
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,43 @@ pub use libc::c_int;
1212
/// This macro is used to wrap a rust function in an `extern "C"` trampoline
1313
/// to automatically pass a [`State`](state/struct.State.html) struct as the first
1414
/// argument instead of a `lua_State` raw pointer
15+
///
16+
/// # Examples
17+
///
18+
/// ```
19+
/// #[macro_use] extern crate luajit;
20+
///
21+
/// use luajit::{State, c_int, ThreadStatus};
22+
///
23+
/// fn return_42(state: &mut State) -> c_int {
24+
/// state.push(42);
25+
///
26+
/// 1
27+
/// }
28+
///
29+
/// fn main() {
30+
/// let mut state = State::new();
31+
/// state.open_libs();
32+
///
33+
/// state.push(lua_fn!(return_42));
34+
/// state.set_global("return_42");
35+
/// let status = state.do_string("if return_42() ~= 42 then error() end");
36+
/// assert_eq!(status, ThreadStatus::Ok);
37+
///
38+
/// // Equivalent
39+
/// state.register("return_42", lua_fn!(return_42).unwrap());
40+
/// }
41+
/// ```
1542
#[macro_export]
1643
macro_rules! lua_fn {
1744
($method:path) => {
1845
{
1946
#[allow(unused)]
2047
unsafe extern "C" fn trampoline(l: *mut $crate::ffi::lua_State) -> $crate::c_int {
21-
$method(&mut State::from_ptr(l))
48+
$method(&mut $crate::State::from_ptr(l))
2249
};
2350

24-
Some(trampoline)
51+
Some(trampoline as $crate::LuaFunction)
2552
}
2653
}
2754
}

src/state.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -430,7 +430,14 @@ impl State {
430430
/// Registers all functions in `fns` on the global table `name`. If name
431431
/// is `None`, all functions are instead registered on the value on the top
432432
/// of the stack.
433-
pub fn register_fns(&mut self, name: Option<&str>, fns: Vec<luaL_Reg>) {
433+
pub fn register_fns(&mut self, name: Option<&str>, mut fns: Vec<luaL_Reg>) {
434+
// Add a sentinel struct, even if one already exists adding a second
435+
// shouldn't break anything and incur minimal overhead
436+
fns.push(luaL_Reg {
437+
name: ptr::null(),
438+
func: None
439+
});
440+
434441
match name {
435442
Some(s) => unsafe {
436443
luaL_register(self.state, CString::new(s).unwrap().as_ptr(), fns.as_ptr());

src/types.rs

+14
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,20 @@ impl LuaValue for LuaFunction {
9797
}
9898
}
9999

100+
impl LuaValue for Option<LuaFunction> {
101+
fn push_val(self, l: *mut ffi::lua_State) {
102+
if let Some(func) = self {
103+
unsafe {
104+
ffi::lua_pushcfunction(l, Some(func));
105+
}
106+
} else {
107+
unsafe {
108+
ffi::lua_pushnil(l);
109+
}
110+
}
111+
}
112+
}
113+
100114
impl <T> LuaValue for T where T: LuaObject {
101115
fn push_val(self, l: *mut ffi::lua_State) {
102116
let mut state = State::from_ptr(l);

tests/function_macros.rs

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#[macro_use] extern crate luajit;
2+
3+
use luajit::{State, c_int, ThreadStatus};
4+
5+
fn return_42(state: &mut State) -> c_int {
6+
state.push(42);
7+
8+
1
9+
}
10+
11+
#[test]
12+
fn test_lua_fn() {
13+
let mut state = State::new();
14+
state.open_libs();
15+
16+
state.register("return_42", lua_fn!(return_42).unwrap());
17+
let status = state.do_string("if return_42() ~= 42 then error() end");
18+
assert_eq!(status, ThreadStatus::Ok);
19+
}
20+
21+
#[test]
22+
fn test_push_lua_function() {
23+
let mut state = State::new();
24+
state.open_libs();
25+
26+
state.push(lua_fn!(return_42));
27+
state.set_global("return_42");
28+
let status = state.do_string("if return_42() ~= 42 then error() end");
29+
assert_eq!(status, ThreadStatus::Ok);
30+
}

tests/structs.rs

+48
Original file line numberDiff line numberDiff line change
@@ -89,4 +89,52 @@ pub fn test_new_struct() {
8989
if foo:add() ~= 6 then error() end"
9090
);
9191
assert_eq!(res, ThreadStatus::Ok);
92+
}
93+
94+
struct B {
95+
pub val: i32
96+
}
97+
98+
impl LuaObject for B {
99+
fn name() -> *const i8 {
100+
c_str!("B")
101+
}
102+
103+
fn lua_fns() -> Vec<ffi::luaL_Reg> {
104+
vec![
105+
lua_method!("add", B, B::add),
106+
ffi::luaL_Reg {
107+
name: std::ptr::null(),
108+
func: None
109+
}
110+
]
111+
}
112+
}
113+
114+
impl B {
115+
fn add(&mut self, state: &mut State) -> c_int {
116+
let x = state.to_int(2).unwrap();
117+
self.val += x;
118+
state.push(self.val);
119+
120+
1
121+
}
122+
}
123+
124+
#[test]
125+
pub fn test_double_sentinel() {
126+
let mut state = State::new();
127+
state.open_libs();
128+
129+
state.push(B {
130+
val: 1,
131+
});
132+
133+
state.set_global("test");
134+
135+
let res = state.do_string("test:add(4)");
136+
assert_eq!(res, ThreadStatus::Ok);
137+
138+
let res = state.do_string("if test:add(4) ~= 9 then error() end");
139+
assert_eq!(res, ThreadStatus::Ok);
92140
}

0 commit comments

Comments
 (0)