-/*] 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
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
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;
{
if (is_number())
{
- value = (double)lua_tonumber(script_.state_, index);
+ value = (double)lua_tonumber(script_->state_, index);
return true;
}
return false;
{
if (is_boolean())
{
- value = (bool)lua_toboolean(script_.state_, index);
+ value = (bool)lua_toboolean(script_->state_, index);
return true;
}
return false;
{
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;
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
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;
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))
T v;
if (value.get(v)) dictionary[k] = v;
}
- script_.pop();
+ script_->pop();
}
- script_.pop();
+ script_->pop();
return true;
}
/**
* 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
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();
}
}
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_;
};
destroy();
}
-
static script_ptr alloc()
{
return script_ptr(new script);
{
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());
return status(luaL_dofile(state_, file.c_str()));
}
-
- /**
+ /*
* Thread-handling methods.
*/
return script(state_);
}
- void push_thread()
+ slot push_thread()
{
lua_pushthread(state_);
+ return top();
}
status resume(int nargs)
return status(lua_resume(state_, nargs));
}
- status getStatus() const
+ status thread_status() const
{
return status(lua_status(state_));
}
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.
*/
{
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.
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
* 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.
*/
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)
{
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();
}
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_);
}
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
* 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;
else if (slot.get(boolean))
{
if (boolean) stream << "true";
- else stream << "false";
+ else stream << "false";
}
else if (slot.is_nil())
{