1
0
mirror of https://github.com/Llewellynvdm/conky.git synced 2024-11-18 11:05:18 +00:00

Retab luamm in line with setting we use (yeah... i know :/)

This commit is contained in:
Pavel Labath 2010-02-12 17:11:50 +01:00
parent 3e98e18324
commit 0ae2ba5210
2 changed files with 582 additions and 582 deletions

View File

@ -24,429 +24,429 @@
#include "luamm.hh" #include "luamm.hh"
namespace lua { namespace lua {
namespace { namespace {
// keys for storing values in lua registry // keys for storing values in lua registry
const char cpp_exception_metatable[] = "lua::cpp_exception_metatable"; const char cpp_exception_metatable[] = "lua::cpp_exception_metatable";
const char cpp_function_metatable [] = "lua::cpp_function_metatable"; const char cpp_function_metatable [] = "lua::cpp_function_metatable";
const char lua_exception_namespace[] = "lua::lua_exception_namespace"; const char lua_exception_namespace[] = "lua::lua_exception_namespace";
const char this_cpp_object [] = "lua::this_cpp_object"; const char this_cpp_object [] = "lua::this_cpp_object";
// converts C++ exceptions to strings, so lua can do something with them // converts C++ exceptions to strings, so lua can do something with them
int exception_to_string(lua_State *l) int exception_to_string(lua_State *l)
{ {
std::exception_ptr *ptr = static_cast<std::exception_ptr *>(lua_touserdata(l, -1)); std::exception_ptr *ptr = static_cast<std::exception_ptr *>(lua_touserdata(l, -1));
assert(ptr); assert(ptr);
try { try {
std::rethrow_exception(*ptr); std::rethrow_exception(*ptr);
} }
catch(std::exception &e) { catch(std::exception &e) {
lua_pushstring(l, e.what()); lua_pushstring(l, e.what());
} }
catch(...) { catch(...) {
lua_pushstring(l, ptr->__cxa_exception_type()->name()); lua_pushstring(l, ptr->__cxa_exception_type()->name());
} }
return 1; return 1;
} }
int absindex(lua_State *l, int index) throw() int absindex(lua_State *l, int index) throw()
{ return index<0 && -index<=lua_gettop(l) ? lua_gettop(l)+1+index : index; } { return index<0 && -index<=lua_gettop(l) ? lua_gettop(l)+1+index : index; }
// Just like getfield(), only without calling metamethods (or throwing random exceptions) // 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) inline void rawgetfield(lua_State *l, int index, const char *k) throw(std::bad_alloc)
{ {
index = absindex(l, index); index = absindex(l, index);
if(not lua_checkstack(l, 1)) if(not lua_checkstack(l, 1))
throw std::bad_alloc(); throw std::bad_alloc();
lua_pushstring(l, k); lua_pushstring(l, k);
lua_rawget(l, index); lua_rawget(l, index);
} }
// Just like setfield(), only without calling metamethods (or throwing random exceptions) // 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) inline void rawsetfield(lua_State *l, int index, const char *k) throw(std::bad_alloc)
{ {
index = absindex(l, index); index = absindex(l, index);
if(not lua_checkstack(l, 2)) if(not lua_checkstack(l, 2))
throw std::bad_alloc(); throw std::bad_alloc();
lua_pushstring(l, k); lua_pushstring(l, k);
lua_insert(l, -2); lua_insert(l, -2);
lua_rawset(l, index); lua_rawset(l, index);
} }
int closure_trampoline(lua_State *l) int closure_trampoline(lua_State *l)
{ {
lua_checkstack(l, 2); lua_checkstack(l, 2);
rawgetfield(l, REGISTRYINDEX, this_cpp_object); rawgetfield(l, REGISTRYINDEX, this_cpp_object);
assert(lua_islightuserdata(l, -1)); assert(lua_islightuserdata(l, -1));
state *L = static_cast<state *>( lua_touserdata(l, -1) ); state *L = static_cast<state *>( lua_touserdata(l, -1) );
lua_pop(l, 1); lua_pop(l, 1);
try { try {
cpp_function *fn = static_cast<cpp_function *>( L->touserdata(lua_upvalueindex(1)) ); cpp_function *fn = static_cast<cpp_function *>( L->touserdata(lua_upvalueindex(1)) );
assert(fn); assert(fn);
return (*fn)(L); return (*fn)(L);
} }
catch(lua::exception &e) { catch(lua::exception &e) {
// rethrow lua errors as such // rethrow lua errors as such
e.push_lua_error(L); e.push_lua_error(L);
} }
catch(...) { catch(...) {
// C++ exceptions (pointers to them, actually) are stored as lua userdata and // C++ exceptions (pointers to them, actually) are stored as lua userdata and
// then thrown // then thrown
void *ptr = L->newuserdata(sizeof(std::exception_ptr)); void *ptr = L->newuserdata(sizeof(std::exception_ptr));
L->rawgetfield(REGISTRYINDEX, cpp_exception_metatable); L->rawgetfield(REGISTRYINDEX, cpp_exception_metatable);
L->setmetatable(-2); L->setmetatable(-2);
new(ptr) std::exception_ptr(std::current_exception()); new(ptr) std::exception_ptr(std::current_exception());
} }
// lua_error does longjmp(), so destructors for objects in this function will not be // lua_error does longjmp(), so destructors for objects in this function will not be
// called // called
return lua_error(l); return lua_error(l);
} }
/* /*
* This function is called when lua encounters an error outside of any protected * This function is called when lua encounters an error outside of any protected
* environment * environment
* Throwing the exception through lua code appears to work, even if it was compiled * 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 * 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 * 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). * will not be called under normal conditions (we execute everything in protected mode).
*/ */
int panic_throw(lua_State *l) int panic_throw(lua_State *l)
{ {
if(not lua_checkstack(l, 1)) if(not lua_checkstack(l, 1))
throw std::bad_alloc(); throw std::bad_alloc();
rawgetfield(l, REGISTRYINDEX, this_cpp_object); rawgetfield(l, REGISTRYINDEX, this_cpp_object);
assert(lua_islightuserdata(l, -1)); assert(lua_islightuserdata(l, -1));
state *L = static_cast<state *>( lua_touserdata(l, -1) ); state *L = static_cast<state *>( lua_touserdata(l, -1) );
lua_pop(l, 1); lua_pop(l, 1);
throw lua::exception(L); throw lua::exception(L);
} }
// protected mode wrappers for various lua functions // protected mode wrappers for various lua functions
int safe_concat_trampoline(lua_State *l) int safe_concat_trampoline(lua_State *l)
{ {
lua_concat(l, lua_gettop(l)); lua_concat(l, lua_gettop(l));
return 1; return 1;
} }
template<int (*compare)(lua_State *, int, int)> template<int (*compare)(lua_State *, int, int)>
int safe_compare_trampoline(lua_State *l) int safe_compare_trampoline(lua_State *l)
{ {
int r = compare(l, 1, 2); int r = compare(l, 1, 2);
lua_pop(l, 2); lua_pop(l, 2);
lua_pushinteger(l, r); lua_pushinteger(l, r);
return 1; return 1;
} }
int safe_gc_trampoline(lua_State *l) int safe_gc_trampoline(lua_State *l)
{ {
int what = lua_tointeger(l, -2); int what = lua_tointeger(l, -2);
int data = lua_tointeger(l, -1); int data = lua_tointeger(l, -1);
lua_pop(l, 2); lua_pop(l, 2);
lua_pushinteger(l, lua_gc(l, what, data)); lua_pushinteger(l, lua_gc(l, what, data));
return 1; return 1;
} }
template<void (*misc)(lua_State *, int), int nresults> template<void (*misc)(lua_State *, int), int nresults>
int safe_misc_trampoline(lua_State *l) int safe_misc_trampoline(lua_State *l)
{ {
misc(l, 1); misc(l, 1);
return nresults; return nresults;
} }
int safe_next_trampoline(lua_State *l) int safe_next_trampoline(lua_State *l)
{ {
int r = lua_next(l, 1); int r = lua_next(l, 1);
lua_checkstack(l, 1); lua_checkstack(l, 1);
lua_pushinteger(l, r); lua_pushinteger(l, r);
return r ? 3 : 1; return r ? 3 : 1;
} }
} }
std::string exception::get_error_msg(state *L) std::string exception::get_error_msg(state *L)
{ {
static const std::string default_msg("Unknown lua exception"); static const std::string default_msg("Unknown lua exception");
try { try {
return L->tostring(-1); return L->tostring(-1);
} }
catch(not_string_error &e) { catch(not_string_error &e) {
return default_msg; return default_msg;
} }
} }
exception::exception(state *l) exception::exception(state *l)
: std::runtime_error(get_error_msg(l)), L(l) : std::runtime_error(get_error_msg(l)), L(l)
{ {
L->checkstack(1); L->checkstack(1);
L->rawgetfield(REGISTRYINDEX, lua_exception_namespace); L->rawgetfield(REGISTRYINDEX, lua_exception_namespace);
L->insert(-2); L->insert(-2);
key = L->ref(-2); key = L->ref(-2);
L->pop(1); L->pop(1);
} }
exception::~exception() throw() exception::~exception() throw()
{ {
if(not L) if(not L)
return; return;
L->checkstack(1); L->checkstack(1);
L->rawgetfield(REGISTRYINDEX, lua_exception_namespace); L->rawgetfield(REGISTRYINDEX, lua_exception_namespace);
L->unref(-1, key); L->unref(-1, key);
L->pop(); L->pop();
} }
void exception::push_lua_error(state *l) void exception::push_lua_error(state *l)
{ {
if(l != L) if(l != L)
throw std::runtime_error("Cannot transfer exceptions between different lua contexts"); throw std::runtime_error("Cannot transfer exceptions between different lua contexts");
l->checkstack(2); l->checkstack(2);
l->rawgetfield(REGISTRYINDEX, lua_exception_namespace); l->rawgetfield(REGISTRYINDEX, lua_exception_namespace);
l->rawgeti(-1, key); l->rawgeti(-1, key);
l->replace(-2); l->replace(-2);
} }
state::state() state::state()
{ {
if(lua_State *l = luaL_newstate()) if(lua_State *l = luaL_newstate())
cobj.reset(l, &lua_close); cobj.reset(l, &lua_close);
else { else {
// docs say this can happen only in case of a memory allocation error // docs say this can happen only in case of a memory allocation error
throw std::bad_alloc(); throw std::bad_alloc();
} }
// set our panic function // set our panic function
lua_atpanic(cobj.get(), panic_throw); lua_atpanic(cobj.get(), panic_throw);
checkstack(2); checkstack(2);
// store a pointer to ourselves // store a pointer to ourselves
pushlightuserdata(this); pushlightuserdata(this);
rawsetfield(REGISTRYINDEX, this_cpp_object); rawsetfield(REGISTRYINDEX, this_cpp_object);
// a metatable for C++ exceptions travelling through lua code // a metatable for C++ exceptions travelling through lua code
newmetatable(cpp_exception_metatable); newmetatable(cpp_exception_metatable);
lua_pushcfunction(cobj.get(), &exception_to_string); lua_pushcfunction(cobj.get(), &exception_to_string);
rawsetfield(-2, "__tostring"); rawsetfield(-2, "__tostring");
pushboolean(false); pushboolean(false);
rawsetfield(-2, "__metatable"); rawsetfield(-2, "__metatable");
pushdestructor<std::exception_ptr>(); pushdestructor<std::exception_ptr>();
rawsetfield(-2, "__gc"); rawsetfield(-2, "__gc");
pop(); pop();
// a metatable for C++ functions callable from lua code // a metatable for C++ functions callable from lua code
newmetatable(cpp_function_metatable); newmetatable(cpp_function_metatable);
pushboolean(false); pushboolean(false);
rawsetfield(-2, "__metatable"); rawsetfield(-2, "__metatable");
pushdestructor<cpp_function>(); pushdestructor<cpp_function>();
rawsetfield(-2, "__gc"); rawsetfield(-2, "__gc");
pop(); pop();
// while they're travelling through C++ code, lua exceptions will reside here // while they're travelling through C++ code, lua exceptions will reside here
newtable(); newtable();
rawsetfield(REGISTRYINDEX, lua_exception_namespace); rawsetfield(REGISTRYINDEX, lua_exception_namespace);
luaL_openlibs(cobj.get()); luaL_openlibs(cobj.get());
} }
void state::call(int nargs, int nresults, int errfunc) void state::call(int nargs, int nresults, int errfunc)
{ {
int r = lua_pcall(cobj.get(), nargs, nresults, errfunc); int r = lua_pcall(cobj.get(), nargs, nresults, errfunc);
if(r == 0) if(r == 0)
return; return;
if(r == LUA_ERRMEM) { if(r == LUA_ERRMEM) {
// memory allocation error, cross your fingers // memory allocation error, cross your fingers
throw std::bad_alloc(); throw std::bad_alloc();
} }
checkstack(3); checkstack(3);
rawgetfield(REGISTRYINDEX, cpp_exception_metatable); rawgetfield(REGISTRYINDEX, cpp_exception_metatable);
if(getmetatable(-2)) { if(getmetatable(-2)) {
if(rawequal(-1, -2)) { if(rawequal(-1, -2)) {
// it's a C++ exception, rethrow it // it's a C++ exception, rethrow it
std::exception_ptr *ptr = static_cast<std::exception_ptr *>(touserdata(-3)); std::exception_ptr *ptr = static_cast<std::exception_ptr *>(touserdata(-3));
assert(ptr); assert(ptr);
/* /*
* we create a copy, so we can pop the object without fearing the exception will * we create a copy, so we can pop the object without fearing the exception will
* be collected by lua's GC * be collected by lua's GC
*/ */
std::exception_ptr t(*ptr); ptr = NULL; std::exception_ptr t(*ptr); ptr = NULL;
pop(3); pop(3);
std::rethrow_exception(t); std::rethrow_exception(t);
} }
pop(2); pop(2);
} }
// it's a lua exception, wrap it // it's a lua exception, wrap it
if(r == LUA_ERRERR) if(r == LUA_ERRERR)
throw lua::errfunc_error(this); throw lua::errfunc_error(this);
else else
throw lua::exception(this); throw lua::exception(this);
} }
void state::checkstack(int extra) throw(std::bad_alloc) void state::checkstack(int extra) throw(std::bad_alloc)
{ {
if(not lua_checkstack(cobj.get(), extra)) if(not lua_checkstack(cobj.get(), extra))
throw std::bad_alloc(); throw std::bad_alloc();
} }
void state::concat(int n) void state::concat(int n)
{ {
assert(n>=0); assert(n>=0);
checkstack(1); checkstack(1);
lua_pushcfunction(cobj.get(), safe_concat_trampoline); lua_pushcfunction(cobj.get(), safe_concat_trampoline);
insert(-n-1); insert(-n-1);
call(n, 1, 0); call(n, 1, 0);
} }
bool state::equal(int index1, int index2) bool state::equal(int index1, int index2)
{ {
// avoid pcall overhead in trivial cases // avoid pcall overhead in trivial cases
if( rawequal(index1, index2) ) if( rawequal(index1, index2) )
return true; return true;
return safe_compare(&safe_compare_trampoline<lua_equal>, index1, index2); return safe_compare(&safe_compare_trampoline<lua_equal>, index1, index2);
} }
int state::gc(int what, int data) int state::gc(int what, int data)
{ {
checkstack(3); checkstack(3);
lua_pushcfunction(cobj.get(), safe_gc_trampoline); lua_pushcfunction(cobj.get(), safe_gc_trampoline);
pushinteger(what); pushinteger(what);
pushinteger(data); pushinteger(data);
call(2, 1, 0); call(2, 1, 0);
assert(isnumber(-1)); assert(isnumber(-1));
int r = tointeger(-1); int r = tointeger(-1);
pop(); pop();
return r; return r;
} }
void state::getfield(int index, const char *k) void state::getfield(int index, const char *k)
{ {
checkstack(1); checkstack(1);
index = absindex(index); index = absindex(index);
pushstring(k); pushstring(k);
gettable(index); gettable(index);
} }
void state::gettable(int index) void state::gettable(int index)
{ {
checkstack(2); checkstack(2);
pushvalue(index); pushvalue(index);
insert(-2); insert(-2);
lua_pushcfunction(cobj.get(), (&safe_misc_trampoline<&lua_gettable, 1>)); lua_pushcfunction(cobj.get(), (&safe_misc_trampoline<&lua_gettable, 1>));
insert(-3); insert(-3);
call(2, 1, 0); call(2, 1, 0);
} }
bool state::lessthan(int index1, int index2) bool state::lessthan(int index1, int index2)
{ {
return safe_compare(&safe_compare_trampoline<&lua_lessthan>, index1, index2); return safe_compare(&safe_compare_trampoline<&lua_lessthan>, index1, index2);
} }
void state::loadstring(const char *s) throw(lua::syntax_error, std::bad_alloc) void state::loadstring(const char *s) throw(lua::syntax_error, std::bad_alloc)
{ {
switch(luaL_loadstring(cobj.get(), s)) { switch(luaL_loadstring(cobj.get(), s)) {
case 0: case 0:
return; return;
case LUA_ERRSYNTAX: case LUA_ERRSYNTAX:
throw lua::syntax_error(this); throw lua::syntax_error(this);
case LUA_ERRMEM: case LUA_ERRMEM:
throw std::bad_alloc(); throw std::bad_alloc();
default: default:
assert(0); assert(0);
} }
} }
bool state::next(int index) bool state::next(int index)
{ {
checkstack(2); checkstack(2);
pushvalue(index); pushvalue(index);
insert(-2); insert(-2);
lua_pushcfunction(cobj.get(), &safe_next_trampoline); lua_pushcfunction(cobj.get(), &safe_next_trampoline);
insert(-3); insert(-3);
call(2, MULTRET, 0); call(2, MULTRET, 0);
assert(isnumber(-1)); assert(isnumber(-1));
int r = tointeger(-1); int r = tointeger(-1);
pop(); pop();
return r; return r;
} }
void state::pushclosure(const cpp_function &fn, int n) void state::pushclosure(const cpp_function &fn, int n)
{ {
checkstack(2); checkstack(2);
void *ptr = newuserdata(sizeof(cpp_function)); void *ptr = newuserdata(sizeof(cpp_function));
rawgetfield(REGISTRYINDEX, cpp_function_metatable); rawgetfield(REGISTRYINDEX, cpp_function_metatable);
setmetatable(-2); setmetatable(-2);
new(ptr) cpp_function(fn); new(ptr) cpp_function(fn);
insert(-n-1); insert(-n-1);
lua_pushcclosure(cobj.get(), &closure_trampoline, n+1); lua_pushcclosure(cobj.get(), &closure_trampoline, n+1);
} }
void state::rawgetfield(int index, const char *k) throw(std::bad_alloc) void state::rawgetfield(int index, const char *k) throw(std::bad_alloc)
{ lua::rawgetfield(cobj.get(), index, k); } { lua::rawgetfield(cobj.get(), index, k); }
void state::rawsetfield(int index, const char *k) throw(std::bad_alloc) void state::rawsetfield(int index, const char *k) throw(std::bad_alloc)
{ lua::rawsetfield(cobj.get(), index, k); } { lua::rawsetfield(cobj.get(), index, k); }
bool state::safe_compare(lua_CFunction trampoline, int index1, int index2) bool state::safe_compare(lua_CFunction trampoline, int index1, int index2)
{ {
// if one of the indexes is invalid, return false // if one of the indexes is invalid, return false
if(isnone(index1) || isnone(index2)) if(isnone(index1) || isnone(index2))
return false; return false;
// convert relative indexes into absolute // convert relative indexes into absolute
index1 = absindex(index1); index1 = absindex(index1);
index2 = absindex(index2); index2 = absindex(index2);
checkstack(3); checkstack(3);
lua_pushcfunction(cobj.get(), trampoline); lua_pushcfunction(cobj.get(), trampoline);
pushvalue(index1); pushvalue(index1);
pushvalue(index2); pushvalue(index2);
call(2, 1, 0); call(2, 1, 0);
assert(isnumber(-1)); assert(isnumber(-1));
int r = tointeger(-1); int r = tointeger(-1);
pop(); pop();
return r; return r;
} }
void state::setfield(int index, const char *k) void state::setfield(int index, const char *k)
{ {
checkstack(1); checkstack(1);
index = absindex(index); index = absindex(index);
pushstring(k); pushstring(k);
insert(-2); insert(-2);
settable(index); settable(index);
} }
void state::settable(int index) void state::settable(int index)
{ {
checkstack(2); checkstack(2);
pushvalue(index); pushvalue(index);
insert(-3); insert(-3);
lua_pushcfunction(cobj.get(), (&safe_misc_trampoline<&lua_settable, 0>)); lua_pushcfunction(cobj.get(), (&safe_misc_trampoline<&lua_settable, 0>));
insert(-4); insert(-4);
call(3, 0, 0); call(3, 0, 0);
} }
std::string state::tostring(int index) throw(lua::not_string_error) std::string state::tostring(int index) throw(lua::not_string_error)
{ {
size_t len; size_t len;
const char *str = lua_tolstring(cobj.get(), index, &len); const char *str = lua_tolstring(cobj.get(), index, &len);
if(not str) if(not str)
throw not_string_error(); throw not_string_error();
return std::string(str, len); return std::string(str, len);
} }
} }

