]> Dogcows Code - chaz/yoink/blobdiff - src/moof/script.hh
mesh and other random adjustments
[chaz/yoink] / src / moof / script.hh
index 85973dd0402f1823434ced4b2da57305b046146d..35e80d22eb6a8e57cec4d2b81fc148249888b863 100644 (file)
@@ -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
  * providing a cleaner, more consistent API.
  */
 
+#include <cstring>
 #include <iostream>
-#include <list>
 #include <map>
+#include <stdexcept>
 #include <string>
+#include <sstream>
+#include <utility>
 #include <vector>
 
 #include <boost/bind.hpp>
@@ -46,6 +49,7 @@ class script
 public:
 
        typedef boost::function<int(script&)> function;
+       typedef int (*cfunction)(script&);
 
        enum status
        {
@@ -65,6 +69,15 @@ public:
                globals_index           = LUA_GLOBALSINDEX
        };
 
+
+       template <class T>
+       static int object_finalizer(script& script)
+       {
+               reinterpret_cast<T*>(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<class script&>(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<class script*>(&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 <class T>
+               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 <class T>
                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 <class T>
+               bool get(T*& value) const
                {
                        if (is_data())
                        {
-                               value = lua_touserdata(script_.state_, index);
+                               value = reinterpret_cast<T*>(lua_touserdata(script_->state_, index));
                                return true;
                        }
                        return false;
                }
+               void* get_data() const
+               {
+                       return lua_touserdata(script_->state_, index);
+               }
 
                template <class T>
                bool get(std::vector<T>& 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 <class T, class V>
-               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 <class T, class V>
-               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 <class T>
-               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 <class T>
-               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 <class T>
                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 <class T>
                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 <class T>
-       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<script::function>(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 <class T>
+       slot push(const T& object)
+       {
+               void* storage;
+               slot copy = push_data(storage, sizeof(T));
+               new(storage) T(object);
+
+               push_class_metatable<T>();
+               copy.set_metatable();
+               return copy;
+       }
+
+       template <class T>
+       slot push_class(const function& ctor)
+       {
+               slot metatable = push_class_metatable<T>();
+
+               slot constructor = push_table();
+               push(ctor);
+               constructor.set_field("__call");
+               metatable.set_metatable();
+
+               return metatable;
+       }
+       template <class T>
+       slot push_class(cfunction ctor)
+       {
+               slot metatable = push_class_metatable<T>();
+
+               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 <class T>
+       slot push_pointer(const T* ptr)
+       {
+               lua_pushlightuserdata(state_, const_cast<void*>((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 <class T>
+       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 <class T>
+       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<T>();
+                       metatable.set_field("__cxxtype");       // type_info
+
+                       push(object_finalizer_<T>);
+                       metatable.set_field("__gc");            // finalizer
+
+                       //push(object_tostring_<T>);
+                       //metatable.set_field("__tostring");    // tostring
+               }
+               return metatable;
+       }
+
+       template <class T>
+       static int object_tostring_(lua_State* state)
+       {
+               std::ostringstream stream;
+               stream << *reinterpret_cast<T*>(lua_touserdata(state, 1));
+               lua_pushlstring(state, stream.str().c_str(), stream.str().length());
+               return 1;
+       }
+
+       template <class T>
+       static int object_finalizer_(lua_State* state)
+       {
+               reinterpret_cast<T*>(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<function>     functions_;
 };
 
+using namespace std::rel_ops;
+
 
+/**
+ * Output a script value to a stream.
+ */
 inline std::ostream& operator << (std::ostream& stream,
                                                                  const script::slot& slot)
 {
This page took 0.064721 seconds and 4 git commands to generate.