]> Dogcows Code - chaz/yoink/blobdiff - src/moof/script.hh
fixed documentation about where to find licenses
[chaz/yoink] / src / moof / script.hh
index 85973dd0402f1823434ced4b2da57305b046146d..0431f1afcebbe50e5942731074acef3ddf8e85f4 100644 (file)
@@ -1,31 +1,22 @@
 
-/*]  Copyright (c) 2009-2010, Charles McGarvey  [**************************
+/*]  Copyright (c) 2009-2011, Charles McGarvey  [*****************************
 **]  All rights reserved.
 *
-* vi:ts=4 sw=4 tw=75
-*
 * Distributable under the terms and conditions of the 2-clause BSD license;
 * see the file COPYING for a complete text of the license.
 *
-**************************************************************************/
+*****************************************************************************/
 
 #ifndef _MOOF_SCRIPT_HH_
 #define _MOOF_SCRIPT_HH_
 
-/**
- * \file script.hh
- * A thin wrapper over Lua.  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
- * provides that mechanism with a certain level of abstraction while also
- * 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>
 #include <lua.hpp>
 
 
+/**
+ * \file script.hh
+ * 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 provides that
+ * mechanism with a certain level of abstraction while also providing a
+ * cleaner, more consistent API.
+ */
+
 namespace moof {
 
 
 class script;
 typedef boost::shared_ptr<script> script_ptr;
 
-
 class script
 {
 public:
 
        typedef boost::function<int(script&)> function;
+       typedef int (*cfunction)(script&);
 
        enum status
        {
-               success                 = 0,
-               yielding                = LUA_YIELD,
+               success         = 0,
+               yielding        = LUA_YIELD,
                runtime_error   = LUA_ERRRUN,
                syntax_error    = LUA_ERRSYNTAX,
                memory_error    = LUA_ERRMEM,
                handler_error   = LUA_ERRERR,
-               file_error              = LUA_ERRFILE
+               file_error      = LUA_ERRFILE
        };
 
        enum pseudoindex
@@ -65,227 +66,210 @@ 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
         * specifically, it represents a position on the stack.  The
         * distinction is only important when objects are moved around on the
         * stack or if the slot represents a negative index on the stack (the
-        * value of which will change as things are pushed onto and popped from
-        * the stack).
+        * 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.
+                * You have direct access to the index of the value on the
+                * stack being represented.
                 */
-
                int index;
 
-
                enum type
                {
-                       none                    = LUA_TNONE,
-                       nil                             = LUA_TNIL,
-                       boolean                 = LUA_TBOOLEAN,
-                       light_data              = LUA_TLIGHTUSERDATA,
-                       number                  = LUA_TNUMBER,
-                       string                  = LUA_TSTRING,
-                       table                   = LUA_TTABLE,
-                       function                = LUA_TFUNCTION,
-                       data                    = LUA_TUSERDATA,
-                       thread                  = LUA_TTHREAD
+                       none            = LUA_TNONE,
+                       nil             = LUA_TNIL,
+                       boolean         = LUA_TBOOLEAN,
+                       light_data      = LUA_TLIGHTUSERDATA,
+                       number          = LUA_TNUMBER,
+                       string          = LUA_TSTRING,
+                       table           = LUA_TTABLE,
+                       function        = LUA_TFUNCTION,
+                       data            = LUA_TUSERDATA,
+                       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); }
-               bool is_imported_function() const
-               { return (bool)lua_iscfunction(script_.state_, index); }
-               bool is_function() const
-               { return (bool)lua_isfunction(script_.state_, index); }
-               bool is_nil() const
-               { return (bool)lua_isnil(script_.state_, index); }
-               bool is_none() const
-               { return (bool)lua_isnone(script_.state_, index); }
-               bool is_none_or_nil() const
-               { return (bool)lua_isnoneornil(script_.state_, index); }
-               bool is_number() const
-               { return (bool)lua_isnumber(script_.state_, index); }
-               bool is_string() const
-               { return (bool)lua_isstring(script_.state_, index); }
-               bool is_table() const
-               { return (bool)lua_istable(script_.state_, index); }
-               bool is_thread() const
-               { return (bool)lua_isthread(script_.state_, index); }
-               bool is_data() const
-               { return (bool)lua_isuserdata(script_.state_, index); }
-               bool is_light_data() const
-               { return (bool)lua_islightuserdata(script_.state_, index); }
+#define IS_TYPE(T, N) \
+               bool is_##N() const \
+               { \
+                       return (bool)lua_is##T(script_->state_, index); \
+               }//
+               IS_TYPE(boolean, boolean);
+               IS_TYPE(cfunction, imported_function);
+               IS_TYPE(function, function);
+               IS_TYPE(lightuserdata, light_data);
+               IS_TYPE(nil, nil);
+               IS_TYPE(none, none);
+               IS_TYPE(noneornil, none_or_nil);
+               IS_TYPE(number, number);
+               IS_TYPE(string, string);
+               IS_TYPE(table, table);
+               IS_TYPE(thread, thread);
+               IS_TYPE(userdata, data);
+#undef IS_TYPE
 
                /**
                 * 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));
-                       }
-               }
-
-               void raise(const char* error)
-               {
-                       luaL_argerror(script_.state_, index, error);
+                       if (t != type()) raise_type_error(type_name(t));
+                       return *this;
                }
 
+#define REQUIRE_TYPE(T) \
+               const slot& require_##T(const std::string& what = #T) const \
+               { \
+                       if (!is_##T()) raise_type_error(what); \
+                       return *this; \
+               }//
+               REQUIRE_TYPE(boolean);
+               REQUIRE_TYPE(number);
+               REQUIRE_TYPE(string);
+               REQUIRE_TYPE(table);
+               REQUIRE_TYPE(function);
+               REQUIRE_TYPE(data);
+               REQUIRE_TYPE(nil);
+               REQUIRE_TYPE(thread);
+#undef REQUIRE_TYPE
 
-               slot& require_boolean()
-               {
-                       if (!is_boolean())
-                       {
-                               luaL_typerror(script_.state_, index, "boolean");
-                       }
-                       return *this;
-               }
-               slot& require_number()
-               {
-                       if (!is_number())
-                       {
-                               luaL_typerror(script_.state_, index, "number");
-                       }
-                       return *this;
-               }
-               slot& require_string()
-               {
-                       if (!is_string())
-                       {
-                               luaL_typerror(script_.state_, index, "string");
-                       }
-                       return *this;
-               }
-               slot& require_table()
-               {
-                       if (!is_table())
-                       {
-                               luaL_typerror(script_.state_, index, "table");
-                       }
-                       return *this;
-               }
-               slot& require_function()
-               {
-                       if (!is_function())
-                       {
-                               luaL_typerror(script_.state_, index, "function");
-                       }
-                       return *this;
-               }
-               slot& require_data()
-               {
-                       if (!is_data())
-                       {
-                               luaL_typerror(script_.state_, index, "data");
-                       }
-                       return *this;
-               }
-               slot& require_nil()
+               template <class T>
+               const slot&
+               require_object(const std::string& what = typeid(T).name()) const
                {
-                       if (!is_nil())
+                       if (!is_data()) raise_type_error(what);
+
+                       slot metatable = push_metatable();
+                       if (!metatable.is_table())
                        {
-                               luaL_typerror(script_.state_, index, "nil");
+                               metatable.pop();
+                               raise_type_error(what);
                        }
-                       return *this;
-               }
-               slot& require_thread()
-               {
-                       if (!is_thread())
+
+                       slot type = metatable.push_field("__cxxtype");
+                       std::type_info* typeinfo;
+                       if (!type.get(typeinfo))
                        {
-                               luaL_typerror(script_.state_, index, "thread");
+                               metatable.pop();
+                               raise_type_error(what);
                        }
+
+                       metatable.pop();
+                       if (*typeinfo != typeid(T)) raise_type_error(what);
                        return *this;
                }
 
-
                /**
                 * 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);
+               }
 
                /**
-                * Get the length of the value according to the definition given by
-                * Lua.
+                * 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;
-                       else           return index;
+                       if (index < 0)
+                               return index + lua_gettop(script_->state_) + 1;
+                       else
+                               return index;
                }
 
-
                /**
                 * 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
@@ -293,54 +277,49 @@ public:
                        return !is_none();
                }
 
-
                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);
-                               return true;
-                       }
-                       return false;
-               }
+#define GET_INT_OF_TYPE(T) \
+               bool get(T& value) const \
+               { \
+                       if (is_number()) \
+                       { \
+                               value = static_cast<T>(lua_tointeger(script_->state_, index)); \
+                               return true; \
+                       } \
+                       return false; \
+               }//
+               GET_INT_OF_TYPE(char);
+               GET_INT_OF_TYPE(unsigned char);
+               GET_INT_OF_TYPE(short);
+               GET_INT_OF_TYPE(unsigned short);
+               GET_INT_OF_TYPE(int);
+               GET_INT_OF_TYPE(unsigned int);
+               GET_INT_OF_TYPE(long);
+               GET_INT_OF_TYPE(unsigned long);
+               GET_INT_OF_TYPE(long long);
+               GET_INT_OF_TYPE(unsigned long long);
+#undef GET_INT_OF_TYPE
 
                bool get(float& value) const
                {
                        if (is_number())
                        {
-                               value = (float)lua_tonumber(script_.state_, index);
+                               value = (float)lua_tonumber(script_->state_, index);
                                return true;
                        }
                        return false;
@@ -349,7 +328,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 +338,7 @@ public:
                {
                        if (is_boolean())
                        {
-                               value = (bool)lua_toboolean(script_.state_, index);
+                               value = (bool)lua_toboolean(script_->state_, index);
                                return true;
                        }
                        return false;
@@ -369,12 +348,11 @@ public:
                {
                        if (is_string())
                        {
-                               value = lua_tolstring(script_.state_, index, &size);
+                               value = lua_tolstring(script_->state_, index, &size);
                                return true;
                        }
                        return false;
                }
-
                bool get(std::string& value) const
                {
                        const char*     str;
@@ -387,15 +365,30 @@ 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;
                }
+               template <class T>
+               bool get(T& value) const
+               {
+                       if (is_data())
+                       {
+                               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 +397,21 @@ 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;
+                               if (value.get(v))
+                                       array.push_back(v);
+                               else
+                                       done = true;
 
-                               script_.pop();
+                               script_->pop();
                        }
 
                        return true;
@@ -429,12 +424,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 +437,9 @@ public:
                                        T v;
                                        if (value.get(v)) dictionary[k] = v;
                                }
-                               script_.pop();
+                               script_->pop();
                        }
-                       script_.pop();
+                       script_->pop();
 
                        return true;
                }
@@ -452,42 +447,55 @@ 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 +504,78 @@ public:
                template <class T>
                void set(T value)
                {
-                       script_.push(value);
-                       replace();
-               }
-
-               void set()
-               {
-                       replace();
+                       script_->push(value);
+                       set();
                }
 
-
                /**
                 * 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 +585,61 @@ 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_;
        };
 
 
@@ -612,7 +654,6 @@ public:
                destroy();
        }
 
-
        static script_ptr alloc()
        {
                return script_ptr(new script);
@@ -622,73 +663,58 @@ public:
        {
                if (state_) destroy();
                state_ = luaL_newstate();
-               registry().set_field("Script_hh_Object", (void*)this);
        }
 
-
        void import_standard_libraries()
        {
                luaL_openlibs(state_);
        }
 
-       void import_base_library()
-       {
-               lua_pushcfunction(state_, luaopen_base);
-               push(LUA_COLIBNAME);
-               call(1, 0);
-       }
-
-       void import_package_library()
-       {
-               lua_pushcfunction(state_, luaopen_package);
-               push(LUA_LOADLIBNAME);
-               call(1, 0);
-       }
-
-       void import_string_library()
-       {
-               lua_pushcfunction(state_, luaopen_string);
-               push(LUA_STRLIBNAME);
-               call(1, 0);
-       }
-
-       void import_table_library()
-       {
-               lua_pushcfunction(state_, luaopen_table);
-               push(LUA_TABLIBNAME);
-               call(1, 0);
-       }
-
-       void import_math_library()
-       {
-               lua_pushcfunction(state_, luaopen_math);
-               push(LUA_MATHLIBNAME);
-               call(1, 0);
+       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_io_library()
-       {
-               lua_pushcfunction(state_, luaopen_io);
-               push(LUA_IOLIBNAME);
-               call(1, 0);
-       }
-
-       void import_os_library()
-       {
-               lua_pushcfunction(state_, luaopen_os);
-               push(LUA_OSLIBNAME);
-               call(1, 0);
-       }
-
-       void import_debug_library()
-       {
-               lua_pushcfunction(state_, luaopen_debug);
-               push(LUA_DBLIBNAME);
-               call(1, 0);
-       }
-
-
-       void import_function(const std::string& name, const function& function)
+#define IMPORT_LIBRARY(L, K) \
+       void import_##L##_library() \
+       { \
+               push(luaopen_##L); \
+               push(LUA_##K##LIBNAME); \
+               call(1, 0); \
+       }//
+       IMPORT_LIBRARY(base, CO);
+       IMPORT_LIBRARY(debug, DB);
+       IMPORT_LIBRARY(io, IO);
+       IMPORT_LIBRARY(math, MATH);
+       IMPORT_LIBRARY(os, OS);
+       IMPORT_LIBRARY(package, LOAD);
+       IMPORT_LIBRARY(string, STR);
+       IMPORT_LIBRARY(table, TAB);
+#undef IMPORT_LIBRARY
+
+       void
+       import_function(const std::string& name, const function& function)
        {
                push(function);
                lua_setglobal(state_, name.c_str());
@@ -704,8 +730,7 @@ public:
                return status(luaL_dofile(state_, file.c_str()));
        }
 
-
-       /**
+       /*
         * Thread-handling methods.
         */
 
@@ -714,9 +739,10 @@ public:
                return script(state_);
        }
 
