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}