/**
* \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>
public:
typedef boost::function<int(script&)> function;
+ typedef int (*cfunction)(script&);
enum status
{
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
* 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;
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;
}
/**
* 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.
*/
+ 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;
}
* 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
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;
{
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;
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
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;
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();
+ 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();
}
}
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_;
};
{
if (state_) destroy();
state_ = luaL_newstate();
- registry().set_field("Script_hh_Object", (void*)this);
}
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);
}
}
- /**
+ /*
* 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.
*/
+ 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.
*/
* 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);
}
}
- /**
+ /*
* 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();
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_);
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);
+ 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);
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)
{