-       void push_thread()
+       slot push_thread()
        {
                lua_pushthread(state_);
+               return top();
        }
 
        status resume(int nargs)
@@ -724,7 +750,7 @@ public:
                return status(lua_resume(state_, nargs));
        }
 
-       status getStatus() const
+       status thread_status() const
        {
                return status(lua_status(state_));
        }
@@ -736,28 +762,34 @@ 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.
         */
-
-       void raise()
+       int raise()
        {
-               lua_error(state_);
+               throw std::runtime_error("");
+               return 0;
        }
 
-
        /**
+        * 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)
+       {
+               throw std::runtime_error(message);
+               return 0;
+       }
+
+       /*
         * Get significant values.
         */
 
@@ -765,23 +797,19 @@ public:
        {
                return slot(*this, globals_index);
        }
-
        slot registry() const
        {
                return slot(*this, registry_index);
        }
-
        slot environment() const
        {
                return slot(*this, environment_index);
        }
-
        slot top() const
        {
                return slot(*this, stack_size());
        }
 
-
        /**
         * Set the size of the stack.
         * \param size The stack size.
@@ -809,7 +837,6 @@ public:
                stack_size(0);
        }
 
-
        /**
         * Makes sure there is at least extra more places on the stack.
         * Returns false if space couldn't be created.  Just like with the
@@ -817,12 +844,11 @@ 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);
        }
 
-
        /**
         * Concatenates the top-most n slots on the stack.
         */
