userdata_with_drop/main.rs
1// The MIT License (MIT)
2//
3// Copyright (c) 2016 J.C. Moyer
4//
5// Permission is hereby granted, free of charge, to any person obtaining a copy
6// of this software and associated documentation files (the "Software"), to deal
7// in the Software without restriction, including without limitation the rights
8// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9// copies of the Software, and to permit persons to whom the Software is
10// furnished to do so, subject to the following conditions:
11//
12// The above copyright notice and this permission notice shall be included in
13// all copies or substantial portions of the Software.
14//
15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21// THE SOFTWARE.
22
23extern crate lua;
24extern crate libc;
25
26use lua::ffi::lua_State;
27use lua::{State, Function};
28use libc::c_int;
29
30struct VecWrapper {
31 data: Vec<i64>
32}
33
34impl VecWrapper {
35 fn new() -> VecWrapper {
36 VecWrapper {
37 data: Vec::new()
38 }
39 }
40
41 /// Constructs a new VecWrapper and pushes it onto the Lua stack.
42 #[allow(non_snake_case)]
43 unsafe extern "C" fn lua_new(L: *mut lua_State) -> c_int {
44 let mut state = State::from_ptr(L);
45 // construct new userdata in lua space and initialize it
46 let v: *mut VecWrapper = state.new_userdata_typed();
47 std::ptr::write(v, VecWrapper::new());
48 // set the userdata's metatable so we can call methods on it
49 state.set_metatable_from_registry("VecWrapper");
50 // return the userdata on top of the stack
51 1
52 }
53
54 /// Returns the value in the underlying Vec at index `i`. If the index is out
55 /// of bounds, this function returns nil instead.
56 #[allow(non_snake_case)]
57 unsafe extern "C" fn lua_get(L: *mut lua_State) -> c_int {
58 let mut state = State::from_ptr(L);
59 let v = state.check_userdata(1, "VecWrapper") as *mut VecWrapper;
60 let i = state.check_integer(2) as usize;
61 // push integer if index is not out of bounds, otherwise nil
62 match (*v).data.get(i) {
63 Some(value) => state.push_integer(*value),
64 None => state.push_nil()
65 };
66 1
67 }
68
69 /// Pushes a value into the underlying Vec.
70 #[allow(non_snake_case)]
71 unsafe extern "C" fn lua_push(L: *mut lua_State) -> c_int {
72 let mut state = State::from_ptr(L);
73 let v = state.check_userdata(1, "VecWrapper") as *mut VecWrapper;
74 let i = state.check_integer(2);
75 (*v).data.push(i);
76 1
77 }
78
79 /// Returns the length of the underlying Vec.
80 #[allow(non_snake_case)]
81 unsafe extern "C" fn lua_len(L: *mut lua_State) -> c_int {
82 let mut state = State::from_ptr(L);
83 let v = state.check_userdata(1, "VecWrapper") as *mut VecWrapper;
84 state.push_integer((*v).data.len() as i64);
85 1
86 }
87
88 /// Garbage collects a VecWrapper.
89 #[allow(non_snake_case)]
90 unsafe extern "C" fn lua_gc(L: *mut lua_State) -> c_int {
91 let mut state = State::from_ptr(L);
92 let v = state.check_userdata(1, "VecWrapper") as *mut VecWrapper;
93 std::ptr::drop_in_place(v);
94 0
95 }
96}
97
98const VECWRAPPER_LIB: [(&'static str, Function); 4] = [
99 ("new", Some(VecWrapper::lua_new)),
100 ("get", Some(VecWrapper::lua_get)),
101 ("push", Some(VecWrapper::lua_push)),
102 ("len", Some(VecWrapper::lua_len))
103];
104
105fn main() {
106 let mut state = lua::State::new();
107
108 state.open_libs();
109
110 // make a VecWrapper table globally available to the lua state and register
111 // our functions there:
112 state.new_table();
113 state.set_fns(&VECWRAPPER_LIB, 0);
114 // copy reference to VecWrapper table so we can keep the original reference
115 // on the stack for later
116 state.push_value(-1);
117 state.set_global("VecWrapper");
118
119 // create a metatable for VecWrapper in the lua registry that refers to the
120 // global VecWrapper table:
121 state.new_metatable("VecWrapper");
122 // copy reference to VecWrapper table
123 state.push_value(-2);
124 // VecWrappermetatable.__index = VecWrapper
125 state.set_field(-2, "__index");
126 // VecWrappermetatable.__gc = lua_gc
127 state.push_fn(Some(VecWrapper::lua_gc));
128 state.set_field(-2, "__gc");
129
130 // pop metatable and VecWrapper table from the stack
131 state.pop(2);
132
133 // try it out:
134 state.do_string("local v = VecWrapper.new()
135 v:push(12)
136 v:push(34)
137 -- should print 2
138 print('length of vec is', v:len())
139 -- should print 12 34 nil
140 print(v:get(0), v:get(1), v:get(2))");
141}