class Namespace
{
private:
Namespace& operator= (Namespace const& other);
lua_State* const L;
int mutable m_stackSize;
private:
#if 0
static int luaError (lua_State* L, std::string message)
{
assert (lua_isstring (L, lua_upvalueindex (1)));
std::string s;
lua_Debug ar;
int result = lua_getstack (L, 2, &ar);
if (result != 0)
{
lua_getinfo (L, "Sl", &ar);
s = ar.short_src;
if (ar.currentline != -1)
{
lua_pushnumber (L, ar.currentline);
s = s + ":" + lua_tostring (L, -1) + ": ";
lua_pop (L, 1);
}
}
s = s + message;
return luaL_error (L, s.c_str ());
}
#endif
void pop (int n) const
{
if (m_stackSize >= n && lua_gettop (L) >= n)
{
lua_pop (L, n);
m_stackSize -= n;
}
else
{
throw std::logic_error ("invalid stack");
}
}
private:
class ClassBase
{
private:
ClassBase& operator= (ClassBase const& other);
protected:
friend class Namespace;
lua_State* const L;
int mutable m_stackSize;
protected:
static int indexMetaMethod (lua_State* L)
{
int result = 0;
assert (lua_isuserdata (L, 1)); lua_getmetatable (L, 1); for (;;)
{
lua_pushvalue (L, 2); lua_rawget (L, -2); if (lua_iscfunction (L, -1)) {
lua_remove (L, -2); result = 1;
break;
}
else if (lua_isnil (L, -1))
{
lua_pop (L, 1);
}
else
{
lua_pop (L, 2);
throw std::logic_error ("not a cfunction");
}
rawgetfield (L, -1, "__propget"); if (lua_istable (L, -1)) {
lua_pushvalue (L, 2); lua_rawget (L, -2); lua_remove (L, -2); if (lua_iscfunction (L, -1)) {
lua_remove (L, -2); lua_pushvalue (L, 1); lua_call (L, 1, 1);
result = 1;
break;
}
else if (lua_isnil (L, -1))
{
lua_pop (L, 1);
}
else
{
lua_pop (L, 2);
throw std::logic_error ("not a cfunction");
}
}
else
{
lua_pop (L, 2);
throw std::logic_error ("missing __propget table");
}
rawgetfield (L, -1, "__parent");
if (lua_istable (L, -1))
{
lua_remove (L, -2);
}
else if (lua_isnil (L, -1))
{
result = 1;
break;
}
else
{
lua_pop (L, 2);
throw std::logic_error ("__parent is not a table");
}
}
return result;
}
static int newindexMetaMethod (lua_State* L)
{
int result = 0;
lua_getmetatable (L, 1);
for (;;)
{
rawgetfield (L, -1, "__propset");
if (!lua_isnil (L, -1))
{
lua_pushvalue (L, 2);
lua_rawget (L, -2);
if (!lua_isnil (L, -1))
{
assert (lua_isfunction (L, -1));
lua_pushvalue (L, 1);
lua_pushvalue (L, 3);
lua_call (L, 2, 0);
result = 0;
break;
}
lua_pop (L, 1);
}
lua_pop (L, 1);
rawgetfield (L, -1, "__parent");
if (lua_isnil (L, -1))
{
luaL_error (L,
"no member named '%s'", lua_tostring (L, 2));
}
lua_remove (L, -2);
}
return result;
}
void createConstTable (char const* name)
{
lua_newtable (L);
lua_pushvalue (L, -1);
lua_setmetatable (L, -2);
lua_pushboolean (L, 1);
lua_rawsetp (L, -2, getIdentityKey ());
lua_pushstring (L, (std::string ("const ") + name).c_str ());
rawsetfield (L, -2, "__type");
lua_pushcfunction (L, &indexMetaMethod);
rawsetfield (L, -2, "__index");
lua_pushcfunction (L, &newindexMetaMethod);
rawsetfield (L, -2, "__newindex");
lua_newtable (L);
rawsetfield (L, -2, "__propget");
if (Security::hideMetatables ())
{
lua_pushnil (L);
rawsetfield (L, -2, "__metatable");
}
}
void createClassTable (char const* name)
{
lua_newtable (L);
lua_pushvalue (L, -1);
lua_setmetatable (L, -2);
lua_pushboolean (L, 1);
lua_rawsetp (L, -2, getIdentityKey ());
lua_pushstring (L, name);
rawsetfield (L, -2, "__type");
lua_pushcfunction (L, &indexMetaMethod);
rawsetfield (L, -2, "__index");
lua_pushcfunction (L, &newindexMetaMethod);
rawsetfield (L, -2, "__newindex");
lua_newtable (L);
rawsetfield (L, -2, "__propget");
lua_newtable (L);
rawsetfield (L, -2, "__propset");
lua_pushvalue (L, -2);
rawsetfield (L, -2, "__const");
lua_pushvalue (L, -1);
rawsetfield (L, -3, "__class");
if (Security::hideMetatables ())
{
lua_pushnil (L);
rawsetfield (L, -2, "__metatable");
}
}
void createStaticTable (char const* name)
{
lua_newtable (L);
lua_newtable (L);
lua_pushvalue (L, -1);
lua_setmetatable (L, -3);
lua_insert (L, -2);
rawsetfield (L, -5, name);
#if 0
lua_pushlightuserdata (L, this);
lua_pushcclosure (L, &tostringMetaMethod, 1);
rawsetfield (L, -2, "__tostring");
#endif
lua_pushcfunction (L, &CFunc::indexMetaMethod);
rawsetfield (L, -2, "__index");
lua_pushcfunction (L, &CFunc::newindexMetaMethod);
rawsetfield (L, -2, "__newindex");
lua_newtable (L);
rawsetfield (L, -2, "__propget");
lua_newtable (L);
rawsetfield (L, -2, "__propset");
lua_pushvalue (L, -2);
rawsetfield (L, -2, "__class");
if (Security::hideMetatables ())
{
lua_pushnil (L);
rawsetfield (L, -2, "__metatable");
}
}
template <class Params, class C>
static int ctorContainerProxy (lua_State* L)
{
typedef typename ContainerTraits <C>::Type T;
ArgList <Params, 2> args (L);
T* const p = Constructor <T, Params>::call (args);
UserdataSharedHelper <C, false>::push (L, p);
return 1;
}
template <class Params, class T>
static int ctorPlacementProxy (lua_State* L)
{
ArgList <Params, 2> args (L);
Constructor <T, Params>::call (UserdataValue <T>::place (L), args);
return 1;
}
void pop (int n) const
{
if (m_stackSize >= n && lua_gettop (L) >= n)
{
lua_pop (L, n);
m_stackSize -= n;
}
else
{
throw std::logic_error ("invalid stack");
}
}
public:
explicit ClassBase (lua_State* L_)
: L (L_)
, m_stackSize (0)
{
}
ClassBase (ClassBase const& other)
: L (other.L)
, m_stackSize (0)
{
m_stackSize = other.m_stackSize;
other.m_stackSize = 0;
}
~ClassBase ()
{
pop (m_stackSize);
}
};
template <class T>
class Class : public ClassBase
{
public:
Class (char const* name, Namespace const* parent) : ClassBase (parent->L)
{
m_stackSize = parent->m_stackSize + 3;
parent->m_stackSize = 0;
assert (lua_istable (L, -1));
rawgetfield (L, -1, name);
if (lua_isnil (L, -1))
{
lua_pop (L, 1);
createConstTable (name);
lua_pushcfunction (L, &CFunc::gcMetaMethod <T>);
rawsetfield (L, -2, "__gc");
createClassTable (name);
lua_pushcfunction (L, &CFunc::gcMetaMethod <T>);
rawsetfield (L, -2, "__gc");
createStaticTable (name);
lua_pushvalue (L, -1);
lua_rawsetp (L, LUA_REGISTRYINDEX, ClassInfo <T>::getStaticKey ());
lua_pushvalue (L, -2);
lua_rawsetp (L, LUA_REGISTRYINDEX, ClassInfo <T>::getClassKey ());
lua_pushvalue (L, -3);
lua_rawsetp (L, LUA_REGISTRYINDEX, ClassInfo <T>::getConstKey ());
}
else
{
rawgetfield (L, -1, "__class");
rawgetfield (L, -1, "__const");
lua_insert (L, -3);
lua_insert (L, -2);
}
}
Class (char const* name, Namespace const* parent, void const* const staticKey)
: ClassBase (parent->L)
{
m_stackSize = parent->m_stackSize + 3;
parent->m_stackSize = 0;
assert (lua_istable (L, -1));
createConstTable (name);
lua_pushcfunction (L, &CFunc::gcMetaMethod <T>);
rawsetfield (L, -2, "__gc");
createClassTable (name);
lua_pushcfunction (L, &CFunc::gcMetaMethod <T>);
rawsetfield (L, -2, "__gc");
createStaticTable (name);
lua_rawgetp (L, LUA_REGISTRYINDEX, staticKey);
assert (lua_istable (L, -1));
rawgetfield (L, -1, "__class");
assert (lua_istable (L, -1));
rawgetfield (L, -1, "__const");
assert (lua_istable (L, -1));
rawsetfield (L, -6, "__parent");
rawsetfield (L, -4, "__parent");
rawsetfield (L, -2, "__parent");
lua_pushvalue (L, -1);
lua_rawsetp (L, LUA_REGISTRYINDEX, ClassInfo <T>::getStaticKey ());
lua_pushvalue (L, -2);
lua_rawsetp (L, LUA_REGISTRYINDEX, ClassInfo <T>::getClassKey ());
lua_pushvalue (L, -3);
lua_rawsetp (L, LUA_REGISTRYINDEX, ClassInfo <T>::getConstKey ());
}
Namespace endClass ()
{
return Namespace (this);
}
template <class U>
Class <T>& addStaticData (char const* name, U* pu, bool isWritable = true)
{
assert (lua_istable (L, -1));
rawgetfield (L, -1, "__propget");
assert (lua_istable (L, -1));
lua_pushlightuserdata (L, pu);
lua_pushcclosure (L, &CFunc::getVariable <U>, 1);
rawsetfield (L, -2, name);
lua_pop (L, 1);
rawgetfield (L, -1, "__propset");
assert (lua_istable (L, -1));
if (isWritable)
{
lua_pushlightuserdata (L, pu);
lua_pushcclosure (L, &CFunc::setVariable <U>, 1);
}
else
{
lua_pushstring (L, name);
lua_pushcclosure (L, &CFunc::readOnlyError, 1);
}
rawsetfield (L, -2, name);
lua_pop (L, 1);
return *this;
}
template <class U>
Class <T>& addStaticProperty (char const* name, U (*get)(), void (*set)(U) = 0)
{
typedef U (*get_t)();
typedef void (*set_t)(U);
assert (lua_istable (L, -1));
rawgetfield (L, -1, "__propget");
assert (lua_istable (L, -1));
new (lua_newuserdata (L, sizeof (get))) get_t (get);
lua_pushcclosure (L, &CFunc::Call <U (*) (void)>::f, 1);
rawsetfield (L, -2, name);
lua_pop (L, 1);
rawgetfield (L, -1, "__propset");
assert (lua_istable (L, -1));
if (set != 0)
{
new (lua_newuserdata (L, sizeof (set))) set_t (set);
lua_pushcclosure (L, &CFunc::Call <void (*) (U)>::f, 1);
}
else
{
lua_pushstring (L, name);
lua_pushcclosure (L, &CFunc::readOnlyError, 1);
}
rawsetfield (L, -2, name);
lua_pop (L, 1);
return *this;
}
template <class FP>
Class <T>& addStaticFunction (char const* name, FP const fp)
{
new (lua_newuserdata (L, sizeof (fp))) FP (fp);
lua_pushcclosure (L, &CFunc::Call <FP>::f, 1);
rawsetfield (L, -2, name);
return *this;
}
Class <T>& addStaticCFunction (char const* name, int (*const fp)(lua_State*))
{
lua_pushcfunction (L, fp);
rawsetfield (L, -2, name);
return *this;
}
template <class U>
Class <T>& addData (char const* name, const U T::* mp, bool isWritable = true)
{
typedef const U T::*mp_t;
{
rawgetfield (L, -2, "__propget");
rawgetfield (L, -4, "__propget");
new (lua_newuserdata (L, sizeof (mp_t))) mp_t (mp);
lua_pushcclosure (L, &CFunc::getProperty <T,U>, 1);
lua_pushvalue (L, -1);
rawsetfield (L, -4, name);
rawsetfield (L, -2, name);
lua_pop (L, 2);
}
if (isWritable)
{
rawgetfield (L, -2, "__propset");
assert (lua_istable (L, -1));
new (lua_newuserdata (L, sizeof (mp_t))) mp_t (mp);
lua_pushcclosure (L, &CFunc::setProperty <T,U>, 1);
rawsetfield (L, -2, name);
lua_pop (L, 1);
}
return *this;
}
template <class TG, class TS>
Class <T>& addProperty (char const* name, TG (T::* get) () const, void (T::* set) (TS))
{
{
rawgetfield (L, -2, "__propget");
rawgetfield (L, -4, "__propget");
typedef TG (T::*get_t) () const;
new (lua_newuserdata (L, sizeof (get_t))) get_t (get);
lua_pushcclosure (L, &CFunc::CallConstMember <get_t>::f, 1);
lua_pushvalue (L, -1);
rawsetfield (L, -4, name);
rawsetfield (L, -2, name);
lua_pop (L, 2);
}
{
rawgetfield (L, -2, "__propset");
assert (lua_istable (L, -1));
typedef void (T::* set_t) (TS);
new (lua_newuserdata (L, sizeof (set_t))) set_t (set);
lua_pushcclosure (L, &CFunc::CallMember <set_t>::f, 1);
rawsetfield (L, -2, name);
lua_pop (L, 1);
}
return *this;
}
template <class TG>
Class <T>& addProperty (char const* name, TG (T::* get) () const)
{
rawgetfield (L, -2, "__propget");
rawgetfield (L, -4, "__propget");
typedef TG (T::*get_t) () const;
new (lua_newuserdata (L, sizeof (get_t))) get_t (get);
lua_pushcclosure (L, &CFunc::CallConstMember <get_t>::f, 1);
lua_pushvalue (L, -1);
rawsetfield (L, -4, name);
rawsetfield (L, -2, name);
lua_pop (L, 2);
return *this;
}
template <class TG, class TS>
Class <T>& addProperty (char const* name, TG (*get) (T const*), void (*set) (T*, TS))
{
{
rawgetfield (L, -2, "__propget");
rawgetfield (L, -4, "__propget");
typedef TG (*get_t) (T const*);
new (lua_newuserdata (L, sizeof (get_t))) get_t (get);
lua_pushcclosure (L, &CFunc::Call <get_t>::f, 1);
lua_pushvalue (L, -1);
rawsetfield (L, -4, name);
rawsetfield (L, -2, name);
lua_pop (L, 2);
}
if (set != 0)
{
rawgetfield (L, -2, "__propset");
assert (lua_istable (L, -1));
typedef void (*set_t) (T*, TS);
new (lua_newuserdata (L, sizeof (set_t))) set_t (set);
lua_pushcclosure (L, &CFunc::Call <set_t>::f, 1);
rawsetfield (L, -2, name);
lua_pop (L, 1);
}
return *this;
}
template <class TG, class TS>
Class <T>& addProperty (char const* name, TG (*get) (T const*))
{
rawgetfield (L, -2, "__propget");
rawgetfield (L, -4, "__propget");
typedef TG (*get_t) (T const*);
new (lua_newuserdata (L, sizeof (get_t))) get_t (get);
lua_pushcclosure (L, &CFunc::Call <get_t>::f, 1);
lua_pushvalue (L, -1);
rawsetfield (L, -4, name);
rawsetfield (L, -2, name);
lua_pop (L, 2);
return *this;
}
template <class MemFn>
Class <T>& addFunction (char const* name, MemFn mf)
{
CFunc::CallMemberFunctionHelper <MemFn, FuncTraits <MemFn>::isConstMemberFunction>::add (L, name, mf);
return *this;
}
Class <T>& addCFunction (char const* name, int (T::*mfp)(lua_State*))
{
typedef int (T::*MFP)(lua_State*);
assert (lua_istable (L, -1));
new (lua_newuserdata (L, sizeof (mfp))) MFP (mfp);
lua_pushcclosure (L, &CFunc::CallMemberCFunction <T>::f, 1);
rawsetfield (L, -3, name);
return *this;
}
Class <T>& addCFunction (char const* name, int (T::*mfp)(lua_State*) const)
{
typedef int (T::*MFP)(lua_State*) const;
assert (lua_istable (L, -1));
new (lua_newuserdata (L, sizeof (mfp))) MFP (mfp);
lua_pushcclosure (L, &CFunc::CallConstMemberCFunction <T>::f, 1);
lua_pushvalue (L, -1);
rawsetfield (L, -5, name); rawsetfield (L, -3, name);
return *this;
}
template <class MemFn, class C>
Class <T>& addConstructor ()
{
lua_pushcclosure (L,
&ctorContainerProxy <typename FuncTraits <MemFn>::Params, C>, 0);
rawsetfield(L, -2, "__call");
return *this;
}
template <class MemFn>
Class <T>& addConstructor ()
{
lua_pushcclosure (L,
&ctorPlacementProxy <typename FuncTraits <MemFn>::Params, T>, 0);
rawsetfield(L, -2, "__call");
return *this;
}
};
private:
explicit Namespace (lua_State* L_)
: L (L_)
, m_stackSize (0)
{
lua_getglobal (L, "_G");
++m_stackSize;
}
Namespace (char const* name, Namespace const* parent)
: L (parent->L)
, m_stackSize (0)
{
m_stackSize = parent->m_stackSize + 1;
parent->m_stackSize = 0;
assert (lua_istable (L, -1));
rawgetfield (L, -1, name);
if (lua_isnil (L, -1))
{
lua_pop (L, 1);
lua_newtable (L);
lua_pushvalue (L, -1);
lua_setmetatable (L, -2);
lua_pushcfunction (L, &CFunc::indexMetaMethod);
rawsetfield (L, -2, "__index");
lua_pushcfunction (L, &CFunc::newindexMetaMethod);
rawsetfield (L, -2, "__newindex");
lua_newtable (L);
rawsetfield (L, -2, "__propget");
lua_newtable (L);
rawsetfield (L, -2, "__propset");
lua_pushvalue (L, -1);
rawsetfield (L, -3, name);
#if 0
lua_pushcfunction (L, &tostringMetaMethod);
rawsetfield (L, -2, "__tostring");
#endif
}
}
explicit Namespace (Namespace const* child)
: L (child->L)
, m_stackSize (0)
{
m_stackSize = child->m_stackSize - 1;
child->m_stackSize = 1;
child->pop (1);
assert (m_stackSize != 0);
}
explicit Namespace (ClassBase const* child)
: L (child->L)
, m_stackSize (0)
{
m_stackSize = child->m_stackSize - 3;
child->m_stackSize = 3;
child->pop (3);
}
public:
Namespace (Namespace const& other) : L (other.L)
{
m_stackSize = other.m_stackSize;
other.m_stackSize = 0;
}
~Namespace ()
{
pop (m_stackSize);
}
static Namespace getGlobalNamespace (lua_State* L)
{
return Namespace (L);
}
Namespace beginNamespace (char const* name)
{
return Namespace (name, this);
}
Namespace endNamespace ()
{
return Namespace (this);
}
template <class T>
Namespace& addVariable (char const* name, T* pt, bool isWritable = true)
{
assert (lua_istable (L, -1));
rawgetfield (L, -1, "__propget");
assert (lua_istable (L, -1));
lua_pushlightuserdata (L, pt);
lua_pushcclosure (L, &CFunc::getVariable <T>, 1);
rawsetfield (L, -2, name);
lua_pop (L, 1);
rawgetfield (L, -1, "__propset");
assert (lua_istable (L, -1));
if (isWritable)
{
lua_pushlightuserdata (L, pt);
lua_pushcclosure (L, &CFunc::setVariable <T>, 1);
}
else
{
lua_pushstring (L, name);
lua_pushcclosure (L, &CFunc::readOnlyError, 1);
}
rawsetfield (L, -2, name);
lua_pop (L, 1);
return *this;
}
template <class TG, class TS>
Namespace& addProperty (char const* name, TG (*get) (), void (*set)(TS) = 0)
{
assert (lua_istable (L, -1));
rawgetfield (L, -1, "__propget");
assert (lua_istable (L, -1));
typedef TG (*get_t) ();
new (lua_newuserdata (L, sizeof (get_t))) get_t (get);
lua_pushcclosure (L, &CFunc::Call <TG (*) (void)>::f, 1);
rawsetfield (L, -2, name);
lua_pop (L, 1);
rawgetfield (L, -1, "__propset");
assert (lua_istable (L, -1));
if (set != 0)
{
typedef void (*set_t) (TS);
new (lua_newuserdata (L, sizeof (set_t))) set_t (set);
lua_pushcclosure (L, &CFunc::Call <void (*) (TS)>::f, 1);
}
else
{
lua_pushstring (L, name);
lua_pushcclosure (L, &CFunc::readOnlyError, 1);
}
rawsetfield (L, -2, name);
lua_pop (L, 1);
return *this;
}
template <class FP>
Namespace& addFunction (char const* name, FP const fp)
{
assert (lua_istable (L, -1));
new (lua_newuserdata (L, sizeof (fp))) FP (fp);
lua_pushcclosure (L, &CFunc::Call <FP>::f, 1);
rawsetfield (L, -2, name);
return *this;
}
Namespace& addCFunction (char const* name, int (*const fp)(lua_State*))
{
lua_pushcfunction (L, fp);
rawsetfield (L, -2, name);
return *this;
}
template <class T>
Class <T> beginClass (char const* name)
{
return Class <T> (name, this);
}
template <class T, class U>
Class <T> deriveClass (char const* name)
{
return Class <T> (name, this, ClassInfo <U>::getStaticKey ());
}
};
inline Namespace getGlobalNamespace (lua_State* L)
{
return Namespace::getGlobalNamespace (L);
}