@@ -831,16 +857,27 @@ public:
                lua_concat(state_, n);
        }
 
-
-       /**
+       /*
         * Push some values onto the stack.
         */
-       template <class T>
-       slot push(T value)
-       {
-               lua_pushinteger(state_, lua_Integer(value));
-               return top();
-       }
+
+#define PUSH_INT_OF_TYPE(T) \
+       slot push(T value) \
+       { \
+               lua_pushinteger(state_, static_cast<lua_Integer>(value)); \
+               return top(); \
+       }//
+       PUSH_INT_OF_TYPE(char);
+       PUSH_INT_OF_TYPE(unsigned char);
+       PUSH_INT_OF_TYPE(short);
+       PUSH_INT_OF_TYPE(unsigned short);
+       PUSH_INT_OF_TYPE(int);
+       PUSH_INT_OF_TYPE(unsigned int);
+       PUSH_INT_OF_TYPE(long);
+       PUSH_INT_OF_TYPE(unsigned long);
+       PUSH_INT_OF_TYPE(long long);
+       PUSH_INT_OF_TYPE(unsigned long long);
+#undef PUSH_INT_OF_TYPE
 
        slot push(bool value)
        {
@@ -850,12 +887,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 +914,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_);
@@ -908,27 +983,66 @@ public:
        }
 
        slot push_code(const std::string& name,
-                                  const char* buffer,
-                                  size_t size,
-                                  status& result)
+                       const char* buffer, size_t size, status& result)
        {
                result = status(luaL_loadbuffer(state_,
-                                                                               buffer, size, name.c_str()));
+                                       buffer, size, name.c_str()));
                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,101 +1050,194 @@ 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));
        }
 
