#ifndef _library__lua_base__hpp__included__ #define _library__lua_base__hpp__included__ #include #include #include #include #include #include #include #include #include #include #include "string.hpp" #include "utf8.hpp" #include "int24.hpp" #include "lua-version.hpp" namespace lua { class state; class function; class function_group; class class_group; class class_base; /** * Lua state object. */ class state { public: const static unsigned trampoline_upvals = 2; //Auxillary type for store-tag. template struct _store_tag { T& addr; T val; _store_tag(T& a, T v) : addr(a), val(v) {} }; //Auxillary type for vararg-tag. struct vararg_tag { std::list args; vararg_tag(std::list& _args) : args(_args) {} int pushargs(state& L); }; //Auxillary type for numeric-tag. template struct _numeric_tag { T val; _numeric_tag(T v) : val(v) {} }; //Auxillary type for fnptr-tag. template struct _fnptr_tag { int(*fn)(state& L, T v); T val; _fnptr_tag(int (*f)(state& L, T v), T v) : fn(f), val(v) {} }; //Auxillary type for fn-tag. template struct _fn_tag { T fn; _fn_tag(T f) : fn(f) {} }; /** * Callback parameter: Don't pass any real parameter, but instead store specified value in specified * location. * * Parameter a: The location to store value to. * Parameter v: The value to store. * Returns: The parameter structure. */ template static struct _store_tag store_tag(T& a, T v) { return _store_tag(a, v); } /** * Callback parameter: Pass numeric value. * * Parameter v: The value to pass. * Returns: The parameter structure. */ template static struct _numeric_tag numeric_tag(T v) { return _numeric_tag(v); } /** * Callback parameter: Execute function to push more parameters. * * Parameter f: The function to execute. The return value is number of additional parameters pushed. * Parameter v: The value to pass to function. * Returns: The parameter structure. */ template static struct _fnptr_tag fnptr_tag(int (*f)(state& L, T v), T v) { return _fnptr_tag(f, v); } /** * Callback parameter: Execute function to push more parameters. * * Parameter v: The functor to execute. Passed reference to the Lua state. The return value is number of * additional parameters pushed. * Returns: The parameter structure. */ template static struct _fn_tag fn_tag(T v) { return _fn_tag(v); } /** * Callback parameter: Pass boolean argument. * * Parameter v: The boolean value to pass. */ struct boolean_tag { bool val; boolean_tag(bool v) : val(v) {}}; /** * Callback parameter: Pass string argument. * * Parameter v: The string value to pass. */ struct string_tag { std::string val; string_tag(const std::string& v) : val(v) {}}; /** * Callback parameter: Pass nil argument. */ struct nil_tag { nil_tag() {}}; private: template void _callback(int argc, _store_tag tag, T... args) { tag.addr = tag.val; _callback(argc, args...); tag.addr = NULL; } template void _callback(int argc, vararg_tag tag, T... args) { int e = tag.pushargs(*this); _callback(argc + e, args...); } template void _callback(int argc, nil_tag tag, T... args) { pushnil(); _callback(argc + 1, args...); } template void _callback(int argc, boolean_tag tag, T... args) { pushboolean(tag.val); _callback(argc + 1, args...); } template void _callback(int argc, string_tag tag, T... args) { pushlstring(tag.val); _callback(argc + 1, args...); } template void _callback(int argc, _numeric_tag tag, T... args) { pushnumber(tag.val); _callback(argc + 1, args...); } template void _callback(int argc, _fnptr_tag tag, T... args) { int extra = tag.fn(*this, tag.val); _callback(argc + extra, args...); } template void _callback(int argc, _fn_tag tag, T... args) { int extra = tag.fn(*this); _callback(argc + extra, args...); } void _callback(int argc) { int r = pcall(argc, 0, 0); if(r == LUA_ERRRUN) { (stringfmt() << "Error running Lua callback: " << tostring(-1)).throwex(); pop(1); } if(r == LUA_ERRMEM) { (stringfmt() << "Error running Lua callback: Out of memory").throwex(); pop(1); } if(r == LUA_ERRERR) { (stringfmt() << "Error running Lua callback: Double Fault???").throwex(); pop(1); } } public: /** * Create a new state. */ state(); /** * Create a new state with specified master state. */ state(state& _master, lua_State* L); /** * Destroy a state. */ ~state() throw(); /** * Get the internal state object. * * Return value: Internal state. */ lua_State* handle() { return lua_handle; } /** * Get the master state. */ state& get_master() { return master ? master->get_master() : *this; } /** * Set the internal state object. */ void handle(lua_State* l) { lua_handle = l; } /** * Set OOM handler. */ void set_oom_handler(void (*oom)()) { oom_handler = oom ? oom : builtin_oom; } /** * Set soft OOM handler. */ void set_soft_oom_handler(void (*oom)(int status)) { soft_oom_handler = oom ? oom : builtin_soft_oom; } /** * Set memory use change handler. */ void set_memory_change_handler(std::function cb) { memory_change = cb; } /** * Reset the state. */ void reset(); /** * Deinit the state. */ void deinit() throw(); /** * Create a trampoline. * * Parameter fn The function to execute. * Parameter n_upvals The number of extra upvalues besides the 2 used by trampoline itself. Popped from stack. */ void push_trampoline(int(*fn)(state& L), unsigned n_upvals); /** * Get specified trampoline upvalue index. */ int trampoline_upval(int val) { return lua_upvalueindex(trampoline_upvals + val); } /** * Set value of interruptable flag. * * Parameter flag: The flag. */ void set_interruptable_flag(bool flag) { if(master) master->set_interruptable_flag(flag); else interruptable = flag; } /** * Get interruptable flag. */ bool get_interruptable_flag() { if(master) return master->get_interruptable_flag(); else return interruptable; } /** * Set memory limit. */ void set_memory_limit(size_t limit) { if(master) master->set_memory_limit(limit); else memory_limit = limit; } /** * Get memory limit. */ size_t get_memory_limit() { if(master) return master->get_memory_limit(); else return memory_limit; } /** * Get memory use. */ size_t get_memory_use() { if(master) return master->get_memory_use(); else return memory_use; } /** * Charge against memory limit. */ bool charge_memory(size_t amount, bool release); /** * Execute function in interruptable mode. * * Parameter fn: The function to execute * Parameter in: Number of slots to copy in. * Parameter out: Number of slots to copy out. */ void run_interruptable(std::function fn, unsigned in, unsigned out); /** * Get a string argument. * * Parameter argindex: The stack index. * Parameter fname: The name of function to use in error messages. * Returns: The string. * Throws std::runtime_error: The specified argument is not a string. */ std::string get_string(int argindex, const std::string& fname) { if(isnone(argindex)) (stringfmt() << "argument #" << argindex << " to " << fname << " must be string").throwex(); size_t len; const char* f = lua_tolstring(lua_handle, argindex, &len); if(!f) (stringfmt() << "argument #" << argindex << " to " << fname << " must be string").throwex(); return std::string(f, f + len); } /** * Get a boolean argument. * * Parameter argindex: The stack index. * Parameter fname: The name of function to use in error messages. * Returns: The string. * Throws std::runtime_error: The specified argument is not a boolean. */ bool get_bool(int argindex, const std::string& fname) { if(isnone(argindex) || !isboolean(argindex)) (stringfmt() << "argument #" << argindex << " to " << fname << " must be boolean").throwex(); return (lua_toboolean(lua_handle, argindex) != 0); } /** * Get a mandatory numeric argument. * * Parameter argindex: The stack index. * Parameter fname: The name of function to use in error messages. * Returns: The parsed number. * Throws std::runtime_error: Bad type. */ template T get_numeric_argument(int argindex, const std::string& fname) { if(std::numeric_limits::is_integer) { if(isnone(argindex) || !isinteger(argindex)) (stringfmt() << "Argument #" << argindex << " to " << fname << " must be integer").throwex(); return static_cast(lua_tointeger(lua_handle, argindex)); } else { if(isnone(argindex) || !isnumber(argindex)) (stringfmt() << "Argument #" << argindex << " to " << fname << " must be numeric").throwex(); return static_cast(lua_tonumber(lua_handle, argindex)); } } /** * Get a optional numeric argument. * * Parameter argindex: The stack index. * Parameter value: The place to store the value. * Parameter fname: The name of function to use in error messages. * Throws std::runtime_error: Bad type. */ template void get_numeric_argument(unsigned argindex, T& value, const std::string& fname) { if(isnoneornil(argindex)) return; if(std::numeric_limits::is_integer) { if(isnone(argindex) || !isinteger(argindex)) (stringfmt() << "Argument #" << argindex << " to " << fname << " must be integer if " "present").throwex(); value = static_cast(lua_tointeger(lua_handle, argindex)); } else { if(isnone(argindex) || !isnumber(argindex)) (stringfmt() << "Argument #" << argindex << " to " << fname << " must be numeric if " "present").throwex(); value = static_cast(lua_tonumber(lua_handle, argindex)); } } /** * Do a callback. * * Parameter name: The name of the callback. * Parameter args: Arguments to pass to the callback. */ template bool callback(const std::string& name, T... args) { getglobal(name.c_str()); int t = type(-1); if(t != LUA_TFUNCTION) { pop(1); return false; } _callback(0, args...); return true; } /** * Do a callback. * * Parameter cblist: List of environment keys to do callbacks. * Parameter args: Arguments to pass to the callback. */ template bool callback(const std::list& cblist, T... args) { bool any = false; for(auto& i : cblist) { pushlightuserdata(const_cast(&i)); rawget(LUA_REGISTRYINDEX); int t = type(-1); if(t != LUA_TFUNCTION) { pop(1); } else { _callback(0, args...); any = true; } } return any; } /** * Add a group of functions. */ void add_function_group(function_group& group); /** * Add a group of classes. */ void add_class_group(class_group& group); /** * Function callback. */ void function_callback(const std::string& name, function* func); /** * Class callback. */ void class_callback(const std::string& name, class_base* func); /** * Do something just once per VM. * * Parameter key: The do-once key value. * Returns: True if called the first time for given key on given VM, false otherwise. */ bool do_once(void* key); /** * Callback list. */ class callback_list { public: callback_list(state& L, const std::string& name, const std::string& fn_cbname = ""); ~callback_list(); void _register(state& L); //Reads callback from top of lua stack. void _unregister(state& L); //Reads callback from top of lua stack. template bool callback(T... args) { bool any = L.callback(callbacks, args...); if(fn_cbname != "" && L.callback(fn_cbname, args...)) any = true; return any; } const std::string& get_name() { return name; } void clear() { callbacks.clear(); } private: callback_list(const callback_list&); callback_list& operator=(const callback_list&); std::list callbacks; state& L; std::string name; std::string fn_cbname; }; /** * Enumerate all callbacks. */ std::list get_callbacks(); /** * Register/Unregister a callback list. */ void do_register(const std::string& name, callback_list& callback); void do_unregister(const std::string& name, callback_list& callback); //All kinds of Lua API functions. void pop(int n) { lua_pop(lua_handle, n); } void* newuserdata(size_t size) { return lua_newuserdata(lua_handle, size); } int setmetatable(int index) { return lua_setmetatable(lua_handle, index); } int type(int index) { return lua_type(lua_handle, index); } void replace(int index) { lua_replace(lua_handle, index); } int getmetatable(int index) { return lua_getmetatable(lua_handle, index); } int rawequal(int index1, int index2) { return lua_rawequal(lua_handle, index1, index2); } void* touserdata(int index) { return lua_touserdata(lua_handle, index); } const void* topointer(int index) { return lua_topointer(lua_handle, index); } int gettop() { return lua_gettop(lua_handle); } void pushvalue(int index) { lua_pushvalue(lua_handle, index); } void pushlightuserdata(void* p) { lua_pushlightuserdata(lua_handle, p); } void rawset(int index) { lua_rawset(lua_handle, index); } void pushnil() { lua_pushnil(lua_handle); } void pushstring(const char* s) { lua_pushstring(lua_handle, s); } void rawget(int index) { lua_rawget(lua_handle, index); } int isnil(int index) { return lua_isnil(lua_handle, index); } void newtable() { lua_newtable(lua_handle); } void pushcclosure(lua_CFunction fn, int n) { lua_pushcclosure(lua_handle, fn, n); } void pushcfunction(lua_CFunction fn) { lua_pushcfunction(lua_handle, fn); } void setfield(int index, const char* k) { lua_setfield(lua_handle, index, k); } void getfield(int index, const char* k) { lua_getfield(lua_handle, index, k); } void getglobal(const char* name) { lua_getglobal(lua_handle, name); } void setglobal(const char* name) { lua_setglobal(lua_handle, name); } void insert(int index) { lua_insert(lua_handle, index); } void settable(int index) { lua_settable(lua_handle, index); } int isnone(int index) { return lua_isnone(lua_handle, index); } int isnumber(int index) { return lua_isnumber(lua_handle, index); } int isinteger(int index) { return LUA_INTEGER_POSTFIX(lua_is) (lua_handle, index); } int isboolean(int index) { return lua_isboolean(lua_handle, index); } int toboolean(int index) { return lua_toboolean(lua_handle, index); } const char* tolstring(int index, size_t *len) { return lua_tolstring(lua_handle, index, len); } lua_Number tonumber(int index) { return lua_tonumber(lua_handle, index); } int64_t tointeger(int index) { return LUA_INTEGER_POSTFIX(lua_to) (lua_handle, index); } void gettable(int index) { lua_gettable(lua_handle, index); } int load(lua_Reader reader, void* data, const char* chunkname, const char* mode) { (void)mode; return lua_load(lua_handle, reader, data, chunkname LUA_LOADMODE_ARG(mode) ); } const char* tostring(int index) { return lua_tostring(lua_handle, index); } const char* tolstring(int index, size_t& len) { return lua_tolstring(lua_handle, index, &len); } void pushlstring(const char* s, size_t len) { lua_pushlstring(lua_handle, s, len); } void pushlstring(const std::string& s) { lua_pushlstring(lua_handle, s.c_str(), s.length()); } void pushlstring(const char32_t* s, size_t len) { pushlstring(utf8::to8(std::u32string(s, len))); } int pcall(int nargs, int nresults, int errfunc) { state* master_state = this; while(master_state->master) master_state = master_state->master; //Upon entry to protected mode, interruptable mode is always set, and it is restored on exit //from protected mode. bool old_interruptable = master_state->interruptable; master_state->interruptable = true; auto ret = lua_pcall(lua_handle, nargs, nresults, errfunc); master_state->interruptable = old_interruptable; return ret; } int next(int index) { return lua_next(lua_handle, index); } int isnoneornil(int index) { return lua_isnoneornil(lua_handle, index); } void rawgeti(int index, int n) { lua_rawgeti(lua_handle, index, n); } template void pushnumber(T val) { if(std::numeric_limits::is_integer || is_ss_int24::flag) return LUA_INTEGER_POSTFIX(lua_push) (lua_handle, val); else return lua_pushnumber(lua_handle, val); } void pushboolean(bool b) { lua_pushboolean(lua_handle, b); } void pushglobals() { LUA_LOADGLOBALS } private: static void builtin_oom(); static void builtin_soft_oom(int status); static void* builtin_alloc(void* user, void* old, size_t olds, size_t news); void (*oom_handler)(); void (*soft_oom_handler)(int status); std::function memory_change; state* master; bool interruptable; size_t memory_limit; size_t memory_use; lua_State* lua_handle; state(state&); state& operator=(state&); }; } #endif