diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 971a83ba..b926d882 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -38,7 +38,7 @@ set(conky_sources colours.cc combine.cc common.cc conky.cc core.cc
diskio.cc entropy.cc exec.cc fs.cc mail.cc mixer.cc net_stat.cc template.cc
mboxscan.cc read_tcp.cc scroll.cc specials.cc tailhead.cc
temphelper.cc text_object.cc timeinfo.cc top.cc algebra.cc prioqueue.c proc.cc
- user.cc)
+ user.cc luamm.cc)
# add timed thread library
add_library(timed-thread timed-thread.cc)
diff --git a/src/luamm.cc b/src/luamm.cc
new file mode 100644
index 00000000..d1e6602f
--- /dev/null
+++ b/src/luamm.cc
@@ -0,0 +1,458 @@
+/* -*- mode: c; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: t -*-
+ * vim: ts=4 sw=4 noet ai cindent syntax=cpp
+ *
+ * luamm: C++ binding for lua
+ *
+ * Copyright (C) 2010 Pavel Labath et al.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ */
+
+#include "luamm.hh"
+
+namespace lua {
+ namespace {
+ // keys for storing values in lua registry
+ const char cpp_exception_metatable[] = "lua::cpp_exception_metatable";
+ const char cpp_function_metatable [] = "lua::cpp_function_metatable";
+ const char lua_exception_namespace[] = "lua::lua_exception_namespace";
+ const char this_cpp_object [] = "lua::this_cpp_object";
+
+ // destructor for C++ objects stored as lua userdata
+ template
+ int destroy_cpp_object(lua_State *l)
+ {
+ T *ptr = static_cast(lua_touserdata(l, -1));
+ assert(ptr);
+ ptr->~T();
+ return 0;
+ }
+
+ // converts C++ exceptions to strings, so lua can do something with them
+ int exception_to_string(lua_State *l)
+ {
+ std::exception_ptr *ptr = static_cast(lua_touserdata(l, -1));
+ assert(ptr);
+ try {
+ std::rethrow_exception(*ptr);
+ }
+ catch(std::exception &e) {
+ lua_pushstring(l, e.what());
+ }
+ catch(...) {
+ lua_pushstring(l, ptr->__cxa_exception_type()->name());
+ }
+ return 1;
+ }
+
+ int absindex(lua_State *l, int index) throw()
+ { return index<0 && -index<=lua_gettop(l) ? lua_gettop(l)+1+index : index; }
+
+ // Just like getfield(), only without calling metamethods (or throwing random exceptions)
+ inline void rawgetfield(lua_State *l, int index, const char *k) throw(std::bad_alloc)
+ {
+ index = absindex(l, index);
+ if(not lua_checkstack(l, 1))
+ throw std::bad_alloc();
+
+ lua_pushstring(l, k);
+ lua_rawget(l, index);
+ }
+
+ // Just like setfield(), only without calling metamethods (or throwing random exceptions)
+ inline void rawsetfield(lua_State *l, int index, const char *k) throw(std::bad_alloc)
+ {
+ index = absindex(l, index);
+ if(not lua_checkstack(l, 2))
+ throw std::bad_alloc();
+
+ lua_pushstring(l, k);
+ lua_insert(l, -2);
+ lua_rawset(l, index);
+ }
+
+ int closure_trampoline(lua_State *l)
+ {
+ lua_checkstack(l, 2);
+ rawgetfield(l, REGISTRYINDEX, this_cpp_object);
+ assert(lua_islightuserdata(l, -1));
+ state *L = static_cast( lua_touserdata(l, -1) );
+ lua_pop(l, 1);
+
+ try {
+ cpp_function *fn = static_cast( L->touserdata(lua_upvalueindex(1)) );
+ assert(fn);
+ return (*fn)(L);
+ }
+ catch(lua::exception &e) {
+ // rethrow lua errors as such
+ e.push_lua_error(L);
+ }
+ catch(...) {
+ // C++ exceptions (pointers to them, actually) are stored as lua userdata and
+ // then thrown
+ void *ptr = L->newuserdata(sizeof(std::exception_ptr));
+ L->rawgetfield(REGISTRYINDEX, cpp_exception_metatable);
+ L->setmetatable(-2);
+ new(ptr) std::exception_ptr(std::current_exception());
+ }
+
+ // lua_error does longjmp(), so destructors for objects in this function will not be
+ // called
+ return lua_error(l);
+ }
+
+ /*
+ * This function is called when lua encounters an error outside of any protected
+ * environment
+ * Throwing the exception through lua code appears to work, even if it was compiled
+ * without -fexceptions. If it turns out, it fails in some conditions, it could be
+ * replaced with some longjmp() magic. But that shouldn't be necessary, as this function
+ * will not be called under normal conditions (we execute everything in protected mode).
+ */
+ int panic_throw(lua_State *l)
+ {
+ if(not lua_checkstack(l, 1))
+ throw std::bad_alloc();
+
+ rawgetfield(l, REGISTRYINDEX, this_cpp_object);
+ assert(lua_islightuserdata(l, -1));
+ state *L = static_cast( lua_touserdata(l, -1) );
+ lua_pop(l, 1);
+
+ throw lua::exception(L);
+ }
+
+ // protected mode wrappers for various lua functions
+ int safe_concat_trampoline(lua_State *l)
+ {
+ lua_concat(l, lua_gettop(l));
+ return 1;
+ }
+
+ template
+ int safe_compare_trampoline(lua_State *l)
+ {
+ int r = compare(l, 1, 2);
+ lua_pop(l, 2);
+ lua_pushinteger(l, r);
+ return 1;
+ }
+
+ int safe_gc_trampoline(lua_State *l)
+ {
+ int what = lua_tointeger(l, -2);
+ int data = lua_tointeger(l, -1);
+ lua_pop(l, 2);
+ lua_pushinteger(l, lua_gc(l, what, data));
+ return 1;
+ }
+
+ template
+ int safe_misc_trampoline(lua_State *l)
+ {
+ misc(l, 1);
+ return nresults;
+ }
+
+ int safe_next_trampoline(lua_State *l)
+ {
+ int r = lua_next(l, 1);
+ lua_checkstack(l, 1);
+ lua_pushinteger(l, r);
+ return r ? 3 : 1;
+ }
+
+ }
+
+ std::string exception::get_error_msg(state *L)
+ {
+ static const std::string default_msg("Unknown lua exception");
+
+ try {
+ return L->tostring(-1);
+ }
+ catch(not_string_error &e) {
+ return default_msg;
+ }
+ }
+
+ exception::exception(state *l)
+ : std::runtime_error(get_error_msg(l)), L(l)
+ {
+ L->checkstack(1);
+
+ L->rawgetfield(REGISTRYINDEX, lua_exception_namespace);
+ L->insert(-2);
+ key = L->ref(-2);
+ L->pop(1);
+ }
+
+ exception::~exception() throw()
+ {
+ if(not L)
+ return;
+ L->checkstack(1);
+
+ L->rawgetfield(REGISTRYINDEX, lua_exception_namespace);
+ L->unref(-1, key);
+ L->pop();
+ }
+
+ void exception::push_lua_error(state *l)
+ {
+ if(l != L)
+ throw std::runtime_error("Cannot transfer exceptions between different lua contexts");
+ l->checkstack(2);
+
+ l->rawgetfield(REGISTRYINDEX, lua_exception_namespace);
+ l->rawgeti(-1, key);
+ l->replace(-2);
+ }
+
+ state::state()
+ {
+ if(lua_State *l = luaL_newstate())
+ cobj.reset(l, &lua_close);
+ else {
+ // docs say this can happen only in case of a memory allocation error
+ throw std::bad_alloc();
+ }
+
+ // set our panic function
+ lua_atpanic(cobj.get(), panic_throw);
+
+ checkstack(2);
+
+ // store a pointer to ourselves
+ pushlightuserdata(this);
+ rawsetfield(REGISTRYINDEX, this_cpp_object);
+
+ // a metatable for C++ exceptions travelling through lua code
+ newmetatable(cpp_exception_metatable);
+ lua_pushcfunction(cobj.get(), &exception_to_string);
+ rawsetfield(-2, "__tostring");
+ pushboolean(false);
+ rawsetfield(-2, "__metatable");
+ lua_pushcfunction(cobj.get(), &destroy_cpp_object);
+ rawsetfield(-2, "__gc");
+ pop();
+
+ // a metatable for C++ functions callable from lua code
+ newmetatable(cpp_function_metatable);
+ lua_pushcfunction(cobj.get(), &destroy_cpp_object);
+ rawsetfield(-2, "__gc");
+ pop();
+
+ // while they're travelling through C++ code, lua exceptions will reside here
+ newtable();
+ rawsetfield(REGISTRYINDEX, lua_exception_namespace);
+
+ luaL_openlibs(cobj.get());
+ }
+
+ void state::call(int nargs, int nresults, int errfunc)
+ {
+ int r = lua_pcall(cobj.get(), nargs, nresults, errfunc);
+ if(r == 0)
+ return;
+
+ if(r == LUA_ERRMEM) {
+ // memory allocation error, cross your fingers
+ throw std::bad_alloc();
+ }
+
+ checkstack(3);
+ rawgetfield(REGISTRYINDEX, cpp_exception_metatable);
+ if(getmetatable(-2)) {
+ if(rawequal(-1, -2)) {
+ // it's a C++ exception, rethrow it
+ std::exception_ptr *ptr = static_cast(touserdata(-3));
+ assert(ptr);
+
+ /*
+ * we create a copy, so we can pop the object without fearing the exception will
+ * be collected by lua's GC
+ */
+ std::exception_ptr t(*ptr); ptr = NULL;
+ pop(3);
+ std::rethrow_exception(t);
+ }
+ pop(2);
+ }
+ // it's a lua exception, wrap it
+ if(r == LUA_ERRERR)
+ throw lua::errfunc_error(this);
+ else
+ throw lua::exception(this);
+ }
+
+ void state::checkstack(int extra) throw(std::bad_alloc)
+ {
+ if(not lua_checkstack(cobj.get(), extra))
+ throw std::bad_alloc();
+ }
+
+ void state::concat(int n)
+ {
+ assert(n>=0);
+ checkstack(1);
+ lua_pushcfunction(cobj.get(), safe_concat_trampoline);
+ insert(-n-1);
+ call(n, 1, 0);
+ }
+
+ bool state::equal(int index1, int index2)
+ {
+ // avoid pcall overhead in trivial cases
+ if( rawequal(index1, index2) )
+ return true;
+
+ return safe_compare(&safe_compare_trampoline, index1, index2);
+ }
+
+ int state::gc(int what, int data)
+ {
+ checkstack(3);
+ lua_pushcfunction(cobj.get(), safe_gc_trampoline);
+ pushinteger(what);
+ pushinteger(data);
+ call(2, 1, 0);
+ assert(isnumber(-1));
+ int r = tointeger(-1);
+ pop();
+ return r;
+ }
+
+ void state::getfield(int index, const char *k)
+ {
+ checkstack(1);
+ index = absindex(index);
+ pushstring(k);
+ gettable(index);
+ }
+
+ void state::gettable(int index)
+ {
+ checkstack(2);
+ pushvalue(index);
+ insert(-2);
+ lua_pushcfunction(cobj.get(), (&safe_misc_trampoline<&lua_gettable, 1>));
+ insert(-3);
+ call(2, 1, 0);
+ }
+
+ bool state::lessthan(int index1, int index2)
+ {
+ return safe_compare(&safe_compare_trampoline<&lua_lessthan>, index1, index2);
+ }
+
+ void state::loadstring(const char *s) throw(lua::syntax_error, std::bad_alloc)
+ {
+ switch(luaL_loadstring(cobj.get(), s)) {
+ case 0:
+ return;
+ case LUA_ERRSYNTAX:
+ throw lua::syntax_error(this);
+ case LUA_ERRMEM:
+ throw std::bad_alloc();
+ default:
+ assert(0);
+ }
+ }
+
+ bool state::next(int index)
+ {
+ checkstack(2);
+ pushvalue(index);
+ insert(-2);
+ lua_pushcfunction(cobj.get(), &safe_next_trampoline);
+ insert(-3);
+
+ call(2, MULTRET, 0);
+
+ assert(isnumber(-1));
+ int r = tointeger(-1);
+ pop();
+ return r;
+ }
+
+ void state::pushclosure(const cpp_function &fn, int n)
+ {
+ checkstack(2);
+
+ void *ptr = newuserdata(sizeof(cpp_function));
+ rawgetfield(REGISTRYINDEX, cpp_function_metatable);
+ setmetatable(-2);
+ new(ptr) cpp_function(fn);
+
+ insert(-n-1);
+ lua_pushcclosure(cobj.get(), &closure_trampoline, n+1);
+ }
+
+ void state::rawgetfield(int index, const char *k) throw(std::bad_alloc)
+ { lua::rawgetfield(cobj.get(), index, k); }
+
+ void state::rawsetfield(int index, const char *k) throw(std::bad_alloc)
+ { lua::rawsetfield(cobj.get(), index, k); }
+
+ bool state::safe_compare(lua_CFunction trampoline, int index1, int index2)
+ {
+ // if one of the indexes is invalid, return false
+ if(isnone(index1) || isnone(index2))
+ return false;
+
+ // convert relative indexes into absolute
+ index1 = absindex(index1);
+ index2 = absindex(index2);
+
+ checkstack(3);
+ lua_pushcfunction(cobj.get(), trampoline);
+ pushvalue(index1);
+ pushvalue(index2);
+ call(2, 1, 0);
+ assert(isnumber(-1));
+ int r = tointeger(-1);
+ pop();
+ return r;
+ }
+
+ void state::setfield(int index, const char *k)
+ {
+ checkstack(1);
+ index = absindex(index);
+ pushstring(k);
+ insert(-2);
+ settable(index);
+ }
+
+ void state::settable(int index)
+ {
+ checkstack(2);
+ pushvalue(index);
+ insert(-3);
+ lua_pushcfunction(cobj.get(), (&safe_misc_trampoline<&lua_settable, 0>));
+ insert(-4);
+ call(2, 0, 0);
+ }
+
+ std::string state::tostring(int index) throw(lua::not_string_error)
+ {
+ size_t len;
+ const char *str = lua_tolstring(cobj.get(), index, &len);
+ if(not str)
+ throw not_string_error();
+ return std::string(str, len);
+ }
+}
diff --git a/src/luamm.hh b/src/luamm.hh
new file mode 100644
index 00000000..d36a6c7f
--- /dev/null
+++ b/src/luamm.hh
@@ -0,0 +1,267 @@
+/* -*- mode: c; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: t -*-
+ * vim: ts=4 sw=4 noet ai cindent syntax=cpp
+ *
+ * luamm: C++ binding for lua
+ *
+ * Copyright (C) 2010 Pavel Labath et al.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ */
+
+#include
+#include
+#include
+
+#include
+
+namespace lua {
+ class state;
+
+ typedef lua_Integer integer;
+ typedef lua_Number number;
+ typedef std::function cpp_function;
+
+ enum {
+ ENVIRONINDEX = LUA_ENVIRONINDEX,
+ GLOBALSINDEX = LUA_GLOBALSINDEX,
+ REGISTRYINDEX = LUA_REGISTRYINDEX
+ };
+
+ enum {
+ GCSTOP = LUA_GCSTOP,
+ GCRESTART = LUA_GCRESTART,
+ GCCOLLECT = LUA_GCCOLLECT,
+ GCCOUNT = LUA_GCCOUNT,
+ GCCOUNTB = LUA_GCCOUNTB,
+ GCSTEP = LUA_GCSTEP,
+ GCSETPAUSE = LUA_GCSETPAUSE,
+ GCSETSTEPMUL = LUA_GCSETSTEPMUL
+ };
+
+ enum {
+ MULTRET = LUA_MULTRET
+ };
+
+ enum {
+ TBOOLEAN = LUA_TBOOLEAN,
+ TFUNCTION = LUA_TFUNCTION,
+ TLIGHTUSERDATA = LUA_TLIGHTUSERDATA,
+ TNIL = LUA_TNIL,
+ TNONE = LUA_TNONE,
+ TNUMBER = LUA_TNUMBER,
+ TSTRING = LUA_TSTRING,
+ TTABLE = LUA_TTABLE,
+ TTHREAD = LUA_TTHREAD,
+ TUSERDATA = LUA_TUSERDATA
+ };
+
+ // we reserve one upvalue for the function pointer
+ int upvalueindex(int n)
+ { return lua_upvalueindex(n+1); }
+
+ /*
+ * Lua error()s are wrapped in this class when rethrown into C++ code. what() returns the
+ * error message. push_lua_error() pushes the error onto lua stack. The error can only be
+ * pushed into the same state it was generated in.
+ */
+ class exception: public std::runtime_error {
+ /*
+ * We only allow moving, to avoid complications with multiple references. It shouldn't be
+ * difficult to modify this to work with copying, if that proves unavoidable.
+ */
+ state *L;
+ int key;
+
+ static std::string get_error_msg(state *L);
+
+ exception(const exception &) = delete;
+ const exception& operator=(const exception &) = delete;
+
+ public:
+ exception(exception &&other)
+ : std::runtime_error(std::move(other)), L(other.L), key(other.key)
+ { other.L = NULL; }
+
+ explicit exception(state *l);
+ virtual ~exception() throw();
+
+ void push_lua_error(state *l);
+ };
+
+ class not_string_error: public std::runtime_error {
+ public:
+ not_string_error()
+ : std::runtime_error("Cannot convert value to a string")
+ {}
+ };
+
+ // the name says it all
+ class syntax_error: public lua::exception {
+ syntax_error(const syntax_error &) = delete;
+ const syntax_error& operator=(const syntax_error &) = delete;
+
+ public:
+ syntax_error(state *L)
+ : lua::exception(L)
+ {}
+
+ syntax_error(syntax_error &&other)
+ : lua::exception(std::move(other))
+ {}
+ };
+
+ // double fault, lua encountered an error while running the error handler function
+ class errfunc_error: public lua::exception {
+ errfunc_error(const errfunc_error &) = delete;
+ const errfunc_error& operator=(const errfunc_error &) = delete;
+
+ public:
+ errfunc_error(state *L)
+ : lua::exception(L)
+ {}
+
+ errfunc_error(errfunc_error &&other)
+ : lua::exception(std::move(other))
+ {}
+ };
+
+ // a fancy wrapper around lua_State
+ class state {
+ std::shared_ptr cobj;
+
+ bool safe_compare(lua_CFunction trampoline, int index1, int index2);
+ public:
+ state();
+
+ /*
+ * Lua functions come in three flavours
+ * a) functions that never throw an exception
+ * b) functions that throw only in case of a memory allocation error
+ * c) functions that throw other kinds of errors
+ *
+ * Calls to type a functions are simply forwarded to the C api.
+ * Type c functions are executed in protected mode, to make sure they don't longjmp()
+ * over us (and our destructors). This add a certain amount overhead. If you care about
+ * performance, try using the raw versions (if possible).
+ * Type b functions are not executed in protected mode atm. as memory allocation errors
+ * don't happen that often (as opposed to the type c, where the user get deliberately set
+ * a metamethod that throws an error). That means those errors will do something
+ * undefined, but hopefully that won't be a problem.
+ *
+ * Semantics are mostly identical to those of the underlying C api. Any deviation is
+ * noted in the respective functions comment. The most important difference is that
+ * instead of return values, we use exceptions to indicate errors. The lua and C++
+ * exception mechanisms are integrated. That means one can throw a C++ exception and
+ * catch it in lua (with pcall). Lua error()s can be caught in C++ as exceptions of type
+ * lua::exception.
+ */
+
+ // type a, never throw
+ int absindex(int index) throw() { return index<0 && -index<=gettop() ? gettop()+1+index : index; }
+ bool getmetatable(int index) throw() { return lua_getmetatable(cobj.get(), index); }
+ int gettop() throw() { return lua_gettop(cobj.get()); }
+ void insert(int index) throw() { lua_insert(cobj.get(), index); }
+ bool isfunction(int index) throw() { return lua_isfunction(cobj.get(), index); }
+ bool islightuserdata(int index) throw() { return lua_islightuserdata(cobj.get(), index); }
+ bool isnone(int index) throw() { return lua_isnone(cobj.get(), index); }
+ bool isnumber(int index) throw() { return lua_isnumber(cobj.get(), index); }
+ bool isstring(int index) throw() { return lua_isstring(cobj.get(), index); }
+ void pop(int n = 1) throw() { lua_pop(cobj.get(), n); }
+ void pushboolean(bool b) throw() { lua_pushboolean(cobj.get(), b); }
+ void pushinteger(integer n) throw() { lua_pushinteger(cobj.get(), n); }
+ void pushlightuserdata(void *p) throw() { lua_pushlightuserdata(cobj.get(), p); }
+ void pushnil() throw() { lua_pushnil(cobj.get()); }
+ void pushvalue(int index) throw() { lua_pushvalue(cobj.get(), index); }
+ void rawget(int index) throw() { lua_rawget(cobj.get(), index); }
+ void rawgeti(int index, int n) throw() { lua_rawgeti(cobj.get(), index, n); }
+ bool rawequal(int index1, int index2) throw() { return lua_rawequal(cobj.get(), index1, index2); }
+ void rawset(int index) throw() { lua_rawset(cobj.get(), index); }
+ void replace(int index) throw() { lua_replace(cobj.get(), index); }
+ // lua_setmetatable returns int, but docs don't specify it's meaning :/
+ int setmetatable(int index) throw() { return lua_setmetatable(cobj.get(), index); }
+ integer tointeger(int index) throw() { return lua_tointeger(cobj.get(), index); }
+ number tonumber(int index) throw() { return lua_tonumber(cobj.get(), index); }
+ void* touserdata(int index) throw() { return lua_touserdata(cobj.get(), index); }
+ int type(int index) throw() { return lua_type(cobj.get(), index); }
+ // typename is a reserved word :/
+ const char* type_name(int tp) throw() { return lua_typename(cobj.get(), tp); }
+ void unref(int t, int ref) throw() { return luaL_unref(cobj.get(), t, ref); }
+
+ // type b, throw only on memory allocation errors
+ // checkstack correctly throws bad_alloc, because lua_checkstack kindly informs us of
+ // that sitution
+ void checkstack(int extra) throw(std::bad_alloc);
+ bool newmetatable(const char *tname) { return luaL_newmetatable(cobj.get(), tname); }
+ void newtable() { lua_newtable(cobj.get()); }
+ void *newuserdata(size_t size) { return lua_newuserdata(cobj.get(), size); }
+ // cpp_function can be anything that std::function can handle, everything else remains
+ // identical
+ void pushclosure(const cpp_function &fn, int n);
+ void pushfunction(const cpp_function &fn) { pushclosure(fn, 0); }
+ void pushstring(const char *s) { lua_pushstring(cobj.get(), s); }
+ void rawgetfield(int index, const char *k) throw(std::bad_alloc);
+ void rawsetfield(int index, const char *k) throw(std::bad_alloc);
+ int ref(int t) { return luaL_ref(cobj.get(), t); }
+ // len recieves length, if not null. Returned value may contain '\0'
+ const char* tocstring(int index, size_t *len = NULL) { return lua_tolstring(cobj.get(), index, len); }
+
+ // type c, throw everything but the kitchen sink
+ // call() is a protected mode call, we don't allow unprotected calls
+ void call(int nargs, int nresults, int errfunc = 0);
+ void concat(int n);
+ bool equal(int index1, int index2);
+ int gc(int what, int data);
+ void getfield(int index, const char *k);
+ void gettable(int index);
+ void getglobal(const char *name) { getfield(GLOBALSINDEX, name); }
+ bool lessthan(int index1, int index2);
+ void loadstring(const char *s) throw(lua::syntax_error, std::bad_alloc);
+ bool next(int index);
+ // register is a reserved word :/
+ void register_fn(const char *name, const cpp_function &f) { pushfunction(f); setglobal(name); }
+ void setfield(int index, const char *k);
+ void setglobal(const char *name) { setfield(GLOBALSINDEX, name); }
+ void settable(int index);
+ // lua_tostring uses NULL to indicate conversion error, since there is no such thing as a
+ // NULL std::string, we throw an exception. Returned value may contain '\0'
+ std::string tostring(int index) throw(lua::not_string_error);
+ };
+
+ /*
+ * Can be used to automatically pop temporary values off the lua stack on exit from the
+ * function/block (e.g. via an exception). The constructor parameter indicates the number of
+ * values to pop(). That can be later changed with the overloaded operators. The idiom is:
+ * stack_sentry s(L);
+ * L.an_operation_that_pushes_something(); ++s;
+ * ...
+ */
+ class stack_sentry {
+ state *L;
+ int n;
+
+ stack_sentry(const stack_sentry &) = delete;
+ const stack_sentry& operator=(const stack_sentry &) = delete;
+ public:
+ explicit stack_sentry(state &l, int n_ = 0) throw()
+ : L(&l), n(n_)
+ {}
+
+ ~stack_sentry() throw() { L->pop(n); }
+
+ void operator++() throw() { ++n; }
+ void operator--() throw() { --n; }
+ void operator+=(int n_) throw() { n+=n_; }
+ void operator-=(int n_) throw() { n-=n_; }
+ };
+}