-
        /**
         * Pops n values from the top of the stack.
         */
-
        void pop(int n = 1)
        {
                lua_pop(state_, n);
        }
 
-
        /**
         * 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);
-       }
-
-       void collect(int step)
-       {
-               lua_gc(state_, LUA_GCSTEP, step);
+               return lua_gc(state_, LUA_GCCOUNT, 0) +
+                          lua_gc(state_, LUA_GCCOUNTB, 0) / 1024.0f;
        }
 
-       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);
        }
 
-
 private:
 
        script(lua_State* state) :
-               state_(lua_newthread(state)),
-               is_main_thread_(false) {}
+               state_(lua_newthread(state)) {}
 
-       static int dispatch_call(lua_State* state)
+       slot push(lua_CFunction function, int upvalues = 0)
        {
-               const function* function = (const script::function*)lua_touserdata(state,
+               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 call_functor(lua_State* state)
+       {
+               function* function = (script::function*)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);
+               }
+       }
+
+       static int call_function(lua_State* state)
+       {
+               cfunction function = (cfunction)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);
+               }
        }
 
        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;
 
-inline std::ostream& operator << (std::ostream& stream,
-                                                                 const script::slot& slot)
+/**
+ * Output a script value to a stream.
+ */
+inline std::ostream&
+operator << (std::ostream& stream, const script::slot& slot)
 {
        std::string     str;
        bool            boolean;
@@ -1042,7 +1249,7 @@ inline std::ostream& operator << (std::ostream& stream,
        else if (slot.get(boolean))
        {
                if (boolean) stream << "true";
-               else         stream << "false";
+               else stream << "false";
        }
        else if (slot.is_nil())
        {
This page took 0.060342 seconds and 4 git commands to generate.