View File

@ -29,126 +29,126 @@
#include <lua.hpp> #include <lua.hpp>
namespace lua { namespace lua {
class state; class state;
typedef lua_Integer integer; typedef lua_Integer integer;
typedef lua_Number number; typedef lua_Number number;
typedef std::function<int(state *)> cpp_function; typedef std::function<int(state *)> cpp_function;
enum { enum {
ENVIRONINDEX = LUA_ENVIRONINDEX, ENVIRONINDEX = LUA_ENVIRONINDEX,
GLOBALSINDEX = LUA_GLOBALSINDEX, GLOBALSINDEX = LUA_GLOBALSINDEX,
REGISTRYINDEX = LUA_REGISTRYINDEX REGISTRYINDEX = LUA_REGISTRYINDEX
}; };
enum { enum {
GCSTOP = LUA_GCSTOP, GCSTOP = LUA_GCSTOP,
GCRESTART = LUA_GCRESTART, GCRESTART = LUA_GCRESTART,
GCCOLLECT = LUA_GCCOLLECT, GCCOLLECT = LUA_GCCOLLECT,
GCCOUNT = LUA_GCCOUNT, GCCOUNT = LUA_GCCOUNT,
GCCOUNTB = LUA_GCCOUNTB, GCCOUNTB = LUA_GCCOUNTB,
GCSTEP = LUA_GCSTEP, GCSTEP = LUA_GCSTEP,
GCSETPAUSE = LUA_GCSETPAUSE, GCSETPAUSE = LUA_GCSETPAUSE,
GCSETSTEPMUL = LUA_GCSETSTEPMUL GCSETSTEPMUL = LUA_GCSETSTEPMUL
}; };
enum { enum {
MULTRET = LUA_MULTRET MULTRET = LUA_MULTRET
}; };
enum { enum {
TBOOLEAN = LUA_TBOOLEAN, TBOOLEAN = LUA_TBOOLEAN,
TFUNCTION = LUA_TFUNCTION, TFUNCTION = LUA_TFUNCTION,
TLIGHTUSERDATA = LUA_TLIGHTUSERDATA, TLIGHTUSERDATA = LUA_TLIGHTUSERDATA,
TNIL = LUA_TNIL, TNIL = LUA_TNIL,
TNONE = LUA_TNONE, TNONE = LUA_TNONE,
TNUMBER = LUA_TNUMBER, TNUMBER = LUA_TNUMBER,
TSTRING = LUA_TSTRING, TSTRING = LUA_TSTRING,
TTABLE = LUA_TTABLE, TTABLE = LUA_TTABLE,
TTHREAD = LUA_TTHREAD, TTHREAD = LUA_TTHREAD,
TUSERDATA = LUA_TUSERDATA TUSERDATA = LUA_TUSERDATA
}; };
// we reserve one upvalue for the function pointer // we reserve one upvalue for the function pointer
inline int upvalueindex(int n) inline int upvalueindex(int n)
{ return lua_upvalueindex(n+1); } { return lua_upvalueindex(n+1); }
/* /*
* Lua error()s are wrapped in this class when rethrown into C++ code. what() returns the * 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 * 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. * pushed into the same state it was generated in.
*/ */
class exception: public std::runtime_error { class exception: public std::runtime_error {
/* /*
* We only allow moving, to avoid complications with multiple references. It shouldn't be * 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. * difficult to modify this to work with copying, if that proves unavoidable.
*/ */
state *L; state *L;
int key; int key;
static std::string get_error_msg(state *L); static std::string get_error_msg(state *L);
exception(const exception &) = delete; exception(const exception &) = delete;
const exception& operator=(const exception &) = delete; const exception& operator=(const exception &) = delete;
public: public:
exception(exception &&other) exception(exception &&other)
: std::runtime_error(std::move(other)), L(other.L), key(other.key) : std::runtime_error(std::move(other)), L(other.L), key(other.key)
{ other.L = NULL; } { other.L = NULL; }
explicit exception(state *l); explicit exception(state *l);
virtual ~exception() throw(); virtual ~exception() throw();
void push_lua_error(state *l); void push_lua_error(state *l);
}; };
class not_string_error: public std::runtime_error { class not_string_error: public std::runtime_error {
public: public:
not_string_error() not_string_error()
: std::runtime_error("Cannot convert value to a string") : std::runtime_error("Cannot convert value to a string")
{} {}
}; };
// the name says it all // the name says it all
class syntax_error: public lua::exception { class syntax_error: public lua::exception {
syntax_error(const syntax_error &) = delete; syntax_error(const syntax_error &) = delete;
const syntax_error& operator=(const syntax_error &) = delete; const syntax_error& operator=(const syntax_error &) = delete;
public: public:
syntax_error(state *L) syntax_error(state *L)
: lua::exception(L) : lua::exception(L)
{} {}
syntax_error(syntax_error &&other) syntax_error(syntax_error &&other)
: lua::exception(std::move(other)) : lua::exception(std::move(other))
{} {}
}; };
// double fault, lua encountered an error while running the error handler function // double fault, lua encountered an error while running the error handler function
class errfunc_error: public lua::exception { class errfunc_error: public lua::exception {
errfunc_error(const errfunc_error &) = delete; errfunc_error(const errfunc_error &) = delete;
const errfunc_error& operator=(const errfunc_error &) = delete; const errfunc_error& operator=(const errfunc_error &) = delete;
public: public:
errfunc_error(state *L) errfunc_error(state *L)
: lua::exception(L) : lua::exception(L)
{} {}
errfunc_error(errfunc_error &&other) errfunc_error(errfunc_error &&other)
: lua::exception(std::move(other)) : lua::exception(std::move(other))
{} {}
}; };
// a fancy wrapper around lua_State // a fancy wrapper around lua_State
class state { class state {
std::shared_ptr<lua_State> cobj; std::shared_ptr<lua_State> cobj;
// destructor for C++ objects stored as lua userdata // destructor for C++ objects stored as lua userdata
template<typename T> template<typename T>
static int destroy_cpp_object(lua_State *l) static int destroy_cpp_object(lua_State *l)
{ {
T *ptr = static_cast<T *>(lua_touserdata(l, -1)); T *ptr = static_cast<T *>(lua_touserdata(l, -1));
assert(ptr); assert(ptr);
try { try {
// throwing exceptions in destructors is a bad idea // throwing exceptions in destructors is a bad idea
// but we catch (and ignore) them, just in case // but we catch (and ignore) them, just in case
@ -156,85 +156,85 @@ namespace lua {
} }
catch(...) { catch(...) {
} }
return 0; return 0;
} }
bool safe_compare(lua_CFunction trampoline, int index1, int index2); bool safe_compare(lua_CFunction trampoline, int index1, int index2);
public: public:
state(); state();
/* /*
* Lua functions come in three flavours * Lua functions come in three flavours
* a) functions that never throw an exception * a) functions that never throw an exception
* b) functions that throw only in case of a memory allocation error * b) functions that throw only in case of a memory allocation error
* c) functions that throw other kinds of errors * c) functions that throw other kinds of errors
* *
* Calls to type a functions are simply forwarded to the C api. * 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() * 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 * over us (and our destructors). This add a certain amount overhead. If you care about
* performance, try using the raw versions (if possible). * performance, try using the raw versions (if possible).
* Type b functions are not executed in protected mode atm. as memory allocation errors * 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 * 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 * a metamethod that throws an error). That means those errors will do something
* undefined, but hopefully that won't be a problem. * undefined, but hopefully that won't be a problem.
* *
* Semantics are mostly identical to those of the underlying C api. Any deviation is * 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 * 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++ * 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 * 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 * catch it in lua (with pcall). Lua error()s can be caught in C++ as exceptions of type
* lua::exception. * lua::exception.
*/ */
// type a, never throw // type a, never throw
int absindex(int index) throw() { return index<0 && -index<=gettop() ? gettop()+1+index : index; } 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); } bool getmetatable(int index) throw() { return lua_getmetatable(cobj.get(), index); }
int gettop() throw() { return lua_gettop(cobj.get()); } int gettop() throw() { return lua_gettop(cobj.get()); }
void insert(int index) throw() { lua_insert(cobj.get(), index); } void insert(int index) throw() { lua_insert(cobj.get(), index); }
bool isfunction(int index) throw() { return lua_isfunction(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 islightuserdata(int index) throw() { return lua_islightuserdata(cobj.get(), index); }
bool isnone(int index) throw() { return lua_isnone(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 isnumber(int index) throw() { return lua_isnumber(cobj.get(), index); }
bool isstring(int index) throw() { return lua_isstring(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 pop(int n = 1) throw() { lua_pop(cobj.get(), n); }
void pushboolean(bool b) throw() { lua_pushboolean(cobj.get(), b); } void pushboolean(bool b) throw() { lua_pushboolean(cobj.get(), b); }
void pushinteger(integer n) throw() { lua_pushinteger(cobj.get(), n); } void pushinteger(integer n) throw() { lua_pushinteger(cobj.get(), n); }
void pushlightuserdata(void *p) throw() { lua_pushlightuserdata(cobj.get(), p); } void pushlightuserdata(void *p) throw() { lua_pushlightuserdata(cobj.get(), p); }
void pushnil() throw() { lua_pushnil(cobj.get()); } void pushnil() throw() { lua_pushnil(cobj.get()); }
void pushnumber(number n) throw() { lua_pushnumber(cobj.get(), n); } void pushnumber(number n) throw() { lua_pushnumber(cobj.get(), n); }
void pushvalue(int index) throw() { lua_pushvalue(cobj.get(), index); } void pushvalue(int index) throw() { lua_pushvalue(cobj.get(), index); }
void rawget(int index) throw() { lua_rawget(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); } 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); } bool rawequal(int index1, int index2) throw() { return lua_rawequal(cobj.get(), index1, index2); }
void replace(int index) throw() { lua_replace(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 :/ // lua_setmetatable returns int, but docs don't specify it's meaning :/
int setmetatable(int index) throw() { return lua_setmetatable(cobj.get(), index); } int setmetatable(int index) throw() { return lua_setmetatable(cobj.get(), index); }
integer tointeger(int index) throw() { return lua_tointeger(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); } number tonumber(int index) throw() { return lua_tonumber(cobj.get(), index); }
void* touserdata(int index) throw() { return lua_touserdata(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); } int type(int index) throw() { return lua_type(cobj.get(), index); }
// typename is a reserved word :/ // typename is a reserved word :/
const char* type_name(int tp) throw() { return lua_typename(cobj.get(), tp); } 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); } void unref(int t, int ref) throw() { return luaL_unref(cobj.get(), t, ref); }
// type b, throw only on memory allocation errors // type b, throw only on memory allocation errors
// checkstack correctly throws bad_alloc, because lua_checkstack kindly informs us of // checkstack correctly throws bad_alloc, because lua_checkstack kindly informs us of
// that sitution // that sitution
void checkstack(int extra) throw(std::bad_alloc); void checkstack(int extra) throw(std::bad_alloc);
bool newmetatable(const char *tname) { return luaL_newmetatable(cobj.get(), tname); } bool newmetatable(const char *tname) { return luaL_newmetatable(cobj.get(), tname); }
void newtable() { lua_newtable(cobj.get()); } void newtable() { lua_newtable(cobj.get()); }
void *newuserdata(size_t size) { return lua_newuserdata(cobj.get(), size); } void *newuserdata(size_t size) { return lua_newuserdata(cobj.get(), size); }
// cpp_function can be anything that std::function can handle, everything else remains // cpp_function can be anything that std::function can handle, everything else remains
// identical // identical
void pushclosure(const cpp_function &fn, int n); void pushclosure(const cpp_function &fn, int n);
void pushfunction(const cpp_function &fn) { pushclosure(fn, 0); } void pushfunction(const cpp_function &fn) { pushclosure(fn, 0); }
void pushstring(const char *s) { lua_pushstring(cobj.get(), s); } void pushstring(const char *s) { lua_pushstring(cobj.get(), s); }
void rawgetfield(int index, const char *k) throw(std::bad_alloc); void rawgetfield(int index, const char *k) throw(std::bad_alloc);
void rawset(int index) { lua_rawset(cobj.get(), index); } void rawset(int index) { lua_rawset(cobj.get(), index); }
void rawsetfield(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); } int ref(int t) { return luaL_ref(cobj.get(), t); }
// len recieves length, if not null. Returned value may contain '\0' // 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); } const char* tocstring(int index, size_t *len = NULL) { return lua_tolstring(cobj.get(), index, len); }
// Don't use pushclosure() to create a __gc function. The problem is that lua calls them // Don't use pushclosure() to create a __gc function. The problem is that lua calls them
// in an unspecified order, and we may end up destroying the object holding the // in an unspecified order, and we may end up destroying the object holding the
// std::function before we get a chance to call it. This pushes a function that simply // std::function before we get a chance to call it. This pushes a function that simply
@ -243,54 +243,54 @@ namespace lua {
void pushdestructor() void pushdestructor()
{ lua_pushcfunction(cobj.get(), &destroy_cpp_object<T>); } { lua_pushcfunction(cobj.get(), &destroy_cpp_object<T>); }
// type c, throw everything but the kitchen sink // type c, throw everything but the kitchen sink
// call() is a protected mode call, we don't allow unprotected calls // call() is a protected mode call, we don't allow unprotected calls
void call(int nargs, int nresults, int errfunc = 0); void call(int nargs, int nresults, int errfunc = 0);
void concat(int n); void concat(int n);
bool equal(int index1, int index2); bool equal(int index1, int index2);
int gc(int what, int data); int gc(int what, int data);
void getfield(int index, const char *k); void getfield(int index, const char *k);
void gettable(int index); void gettable(int index);
void getglobal(const char *name) { getfield(GLOBALSINDEX, name); } void getglobal(const char *name) { getfield(GLOBALSINDEX, name); }
bool lessthan(int index1, int index2); bool lessthan(int index1, int index2);
void loadstring(const char *s) throw(lua::syntax_error, std::bad_alloc); void loadstring(const char *s) throw(lua::syntax_error, std::bad_alloc);
bool next(int index); bool next(int index);
// register is a reserved word :/ // register is a reserved word :/
void register_fn(const char *name, const cpp_function &f) { pushfunction(f); setglobal(name); } void register_fn(const char *name, const cpp_function &f) { pushfunction(f); setglobal(name); }
void setfield(int index, const char *k); void setfield(int index, const char *k);
void setglobal(const char *name) { setfield(GLOBALSINDEX, name); } void setglobal(const char *name) { setfield(GLOBALSINDEX, name); }
void settable(int index); void settable(int index);
// lua_tostring uses NULL to indicate conversion error, since there is no such thing as a // 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' // NULL std::string, we throw an exception. Returned value may contain '\0'
std::string tostring(int index) throw(lua::not_string_error); 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 * 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 * 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: * values to pop(). That can be later changed with the overloaded operators. The idiom is:
* stack_sentry s(L); * stack_sentry s(L);
* L.an_operation_that_pushes_something(); ++s; * L.an_operation_that_pushes_something(); ++s;
* ... * ...
*/ */
class stack_sentry { class stack_sentry {
state *L; state *L;
int n; int n;
stack_sentry(const stack_sentry &) = delete; stack_sentry(const stack_sentry &) = delete;
const stack_sentry& operator=(const stack_sentry &) = delete; const stack_sentry& operator=(const stack_sentry &) = delete;
public: public:
explicit stack_sentry(state &l, int n_ = 0) throw() explicit stack_sentry(state &l, int n_ = 0) throw()
: L(&l), n(n_) : L(&l), n(n_)
{} {}
~stack_sentry() throw() { L->pop(n); } ~stack_sentry() throw() { L->pop(n); }
void operator++() throw() { ++n; } void operator++() throw() { ++n; }
void operator--() throw() { --n; } void operator--() throw() { --n; }
void operator+=(int n_) throw() { n+=n_; } void operator+=(int n_) throw() { n+=n_; }
void operator-=(int n_) throw() { n-=n_; } void operator-=(int n_) throw() { n-=n_; }
}; };
} }
#endif /* LUAMM_HH */ #endif /* LUAMM_HH */