aboutsummaryrefslogtreecommitdiff
path: root/src/FbTk/Luamm.hh
diff options
context:
space:
mode:
Diffstat (limited to 'src/FbTk/Luamm.hh')
-rw-r--r--src/FbTk/Luamm.hh341
1 files changed, 341 insertions, 0 deletions
diff --git a/src/FbTk/Luamm.hh b/src/FbTk/Luamm.hh
new file mode 100644
index 0000000..1df5825
--- /dev/null
+++ b/src/FbTk/Luamm.hh
@@ -0,0 +1,341 @@
1// luamm: C++ binding for lua
2// Copyright (C) 2010 - 2011 Pavel Labath
3//
4// Permission is hereby granted, free of charge, to any person obtaining a
5// copy of this software and associated documentation files (the "Software"),
6// to deal in the Software without restriction, including without limitation
7// the rights to use, copy, modify, merge, publish, distribute, sublicense,
8// and/or sell copies of the Software, and to permit persons to whom the
9// Software is furnished to do so, subject to the following conditions:
10//
11// The above copyright notice and this permission notice shall be included in
12// all copies or substantial portions of the Software.
13//
14// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20// DEALINGS IN THE SOFTWARE.
21
22#ifndef FBTK_LUAMM_HH
23#define FBTK_LUAMM_HH
24
25#include <assert.h>
26#include <memory>
27#include <stdexcept>
28
29#include <lua.h>
30
31namespace lua {
32 class state;
33
34 typedef lua_Integer integer;
35 typedef lua_Number number;
36 typedef std::function<int(state *)> cpp_function;
37
38 enum {
39 ENVIRONINDEX = LUA_ENVIRONINDEX,
40 GLOBALSINDEX = LUA_GLOBALSINDEX,
41 REGISTRYINDEX = LUA_REGISTRYINDEX
42 };
43
44 enum {
45 GCSTOP = LUA_GCSTOP,
46 GCRESTART = LUA_GCRESTART,
47 GCCOLLECT = LUA_GCCOLLECT,
48 GCCOUNT = LUA_GCCOUNT,
49 GCCOUNTB = LUA_GCCOUNTB,
50 GCSTEP = LUA_GCSTEP,
51 GCSETPAUSE = LUA_GCSETPAUSE,
52 GCSETSTEPMUL = LUA_GCSETSTEPMUL
53 };
54
55 enum {
56 MULTRET = LUA_MULTRET
57 };
58
59 enum Type {
60 TBOOLEAN = LUA_TBOOLEAN,
61 TFUNCTION = LUA_TFUNCTION,
62 TLIGHTUSERDATA = LUA_TLIGHTUSERDATA,
63 TNIL = LUA_TNIL,
64 TNONE = LUA_TNONE,
65 TNUMBER = LUA_TNUMBER,
66 TSTRING = LUA_TSTRING,
67 TTABLE = LUA_TTABLE,
68 TTHREAD = LUA_TTHREAD,
69 TUSERDATA = LUA_TUSERDATA
70 };
71
72 // we reserve one upvalue for the function pointer
73 inline int upvalueindex(int n)
74 { return lua_upvalueindex(n+1); }
75
76 /*
77 * Lua error()s are wrapped in this class when rethrown into C++ code. what() returns the
78 * error message. push_lua_error() pushes the error onto lua stack. The error can only be
79 * pushed into the same state it was generated in.
80 */
81 class exception: public std::runtime_error {
82 /*
83 * We only allow moving, to avoid complications with multiple references. It shouldn't be
84 * difficult to modify this to work with copying, if that proves unavoidable.
85 */
86 state *L;
87 int key;
88
89 static std::string get_error_msg(state *L);
90
91 exception(const exception &) = delete;
92 const exception& operator=(const exception &) = delete;
93
94 public:
95 exception(exception &&other)
96 : std::runtime_error(std::move(other)), L(other.L), key(other.key)
97 { other.L = NULL; }
98
99 explicit exception(state *l);
100 virtual ~exception() throw();
101
102 void push_lua_error(state *l);
103 };
104
105 class not_string_error: public std::runtime_error {
106 public:
107 not_string_error()
108 : std::runtime_error("Cannot convert value to a string")
109 {}
110 };
111
112 // the name says it all
113 class syntax_error: public lua::exception {
114 syntax_error(const syntax_error &) = delete;
115 const syntax_error& operator=(const syntax_error &) = delete;
116
117 public:
118 syntax_error(state *L)
119 : lua::exception(L)
120 {}
121
122 syntax_error(syntax_error &&other)
123 : lua::exception(std::move(other))
124 {}
125 };
126
127 // loadfile() encountered an error while opening/reading the file
128 class file_error: public lua::exception {
129 file_error(const file_error &) = delete;
130 const file_error& operator=(const file_error &) = delete;
131
132 public:
133 file_error(state *L)
134 : lua::exception(L)
135 {}
136
137 file_error(file_error &&other)
138 : lua::exception(std::move(other))
139 {}
140 };
141
142 // double fault, lua encountered an error while running the error handler function
143 class errfunc_error: public lua::exception {
144 errfunc_error(const errfunc_error &) = delete;
145 const errfunc_error& operator=(const errfunc_error &) = delete;
146
147 public:
148 errfunc_error(state *L)
149 : lua::exception(L)
150 {}
151
152 errfunc_error(errfunc_error &&other)
153 : lua::exception(std::move(other))
154 {}
155 };
156
157 // a fancy wrapper around lua_State
158 class state {
159 std::shared_ptr<lua_State> cobj;
160
161 // destructor for C++ objects stored as lua userdata
162 template<typename T>
163 static int destroy_cpp_object(lua_State *l)
164 {
165 T *ptr = static_cast<T *>(lua_touserdata(l, -1));
166 assert(ptr);
167 try {
168 // throwing exceptions in destructors is a bad idea
169 // but we catch (and ignore) them, just in case
170 ptr->~T();
171 }
172 catch(...) {
173 }
174 return 0;
175 }
176
177 bool safe_compare(lua_CFunction trampoline, int index1, int index2);
178 public:
179 state();
180
181 /*
182 * Lua functions come in three flavours
183 * a) functions that never throw an exception
184 * b) functions that throw only in case of a memory allocation error
185 * c) functions that throw other kinds of errors
186 *
187 * Calls to type a functions are simply forwarded to the C api.
188 * Type c functions are executed in protected mode, to make sure they don't longjmp()
189 * over us (and our destructors). This add a certain amount overhead. If you care about
190 * performance, try using the raw versions (if possible).
191 * Type b functions are not executed in protected mode atm. as memory allocation errors
192 * don't happen that often (as opposed to the type c, where the user get deliberately set
193 * a metamethod that throws an error). That means those errors will do something
194 * undefined, but hopefully that won't be a problem.
195 *
196 * Semantics are mostly identical to those of the underlying C api. Any deviation is
197 * noted in the respective functions comment. The most important difference is that
198 * instead of return values, we use exceptions to indicate errors. The lua and C++
199 * exception mechanisms are integrated. That means one can throw a C++ exception and
200 * catch it in lua (with pcall). Lua error()s can be caught in C++ as exceptions of type
201 * lua::exception.
202 */
203
204 // type a, never throw
205 int absindex(int index) throw() { return index<0 && -index<=gettop() ? gettop()+1+index : index; }
206 bool getmetatable(int index) throw() { return lua_getmetatable(cobj.get(), index); }
207 int gettop() throw() { return lua_gettop(cobj.get()); }
208 void insert(int index) throw() { lua_insert(cobj.get(), index); }
209 bool isboolean(int index) throw() { return lua_isboolean(cobj.get(), index); }
210 bool isfunction(int index) throw() { return lua_isfunction(cobj.get(), index); }
211 bool islightuserdata(int index) throw() { return lua_islightuserdata(cobj.get(), index); }
212 bool isnil(int index) throw() { return lua_isnil(cobj.get(), index); }
213 bool isnone(int index) throw() { return lua_isnone(cobj.get(), index); }
214 bool isnumber(int index) throw() { return lua_isnumber(cobj.get(), index); }
215 bool isstring(int index) throw() { return lua_isstring(cobj.get(), index); }
216 void pop(int n = 1) throw() { lua_pop(cobj.get(), n); }
217 void pushboolean(bool b) throw() { lua_pushboolean(cobj.get(), b); }
218 void pushinteger(integer n) throw() { lua_pushinteger(cobj.get(), n); }
219 void pushlightuserdata(void *p) throw() { lua_pushlightuserdata(cobj.get(), p); }
220 void pushnil() throw() { lua_pushnil(cobj.get()); }
221 void pushnumber(number n) throw() { lua_pushnumber(cobj.get(), n); }
222 void pushvalue(int index) throw() { lua_pushvalue(cobj.get(), index); }
223 void rawget(int index) throw() { lua_rawget(cobj.get(), index); }
224 void rawgeti(int index, int n) throw() { lua_rawgeti(cobj.get(), index, n); }
225 bool rawequal(int index1, int index2) throw() { return lua_rawequal(cobj.get(), index1, index2); }
226 void replace(int index) throw() { lua_replace(cobj.get(), index); }
227 // lua_setmetatable returns int, but docs don't specify it's meaning :/
228 int setmetatable(int index) throw() { return lua_setmetatable(cobj.get(), index); }
229 void settop(int index) throw() { return lua_settop(cobj.get(), index); }
230 bool toboolean(int index) throw() { return lua_toboolean(cobj.get(), index); }
231 integer tointeger(int index) throw() { return lua_tointeger(cobj.get(), index); }
232 number tonumber(int index) throw() { return lua_tonumber(cobj.get(), index); }
233 void* touserdata(int index) throw() { return lua_touserdata(cobj.get(), index); }
234 Type type(int index) throw() { return static_cast<Type>(lua_type(cobj.get(), index)); }
235 // typename is a reserved word :/
236 const char* type_name(Type tp) throw() { return lua_typename(cobj.get(), tp); }
237 void unref(int t, int ref) throw() { return luaL_unref(cobj.get(), t, ref); }
238
239 // type b, throw only on memory allocation errors
240 // checkstack correctly throws bad_alloc, because lua_checkstack kindly informs us of
241 // that sitution
242 void checkstack(int extra) throw(std::bad_alloc);
243 const char* gsub(const char *s, const char *p, const char *r) { return luaL_gsub(cobj.get(), s, p, r); }
244 bool newmetatable(const char *tname) { return luaL_newmetatable(cobj.get(), tname); }
245 void newtable() { lua_newtable(cobj.get()); }
246 void *newuserdata(size_t size) { return lua_newuserdata(cobj.get(), size); }
247 // cpp_function can be anything that std::function can handle, everything else remains
248 // identical
249 void pushclosure(const cpp_function &fn, int n);
250 void pushfunction(const cpp_function &fn) { pushclosure(fn, 0); }
251 void pushstring(const char *s) { lua_pushstring(cobj.get(), s); }
252 void pushstring(const char *s, size_t len) { lua_pushlstring(cobj.get(), s, len); }
253 void pushstring(const std::string &s) { lua_pushlstring(cobj.get(), s.c_str(), s.size()); }
254 void rawgetfield(int index, const char *k) throw(std::bad_alloc);
255 void rawset(int index) { lua_rawset(cobj.get(), index); }
256 void rawsetfield(int index, const char *k) throw(std::bad_alloc);
257 int ref(int t) { return luaL_ref(cobj.get(), t); }
258 // len recieves length, if not null. Returned value may contain '\0'
259 const char* tocstring(int index, size_t *len = NULL) { return lua_tolstring(cobj.get(), index, len); }
260 // Don't use pushclosure() to create a __gc function. The problem is that lua calls them
261 // in an unspecified order, and we may end up destroying the object holding the
262 // std::function before we get a chance to call it. This pushes a function that simply
263 // calls ~T when the time comes. Only set it as __gc on userdata of type T.
264 template<typename T>
265 void pushdestructor()
266 { lua_pushcfunction(cobj.get(), &destroy_cpp_object<T>); }
267
268 // type c, throw everything but the kitchen sink
269 // call() is a protected mode call, we don't allow unprotected calls
270 void call(int nargs, int nresults, int errfunc = 0);
271 void concat(int n);
272 bool equal(int index1, int index2);
273 int gc(int what, int data);
274 void getfield(int index, const char *k);
275 void gettable(int index);
276 void getglobal(const char *name) { getfield(GLOBALSINDEX, name); }
277 bool lessthan(int index1, int index2);
278 void loadfile(const char *filename) throw(lua::syntax_error, lua::file_error, std::bad_alloc);
279 void loadstring(const char *s) throw(lua::syntax_error, std::bad_alloc);
280 bool next(int index);
281 // register is a reserved word :/
282 void register_fn(const char *name, const cpp_function &f) { pushfunction(f); setglobal(name); }
283 void setfield(int index, const char *k);
284 void setglobal(const char *name) { setfield(GLOBALSINDEX, name); }
285 void settable(int index);
286 // lua_tostring uses NULL to indicate conversion error, since there is no such thing as a
287 // NULL std::string, we throw an exception. Returned value may contain '\0'
288 std::string tostring(int index) throw(lua::not_string_error);
289 // allocate a new lua userdata of appropriate size, and create a object in it
290 // pushes the userdata on stack and returns the pointer
291 template<typename T, typename... Args>
292 T* createuserdata(Args&&... args);
293 };
294
295 /*
296 * Can be used to automatically pop temporary values off the lua stack on exit from the
297 * function/block (e.g. via an exception). It's destructor makes sure the stack contains
298 * exactly n items. The constructor initializes n to l.gettop()+n_, but that can be later
299 * changed with the overloaded operators. It is an error if stack contains less than n
300 * elements at entry into the destructor.
301 *
302 * Proposed stack discipline for functions is this:
303 * - called function always pops parameters off the stack.
304 * - if functions returns normally, it's return values are on the stack.
305 * - if function throws an exception, there are no return values on the stack.
306 * The last point differs from lua C api, which return an error message on the stack. But
307 * since we have exception.what() for that, putting the message on the stack is not
308 * necessary.
309 */
310 class stack_sentry {
311 state *L;
312 int n;
313
314 stack_sentry(const stack_sentry &) = delete;
315 const stack_sentry& operator=(const stack_sentry &) = delete;
316 public:
317 explicit stack_sentry(state &l, int n_ = 0) throw()
318 : L(&l), n(l.gettop()+n_)
319 { assert(n >= 0); }
320
321 ~stack_sentry() throw() { assert(L->gettop() >= n); L->settop(n); }
322
323 void operator++() throw() { ++n; }
324 void operator--() throw() { --n; assert(n >= 0); }
325 void operator+=(int n_) throw() { n+=n_; }
326 void operator-=(int n_) throw() { n-=n_; assert(n >= 0); }
327 };
328
329 template<typename T, typename... Args>
330 T* state::createuserdata(Args&&... args)
331 {
332 stack_sentry s(*this);
333
334 void *t = newuserdata(sizeof(T));
335 new(t) T(std::forward<Args>(args)...);
336 ++s;
337 return static_cast<T *>(t);
338 }
339}
340
341#endif // FBTK_LUAMM_HH