X-Git-Url: https://git.dogcows.com/gitweb?p=chaz%2Fyoink;a=blobdiff_plain;f=src%2Fmoof%2Fscript.hh;h=35e80d22eb6a8e57cec4d2b81fc148249888b863;hp=85973dd0402f1823434ced4b2da57305b046146d;hb=6f1b787a10d8ab1a3117a4b8c004dd2d90599608;hpb=831f04d4bc19a390415ac0bbac4331c7a65509bc diff --git a/src/moof/script.hh b/src/moof/script.hh index 85973dd..35e80d2 100644 --- a/src/moof/script.hh +++ b/src/moof/script.hh @@ -14,7 +14,7 @@ /** * \file script.hh - * A thin wrapper over Lua. This is not meant as a complicated binding + * A thin wrapper over Lua 5.1. This is not meant as a complicated binding * package between C++ and Lua. It is not meant to obscure the division * between C++ and Lua but rather to clarify it and make it more * manageable. It does not hide the concept of the Lua stack, but rather @@ -22,10 +22,13 @@ * providing a cleaner, more consistent API. */ +#include #include -#include #include +#include #include +#include +#include #include #include @@ -46,6 +49,7 @@ class script public: typedef boost::function function; + typedef int (*cfunction)(script&); enum status { @@ -65,6 +69,15 @@ public: globals_index = LUA_GLOBALSINDEX }; + + template + static int object_finalizer(script& script) + { + reinterpret_cast(script[1].get_data())->~T(); + return 0; + } + + /** * This is the most prominent abstraction on top of the standard Lua * API. A slot object represents a value on the stack. More @@ -74,14 +87,12 @@ public: * value of which will change as things are pushed onto and popped from * the stack). */ - struct slot { /** * You have direct access to the index of the value on the stack * being represented. */ - int index; @@ -99,140 +110,128 @@ public: thread = LUA_TTHREAD }; + static std::string type_name(type type) + { + switch (type) + { + case none: return "none"; + case nil: return "nil"; + case boolean: return "boolean"; + case light_data: + case data: return "userdata"; + case number: return "number"; + case string: return "string"; + case table: return "table"; + case function: return "function"; + case thread: return "thread"; + } + return "?"; + } + slot(const class script& s, int i = 0) : index(i), - script_(const_cast(s)) {} - - /** - * A copied value presently points to the same value, except the - * real index is used. That means that if a value that refers to a - * frame referenced from the top of the stack will have its - * normalized index copied into the new value object. - */ - - //slot(const slot& copy) : - //index(copy.positiveIndex()), - //script_(copy.script_) {} + script_(const_cast(&s)) {} // check the type of the value bool is_boolean() const - { return (bool)lua_isboolean(script_.state_, index); } + { return (bool)lua_isboolean(script_->state_, index); } bool is_imported_function() const - { return (bool)lua_iscfunction(script_.state_, index); } + { return (bool)lua_iscfunction(script_->state_, index); } bool is_function() const - { return (bool)lua_isfunction(script_.state_, index); } + { return (bool)lua_isfunction(script_->state_, index); } bool is_nil() const - { return (bool)lua_isnil(script_.state_, index); } + { return (bool)lua_isnil(script_->state_, index); } bool is_none() const - { return (bool)lua_isnone(script_.state_, index); } + { return (bool)lua_isnone(script_->state_, index); } bool is_none_or_nil() const - { return (bool)lua_isnoneornil(script_.state_, index); } + { return (bool)lua_isnoneornil(script_->state_, index); } bool is_number() const - { return (bool)lua_isnumber(script_.state_, index); } + { return (bool)lua_isnumber(script_->state_, index); } bool is_string() const - { return (bool)lua_isstring(script_.state_, index); } + { return (bool)lua_isstring(script_->state_, index); } bool is_table() const - { return (bool)lua_istable(script_.state_, index); } + { return (bool)lua_istable(script_->state_, index); } bool is_thread() const - { return (bool)lua_isthread(script_.state_, index); } + { return (bool)lua_isthread(script_->state_, index); } bool is_data() const - { return (bool)lua_isuserdata(script_.state_, index); } + { return (bool)lua_isuserdata(script_->state_, index); } bool is_light_data() const - { return (bool)lua_islightuserdata(script_.state_, index); } + { return (bool)lua_islightuserdata(script_->state_, index); } /** * Check the value and throw an error if its the wrong type. - * There's a little caveat: This method never returns because it - * does a long jump. Consequently, constructed C++ objects which - * exist on the stack between the current frame and some lua - * function will not be destructed. That's not a problem for - * objects that only exist on the stack, but any objects that - * allocate memory on the heap (such as containers or strings) will - * leak. Therefore, you should only call this method after - * cleaning up such objects. The best thing to do for defining - * functions is to simply check all the parameters at the get-go - * before any C++ objects are even constructed. */ - - void require_type(type t) const + const slot& require_type(type t) const { - if (t != type()) - { - luaL_typerror(script_.state_, index, - lua_typename(script_.state_, t)); - } + if (t != type()) raise_type_error(type_name(t)); + return *this; } - void raise(const char* error) + const slot& require_boolean(const std::string& what = "boolean") const { - luaL_argerror(script_.state_, index, error); + if (!is_boolean()) raise_type_error(what); + return *this; } - - - slot& require_boolean() + const slot& require_number(const std::string& what = "number") const { - if (!is_boolean()) - { - luaL_typerror(script_.state_, index, "boolean"); - } + if (!is_number()) raise_type_error(what); return *this; } - slot& require_number() + const slot& require_string(const std::string& what = "string") const { - if (!is_number()) - { - luaL_typerror(script_.state_, index, "number"); - } + if (!is_string()) raise_type_error(what); return *this; } - slot& require_string() + const slot& require_table(const std::string& what = "table") const { - if (!is_string()) - { - luaL_typerror(script_.state_, index, "string"); - } + if (!is_table()) raise_type_error(what); return *this; } - slot& require_table() + const slot& require_function(const std::string& what = "function") const { - if (!is_table()) - { - luaL_typerror(script_.state_, index, "table"); - } + if (!is_function()) raise_type_error(what); return *this; } - slot& require_function() + const slot& require_data(const std::string& what = "userdata") const { - if (!is_function()) - { - luaL_typerror(script_.state_, index, "function"); - } + if (!is_data()) raise_type_error(what); return *this; } - slot& require_data() + const slot& require_nil(const std::string& what = "nil") const { - if (!is_data()) - { - luaL_typerror(script_.state_, index, "data"); - } + if (!is_nil()) raise_type_error(what); return *this; } - slot& require_nil() + const slot& require_thread(const std::string& what = "thread") const { - if (!is_nil()) - { - luaL_typerror(script_.state_, index, "nil"); - } + if (!is_thread()) raise_type_error(what); return *this; } - slot& require_thread() + + template + const slot& require_object(const std::string& what = typeid(T).name()) const { - if (!is_thread()) + if (!is_data()) raise_type_error(what); + + slot metatable = push_metatable(); + if (!metatable.is_table()) { - luaL_typerror(script_.state_, index, "thread"); + metatable.pop(); + raise_type_error(what); } + + slot type = metatable.push_field("__cxxtype"); + std::type_info* typeinfo; + if (!type.get(typeinfo)) + { + metatable.pop(); + raise_type_error(what); + } + + metatable.pop(); + if (*typeinfo != typeid(T)) raise_type_error(what); return *this; } @@ -240,19 +239,38 @@ public: /** * Get the type of the value. */ - enum type type() const { - return (enum type)lua_type(script_.state_, index); + return (enum type)lua_type(script_->state_, index); } /** * Get the name of the type of the value as a string. */ - std::string type_name() const { - return std::string(luaL_typename(script_.state_, index)); + if (is_none()) return "none"; + else if (is_data() && !is_light_data()) + { + slot metatable = push_metatable(); + if (!metatable.is_table()) + { + metatable.pop(); + return "userdata"; + } + + slot type = metatable.push_field("__cxxtype"); + std::type_info* typeinfo; + if (!type.get(typeinfo)) + { + metatable.pop(); + return "userdata"; + } + + metatable.pop(); + return typeinfo->name(); + } + return luaL_typename(script_->state_, index); } @@ -260,15 +278,19 @@ public: * Get the length of the value according to the definition given by * Lua. */ + size_t size() const + { + return lua_objlen(script_->state_, index); + } size_t length() const { - return lua_objlen(script_.state_, index); + return size(); } - int positiveIndex() const + int positive_index() const { - if (index < 0) return index + lua_gettop(script_.state_) + 1; + if (index < 0) return index + lua_gettop(script_->state_) + 1; else return index; } @@ -277,15 +299,14 @@ public: * Get a pointer value (for userdata, tables, threads, and * functions). */ - const void* id() const { - return lua_topointer(script_.state_, index); + return lua_topointer(script_->state_, index); } bool is_identical(const slot& rhs) const { - return &script_ == &(rhs.script_) && index == rhs.index; + return script_ == rhs.script_ && index == rhs.index; } operator bool () const @@ -296,41 +317,24 @@ public: bool operator == (const slot& rhs) const { - return (bool)lua_equal(script_.state_, index, rhs.index); - } - bool operator != (const slot& rhs) const - { - return !(*this == rhs); + return (bool)lua_equal(script_->state_, index, rhs.index); } bool operator < (const slot& rhs) const { - return (bool)lua_lessthan(script_.state_, index, rhs.index); - } - bool operator <= (const slot& rhs) const - { - return *this < rhs || *this == rhs; - } - bool operator > (const slot& rhs) const - { - return !(*this <= rhs); - } - bool operator >= (const slot& rhs) const - { - return !(*this < rhs); + return (bool)lua_lessthan(script_->state_, index, rhs.index); } /** * Convert the underlying value to a C++ type. */ - template bool get(T& value) const { if (is_number()) { - value = (T)lua_tointeger(script_.state_, index); + value = (T)lua_tointeger(script_->state_, index); return true; } return false; @@ -340,7 +344,7 @@ public: { if (is_number()) { - value = (float)lua_tonumber(script_.state_, index); + value = (float)lua_tonumber(script_->state_, index); return true; } return false; @@ -349,7 +353,7 @@ public: { if (is_number()) { - value = (double)lua_tonumber(script_.state_, index); + value = (double)lua_tonumber(script_->state_, index); return true; } return false; @@ -359,7 +363,7 @@ public: { if (is_boolean()) { - value = (bool)lua_toboolean(script_.state_, index); + value = (bool)lua_toboolean(script_->state_, index); return true; } return false; @@ -369,7 +373,7 @@ public: { if (is_string()) { - value = lua_tolstring(script_.state_, index, &size); + value = lua_tolstring(script_->state_, index, &size); return true; } return false; @@ -387,15 +391,20 @@ public: return false; } - bool get(void*& value) const + template + bool get(T*& value) const { if (is_data()) { - value = lua_touserdata(script_.state_, index); + value = reinterpret_cast(lua_touserdata(script_->state_, index)); return true; } return false; } + void* get_data() const + { + return lua_touserdata(script_->state_, index); + } template bool get(std::vector& array) const @@ -404,19 +413,19 @@ public: array.clear(); - slot value = script_[-1]; - int realIndex = positiveIndex(); + slot value = (*script_)[-1]; + int realIndex = positive_index(); bool done = false; for (int i = 1; !done; ++i) { - lua_rawgeti(script_.state_, realIndex, i); + lua_rawgeti(script_->state_, realIndex, i); T v; if (value.get(v)) array.push_back(v); else done = true; - script_.pop(); + script_->pop(); } return true; @@ -429,12 +438,12 @@ public: dictionary.clear(); - slot key = script_[-2]; - slot value = script_[-1]; - int realIndex = positiveIndex(); + slot key = (*script_)[-2]; + slot value = (*script_)[-1]; + int realIndex = positive_index(); - script_.push_nil(); - while (lua_next(script_.state_, realIndex) != 0) + script_->push_nil(); + while (lua_next(script_->state_, realIndex) != 0) { std::string k; if (!key.is_number() && key.get(k)) @@ -442,9 +451,9 @@ public: T v; if (value.get(v)) dictionary[k] = v; } - script_.pop(); + script_->pop(); } - script_.pop(); + script_->pop(); return true; } @@ -452,42 +461,58 @@ public: /** * Get the value of a field from the table. */ - template - bool get(T& value, V field) const + bool get(T& value, const V& field) const { bool ret = push_field(field).get(value); - script_.pop(); + script_->pop(); return ret; } template - void set_field(T field, V value) + void set_field(const T& field, const V& value) { - script_.push(field); - script_.push(value); + script_->push(field); + script_->push(value); set_field(); } void set_field() { - lua_settable(script_.state_, index); + lua_settable(script_->state_, index); + } + + + void set_field(const std::string& field) + { + set_field(field.c_str()); + } + void set_field(const char* field) + { + lua_setfield(script_->state_, index, field); } template - void set_field(const std::string& field, T value) + void set_field(const std::string& field, const T& value) { set_field(field.c_str(), value); } template - void set_field(const char* field, T value) + void set_field(const char* field, const T& value) { - script_.push(value); - lua_setfield(script_.state_, index, field); + script_->push(value); + set_field(field); } + /** + * Set the top value to be the metatable of this value. + */ + void set_metatable() + { + lua_setmetatable(script_->state_, index); + } /** * This set method, as opposed to the others, sets the value of the @@ -496,80 +521,81 @@ public: template void set(T value) { - script_.push(value); - replace(); + script_->push(value); + set(); } - void set() - { - replace(); - } - - /** * Replace this value with the value at the top of the stack. */ + void set() + { + lua_replace(script_->state_, index); + } - void replace() + void set_nil() { - lua_replace(script_.state_, index); + script_->push_nil(); + set(); } + void remove() { - lua_remove(script_.state_, index); + lua_remove(script_->state_, index); } + /** + * Remove this value and everything above it. + */ void pop() { - // removes this slot, taking with it everything above it - script_.pop(script_.stack_size() - index + 1); + if (index < 0) script_->pop(-index); + else script_->pop(script_->stack_size() - index + 1); } /** * Inserts the top-most value on the stack at position index, * shifting other values as needed. */ - void insert_top_here() { - lua_insert(script_.state_, index); + lua_insert(script_->state_, index); } /** * Copy the value and push the copy to the stack. */ - slot push_copy() const { - lua_pushvalue(script_.state_, index); - return script_.top(); + lua_pushvalue(script_->state_, index); + return script_->top(); } slot push_metatable() const { - lua_getmetatable(script_.state_, index); - return script_.top(); + lua_getmetatable(script_->state_, index); + return script_->top(); } slot push_environment() const { - lua_getfenv(script_.state_, index); - return script_.top(); + lua_getfenv(script_->state_, index); + return script_->top(); } slot push_field() const { - lua_gettable(script_.state_, index); - return script_.top(); + lua_gettable(script_->state_, index); + return script_->top(); } template slot push_field(T index) const { - script_.push(index); + script_->push(index); return push_field(); } @@ -579,25 +605,64 @@ public: } slot push_field(const char* name) const { - lua_getfield(script_.state_, index, name); - return script_.top(); + lua_getfield(script_->state_, index, name); + return script_->top(); } class script& script() { - return script_; + return *script_; } const class script& script() const { - return script_; + return *script_; + } + + + /** + * Throw an exception with a message formatted to communicate a + * type mismatch with the argument represented by this slot. + */ + int raise_type_error(const std::string& expected) const + { + lua_Debug ar; + lua_getstack(script_->state_, 0, &ar); + lua_getinfo(script_->state_, "n", &ar); + const char* func = ar.name ? ar.name : "unknown function"; + + std::ostringstream stream; + stream << "bad argument " << index << " to '" << func + << "' (" << expected << " expected, got " + << type_name() << ")"; + + throw std::invalid_argument(stream.str()); + return 0; + } + + /** + * Throw a generic error concerning this particular slot. + */ + int raise(const std::string& message) const + { + lua_Debug ar; + lua_getstack(script_->state_, 0, &ar); + lua_getinfo(script_->state_, "n", &ar); + const char* func = ar.name ? ar.name : "unknown function"; + + std::ostringstream stream; + stream << "bad argument " << index << " to '" << func + << "' (" << message << ")"; + + throw std::invalid_argument(stream.str()); + return 0; } private: - class script& script_; + mutable class script* script_; }; @@ -622,7 +687,6 @@ public: { if (state_) destroy(); state_ = luaL_newstate(); - registry().set_field("Script_hh_Object", (void*)this); } @@ -631,58 +695,84 @@ public: luaL_openlibs(state_); } + void import_safe_standard_libraries() + { + import_base_library(); + import_string_library(); + import_table_library(); + import_math_library(); + import_os_library(); + import_debug_library(); + + slot g = globals(); + + push_nil(); g.set_field("dofile"); + push_nil(); g.set_field("loadfile"); + push_nil(); g.set_field("require"); + push_nil(); g.set_field("io"); + push_nil(); g.set_field("package"); + slot os = g.push_field("os"); + push_nil(); os.set_field("execute"); + push_nil(); os.set_field("exit"); + push_nil(); os.set_field("getenv"); + push_nil(); os.set_field("remove"); + push_nil(); os.set_field("rename"); + push_nil(); os.set_field("tmpname"); + pop(); + } + void import_base_library() { - lua_pushcfunction(state_, luaopen_base); + push(luaopen_base); push(LUA_COLIBNAME); call(1, 0); } void import_package_library() { - lua_pushcfunction(state_, luaopen_package); + push(luaopen_package); push(LUA_LOADLIBNAME); call(1, 0); } void import_string_library() { - lua_pushcfunction(state_, luaopen_string); + push(luaopen_string); push(LUA_STRLIBNAME); call(1, 0); } void import_table_library() { - lua_pushcfunction(state_, luaopen_table); + push(luaopen_table); push(LUA_TABLIBNAME); call(1, 0); } void import_math_library() { - lua_pushcfunction(state_, luaopen_math); + push(luaopen_math); push(LUA_MATHLIBNAME); call(1, 0); } void import_io_library() { - lua_pushcfunction(state_, luaopen_io); + push(luaopen_io); push(LUA_IOLIBNAME); call(1, 0); } void import_os_library() { - lua_pushcfunction(state_, luaopen_os); + push(luaopen_os); push(LUA_OSLIBNAME); call(1, 0); } void import_debug_library() { - lua_pushcfunction(state_, luaopen_debug); + push(luaopen_debug); push(LUA_DBLIBNAME); call(1, 0); } @@ -705,7 +795,7 @@ public: } - /** + /* * Thread-handling methods. */ @@ -714,9 +804,10 @@ public: return script(state_); } - void push_thread() + slot push_thread() { lua_pushthread(state_); + return top(); } status resume(int nargs) @@ -724,7 +815,7 @@ public: return status(lua_resume(state_, nargs)); } - status getStatus() const + status thread_status() const { return status(lua_status(state_)); } @@ -736,28 +827,36 @@ public: bool is_main_thread() const { - return is_main_thread_; + bool is_main = lua_pushthread(state_); + lua_pop(state_, 1); + return is_main; } /** - * Throw an error with the value at the top of the stack. This method - * never returns because it does a long jump. Consequently, - * constructed C++ objects which exist on the stack between the - * current frame and some lua function will not be destructed. That's - * not a problem for objects that only exist on the stack, but any - * objects that allocate memory on the heap (such as containers or - * strings) will leak. Therefore, you should only call this method - * after cleaning up such objects. + * Throw an error with the value at the top of the stack. If this is + * called from an imported function, the error will be caught and + * returned on the stack when the execution aborts. */ + int raise() + { + throw std::runtime_error(""); + return 0; + } - void raise() + /** + * Throw an error with a given message describing the problem. If this + * is called from an imported function, the error will be caught and + * returned on the stack when the execution aborts. + */ + int raise(const std::string& message) { - lua_error(state_); + throw std::runtime_error(message); + return 0; } - /** + /* * Get significant values. */ @@ -817,7 +916,7 @@ public: * enough to hold whatever you want to push on it. This is usually * only an issue if you're pushing stuff in a loop. */ - bool checkStack(int extra) + bool check_stack(int extra) { return (bool)lua_checkstack(state_, extra); } @@ -832,11 +931,56 @@ public: } - /** + /* * Push some values onto the stack. */ - template - slot push(T value) + + slot push(char value) + { + lua_pushinteger(state_, lua_Integer(value)); + return top(); + } + slot push(unsigned char value) + { + lua_pushinteger(state_, lua_Integer(value)); + return top(); + } + slot push(short value) + { + lua_pushinteger(state_, lua_Integer(value)); + return top(); + } + slot push(unsigned short value) + { + lua_pushinteger(state_, lua_Integer(value)); + return top(); + } + slot push(int value) + { + lua_pushinteger(state_, lua_Integer(value)); + return top(); + } + slot push(unsigned int value) + { + lua_pushinteger(state_, lua_Integer(value)); + return top(); + } + slot push(long value) + { + lua_pushinteger(state_, lua_Integer(value)); + return top(); + } + slot push(unsigned long value) + { + lua_pushinteger(state_, lua_Integer(value)); + return top(); + } + slot push(long long value) + { + lua_pushinteger(state_, lua_Integer(value)); + return top(); + } + slot push(unsigned long long value) { lua_pushinteger(state_, lua_Integer(value)); return top(); @@ -850,12 +994,12 @@ public: slot push(float value) { - lua_pushnumber(state_, (lua_Number)value); + lua_pushnumber(state_, lua_Number(value)); return top(); } slot push(double value) { - lua_pushnumber(state_, (lua_Number)value); + lua_pushnumber(state_, lua_Number(value)); return top(); } @@ -877,18 +1021,56 @@ public: slot push(const function& function) { - functions_.push_back(function); - lua_pushlightuserdata(state_, (void*)&functions_.back()); - lua_pushcclosure(state_, dispatch_call, 1); + push(function); + push_pointer(this); + push(call_functor, 2); return top(); } - - slot push(void* data) + slot push(cfunction function) { - lua_pushlightuserdata(state_, data); + push_pointer(function); + push_pointer(this); + push(call_function, 2); return top(); } + template + slot push(const T& object) + { + void* storage; + slot copy = push_data(storage, sizeof(T)); + new(storage) T(object); + + push_class_metatable(); + copy.set_metatable(); + return copy; + } + + template + slot push_class(const function& ctor) + { + slot metatable = push_class_metatable(); + + slot constructor = push_table(); + push(ctor); + constructor.set_field("__call"); + metatable.set_metatable(); + + return metatable; + } + template + slot push_class(cfunction ctor) + { + slot metatable = push_class_metatable(); + + slot constructor = push_table(); + push(ctor); + constructor.set_field("__call"); + metatable.set_metatable(); + + return metatable; + } + slot push_nil() { lua_pushnil(state_); @@ -917,18 +1099,59 @@ public: return top(); } - slot push_new_data(void*& data, size_t size) + slot push_data(void*& data, size_t size) { data = lua_newuserdata(state_, size); return top(); } - slot push_new_table(int narr = 0, int nrec = 0) + template + slot push_pointer(const T* ptr) + { + lua_pushlightuserdata(state_, const_cast((const void*)ptr)); + return top(); + } + slot push_pointer(cfunction function) + { + return push_pointer((void*)function); + } + + slot push_table(const std::string& name, int narr = 0, int nrec = 0) + { + if (name.empty()) return globals().push_field("_G"); + + slot table = globals().push_field(name); + if (table.is_table()) return table; + + pop(); + push_table(narr, nrec); + globals().set_field(name); + + return globals().push_field(name); + } + slot push_table(int narr = 0, int nrec = 0) { lua_createtable(state_, narr, nrec); return top(); } + slot push_metatable(const std::string& type, bool& is_new) + { + is_new = luaL_newmetatable(state_, type.c_str()); + return top(); + } + slot push_metatable(const std::string& type) + { + luaL_newmetatable(state_, type.c_str()); + return top(); + } + + template + slot push_type() + { + return push_pointer(&typeid(T)); + } + /** * Call a function on the stack. The correct procedure is to push a @@ -936,7 +1159,6 @@ public: * will pop them off upon return, leaving up to nresults return values * (default is any number of return values, depending on the callee). */ - status call(int nargs = 0, int nresults = LUA_MULTRET) { return status(lua_pcall(state_, nargs, nresults, 0)); @@ -946,7 +1168,6 @@ public: /** * Pops n values from the top of the stack. */ - void pop(int n = 1) { lua_pop(state_, n); @@ -956,44 +1177,42 @@ public: /** * Index into the stack to get a slot. */ - slot operator [] (int index) const { return slot(*this, index); } - /** + /* * Control over the garbage collection process. */ - void collect_all() + void collect_garbage() { lua_gc(state_, LUA_GCCOLLECT, 0); } + void collect_garbage(int step) + { + lua_gc(state_, LUA_GCSTEP, step); + } - void stop_collector() + void disable_garbage_collector() { lua_gc(state_, LUA_GCSTOP, 0); } - - void restart_collector() + void enable_garbage_collector() { lua_gc(state_, LUA_GCRESTART, 0); } - int memory_used() const + float memory_used() const { // in kilobytes - return lua_gc(state_, LUA_GCCOUNT, 0); + return lua_gc(state_, LUA_GCCOUNT, 0) + + lua_gc(state_, LUA_GCCOUNTB, 0) / 1024.0f; } - void collect(int step) - { - lua_gc(state_, LUA_GCSTEP, step); - } - - void tune_collector(int pause, int step) + void tune_garbage_collector(int pause, int step = 200) { lua_gc(state_, LUA_GCSETPAUSE, pause); lua_gc(state_, LUA_GCSETSTEPMUL, step); @@ -1003,32 +1222,139 @@ public: private: script(lua_State* state) : - state_(lua_newthread(state)), - is_main_thread_(false) {} + state_(lua_newthread(state)) {} + + slot push(lua_CFunction function, int upvalues = 0) + { + lua_pushcclosure(state_, function, upvalues); + return top(); + } + + template + slot push_class_metatable() + { + bool is_new; + slot metatable = push_metatable(typeid(T).name(), is_new); + if (is_new) + { + metatable.push_copy(); // class behavior + metatable.set_field("__index"); + + push_type(); + metatable.set_field("__cxxtype"); // type_info + + push(object_finalizer_); + metatable.set_field("__gc"); // finalizer + + //push(object_tostring_); + //metatable.set_field("__tostring"); // tostring + } + return metatable; + } + + template + static int object_tostring_(lua_State* state) + { + std::ostringstream stream; + stream << *reinterpret_cast(lua_touserdata(state, 1)); + lua_pushlstring(state, stream.str().c_str(), stream.str().length()); + return 1; + } + + template + static int object_finalizer_(lua_State* state) + { + reinterpret_cast(lua_touserdata(state, 1))->~T(); + return 0; + } - static int dispatch_call(lua_State* state) + static int call_functor(lua_State* state) { - const function* function = (const script::function*)lua_touserdata(state, + function* function = (script::function*)lua_touserdata(state, lua_upvalueindex(1)); - lua_getfield(state, LUA_REGISTRYINDEX, "Script_hh_Object"); - script* script = (moof::script*)lua_touserdata(state, -1); - lua_pop(state, 1); + script* script = (moof::script*)lua_touserdata(state, + lua_upvalueindex(2)); - return (*function)(*script); + try + { + return (*function)(*script); + } + catch (const std::exception& e) + { + if (0 < std::strlen(e.what())) + { + luaL_where(state, 1); + lua_pushstring(state, e.what()); + lua_concat(state, 2); + } + return lua_error(state); + } + catch (const char* e) + { + luaL_where(state, 1); + lua_pushstring(state, e); + lua_concat(state, 2); + return lua_error(state); + } + catch (...) + { + return lua_error(state); + } + } + + + static int call_function(lua_State* state) + { + cfunction function = (cfunction)lua_touserdata(state, + lua_upvalueindex(1)); + + script* script = (moof::script*)lua_touserdata(state, + lua_upvalueindex(2)); + + try + { + return function(*script); + } + catch (const std::exception& e) + { + if (0 < std::strlen(e.what())) + { + luaL_where(state, 1); + lua_pushstring(state, e.what()); + lua_concat(state, 2); + } + return lua_error(state); + } + catch (const char* e) + { + luaL_where(state, 1); + lua_pushstring(state, e); + lua_concat(state, 2); + return lua_error(state); + } + catch (...) + { + return lua_error(state); + } } + void destroy() { - if (is_main_thread_) lua_close(state_); + if (is_main_thread()) lua_close(state_); } + lua_State* state_; - bool is_main_thread_; - std::list functions_; }; +using namespace std::rel_ops; + +/** + * Output a script value to a stream. + */ inline std::ostream& operator << (std::ostream& stream, const script::slot& slot) {