inline void* getIdentityKey ()
{
static char value;
return &value;
}
class Userdata
{
protected:
void* m_p;
inline void* getPointer ()
{
return m_p;
}
private:
static Userdata* getExactClass (lua_State* L,
int narg,
void const* classKey)
{
Userdata* ud = 0;
int const index = lua_absindex (L, narg);
bool mismatch = false;
char const* got = 0;
lua_rawgetp (L, LUA_REGISTRYINDEX, classKey);
assert (lua_istable (L, -1));
if (!lua_isuserdata (L, index))
mismatch = true;
if (!mismatch)
{
lua_getmetatable (L, index);
lua_rawgetp (L, -1, getIdentityKey ());
if (lua_isboolean (L, -1))
{
lua_pop (L, 1);
}
else
{
lua_pop (L, 2);
mismatch = true;
}
}
if (!mismatch)
{
if (lua_rawequal (L, -1, -2))
{
lua_pop (L, 2);
ud = static_cast <Userdata*> (lua_touserdata (L, index));
}
else
{
rawgetfield (L, -2, "__const");
if (lua_rawequal (L, -1, -2))
{
lua_pop (L, 3);
ud = static_cast <Userdata*> (lua_touserdata (L, index));
}
else
{
rawgetfield (L, -2, "__type");
lua_insert (L, -4);
lua_pop (L, 2);
got = lua_tostring (L, -2);
mismatch = true;
}
}
}
if (mismatch)
{
rawgetfield (L, -1, "__type");
assert (lua_type (L, -1) == LUA_TSTRING);
char const* const expected = lua_tostring (L, -1);
if (got == 0)
got = lua_typename (L, lua_type (L, index));
char const* const msg = lua_pushfstring (
L, "%s expected, got %s", expected, got);
if (narg > 0)
luaL_argerror (L, narg, msg);
else
lua_error (L);
}
return ud;
}
static Userdata* getClass (lua_State* L,
int index,
void const* baseClassKey,
bool canBeConst)
{
assert (index > 0);
Userdata* ud = 0;
bool mismatch = false;
char const* got = 0;
lua_rawgetp (L, LUA_REGISTRYINDEX, baseClassKey);
assert (lua_istable (L, -1));
if (lua_isuserdata (L, index))
{
lua_getmetatable (L, index);
lua_rawgetp (L, -1, getIdentityKey ());
if (lua_isboolean (L, -1))
{
lua_pop (L, 1);
rawgetfield (L, -1, "__const");
assert (lua_istable (L, -1) || lua_isnil (L, -1));
bool const isConst = lua_isnil (L, -1);
lua_pop (L, 1);
if (isConst)
{
rawgetfield (L, -2, "__const");
assert (lua_istable (L, -1));
lua_replace (L, -3);
}
for (;;)
{
if (lua_rawequal (L, -1, -2))
{
lua_pop (L, 2);
if (isConst && !canBeConst)
{
luaL_argerror (L, index, "cannot be const");
}
else
{
ud = static_cast <Userdata*> (lua_touserdata (L, index));
break;
}
}
else
{
rawgetfield (L, -1, "__parent");
if (lua_isnil (L, -1))
{
lua_remove (L, -1);
rawgetfield (L, -1, "__type");
lua_insert (L, -3);
lua_pop (L, 1);
got = lua_tostring (L, -2);
mismatch = true;
break;
}
else
{
lua_remove (L, -2);
}
}
}
}
else
{
lua_pop (L, 2);
mismatch = true;
}
}
else
{
mismatch = true;
}
if (mismatch)
{
assert (lua_type (L, -1) == LUA_TTABLE);
rawgetfield (L, -1, "__type");
assert (lua_type (L, -1) == LUA_TSTRING);
char const* const expected = lua_tostring (L, -1);
if (got == 0)
got = lua_typename (L, lua_type (L, index));
char const* const msg = lua_pushfstring (
L, "%s expected, got %s", expected, got);
luaL_argerror (L, index, msg);
}
return ud;
}
public:
virtual ~Userdata () { }
template <class T>
static inline Userdata* getExact (lua_State* L, int index)
{
return getExactClass (L, index, ClassInfo <T>::getClassKey ());
}
template <class T>
static inline T* get (lua_State* L, int index, bool canBeConst)
{
if (lua_isnil (L, index))
return 0;
else
return static_cast <T*> (getClass (L, index,
ClassInfo <T>::getClassKey (), canBeConst)->getPointer ());
}
};
template <class T>
class UserdataValue : public Userdata
{
private:
UserdataValue <T> (UserdataValue <T> const&);
UserdataValue <T> operator= (UserdataValue <T> const&);
char m_storage [sizeof (T)];
inline T* getObject ()
{
return reinterpret_cast <T*> (&m_storage [0]);
}
private:
UserdataValue ()
{
m_p = getObject ();
}
~UserdataValue ()
{
getObject ()->~T ();
}
public:
static void* place (lua_State* const L)
{
UserdataValue <T>* const ud = new (
lua_newuserdata (L, sizeof (UserdataValue <T>))) UserdataValue <T> ();
lua_rawgetp (L, LUA_REGISTRYINDEX, ClassInfo <T>::getClassKey ());
assert (lua_istable (L, -1));
lua_setmetatable (L, -2);
return ud->getPointer ();
}
template <class U>
static inline void push (lua_State* const L, U const& u)
{
new (place (L)) U (u);
}
};
class UserdataPtr : public Userdata
{
private:
UserdataPtr (UserdataPtr const&);
UserdataPtr operator= (UserdataPtr const&);
private:
static void push (lua_State* L, void* const p, void const* const key)
{
if (p)
{
new (lua_newuserdata (L, sizeof (UserdataPtr))) UserdataPtr (p);
lua_rawgetp (L, LUA_REGISTRYINDEX, key);
assert (lua_istable (L, -1));
lua_setmetatable (L, -2);
}
else
{
lua_pushnil (L);
}
}
static void push (lua_State* L, void const* const p, void const* const key)
{
if (p)
{
new (lua_newuserdata (L, sizeof (UserdataPtr)))
UserdataPtr (const_cast <void*> (p));
lua_rawgetp (L, LUA_REGISTRYINDEX, key);
assert (lua_istable (L, -1));
lua_setmetatable (L, -2);
}
else
{
lua_pushnil (L);
}
}
explicit UserdataPtr (void* const p)
{
m_p = p;
assert (m_p != 0);
}
public:
template <class T>
static inline void push (lua_State* const L, T* const p)
{
if (p)
push (L, p, ClassInfo <T>::getClassKey ());
else
lua_pushnil (L);
}
template <class T>
static inline void push (lua_State* const L, T const* const p)
{
if (p)
push (L, p, ClassInfo <T>::getConstKey ());
else
lua_pushnil (L);
}
};
template <class C>
class UserdataShared : public Userdata
{
private:
UserdataShared (UserdataShared <C> const&);
UserdataShared <C>& operator= (UserdataShared <C> const&);
typedef typename TypeTraits::removeConst <
typename ContainerTraits <C>::Type>::Type T;
C m_c;
private:
~UserdataShared ()
{
}
public:
template <class U>
explicit UserdataShared (U const& u) : m_c (u)
{
m_p = const_cast <void*> (reinterpret_cast <void const*> (
(ContainerTraits <C>::get (m_c))));
}
template <class U>
explicit UserdataShared (U* u) : m_c (u)
{
m_p = const_cast <void*> (reinterpret_cast <void const*> (
(ContainerTraits <C>::get (m_c))));
}
};
template <class C, bool makeObjectConst>
struct UserdataSharedHelper
{
typedef typename TypeTraits::removeConst <
typename ContainerTraits <C>::Type>::Type T;
static void push (lua_State* L, C const& c)
{
if (ContainerTraits <C>::get (c) != 0)
{
new (lua_newuserdata (L, sizeof (UserdataShared <C>))) UserdataShared <C> (c);
lua_rawgetp (L, LUA_REGISTRYINDEX, ClassInfo <T>::getClassKey ());
assert (lua_istable (L, -1));
lua_setmetatable (L, -2);
}
else
{
lua_pushnil (L);
}
}
static void push (lua_State* L, T* const t)
{
if (t)
{
new (lua_newuserdata (L, sizeof (UserdataShared <C>))) UserdataShared <C> (t);
lua_rawgetp (L, LUA_REGISTRYINDEX, ClassInfo <T>::getClassKey ());
assert (lua_istable (L, -1));
lua_setmetatable (L, -2);
}
else
{
lua_pushnil (L);
}
}
};
template <class C>
struct UserdataSharedHelper <C, true>
{
typedef typename TypeTraits::removeConst <
typename ContainerTraits <C>::Type>::Type T;
static void push (lua_State* L, C const& c)
{
if (ContainerTraits <C>::get (c) != 0)
{
new (lua_newuserdata (L, sizeof (UserdataShared <C>))) UserdataShared <C> (c);
lua_rawgetp (L, LUA_REGISTRYINDEX, ClassInfo <T>::getConstKey ());
assert (lua_istable (L, -1));
lua_setmetatable (L, -2);
}
else
{
lua_pushnil (L);
}
}
static void push (lua_State* L, T* const t)
{
if (t)
{
new (lua_newuserdata (L, sizeof (UserdataShared <C>))) UserdataShared <C> (t);
lua_rawgetp (L, LUA_REGISTRYINDEX, ClassInfo <T>::getConstKey ());
assert (lua_istable (L, -1));
lua_setmetatable (L, -2);
}
else
{
lua_pushnil (L);
}
}
};
template <class C, bool byContainer>
struct StackHelper
{
static inline void push (lua_State* L, C const& c)
{
UserdataSharedHelper <C,
TypeTraits::isConst <typename ContainerTraits <C>::Type>::value>::push (L, c);
}
typedef typename TypeTraits::removeConst <
typename ContainerTraits <C>::Type>::Type T;
static inline C get (lua_State* L, int index)
{
return Userdata::get <T> (L, index, true);
}
};
template <class T>
struct StackHelper <T, false>
{
static inline void push (lua_State* L, T const& t)
{
UserdataValue <T>::push (L, t);
}
static inline T const& get (lua_State* L, int index)
{
return *Userdata::get <T> (L, index, true);
}
};
template <class T>
struct Stack
{
public:
static inline void push (lua_State* L, T const& t)
{
StackHelper <T,
TypeTraits::isContainer <T>::value>::push (L, t);
}
static inline T get (lua_State* L, int index)
{
return StackHelper <T,
TypeTraits::isContainer <T>::value>::get (L, index);
}
};
template <class T>
struct Stack <T*>
{
static inline void push (lua_State* L, T* const p)
{
UserdataPtr::push (L, p);
}
static inline T* const get (lua_State* L, int index)
{
return Userdata::get <T> (L, index, false);
}
};
template <class T>
struct Stack <T* const>
{
static inline void push (lua_State* L, T* const p)
{
UserdataPtr::push (L, p);
}
static inline T* const get (lua_State* L, int index)
{
return Userdata::get <T> (L, index, false);
}
};
template <class T>
struct Stack <T const*>
{
static inline void push (lua_State* L, T const* const p)
{
UserdataPtr::push (L, p);
}
static inline T const* const get (lua_State* L, int index)
{
return Userdata::get <T> (L, index, true);
}
};
template <class T>
struct Stack <T const* const>
{
static inline void push (lua_State* L, T const* const p)
{
UserdataPtr::push (L, p);
}
static inline T const* const get (lua_State* L, int index)
{
return Userdata::get <T> (L, index, true);
}
};
template <class T>
struct Stack <T&>
{
static inline void push (lua_State* L, T& t)
{
UserdataPtr::push (L, &t);
}
static T& get (lua_State* L, int index)
{
T* const t = Userdata::get <T> (L, index, false);
if (!t)
luaL_error (L, "nil passed to reference");
return *t;
}
};
template <class C, bool byContainer>
struct RefStackHelper
{
typedef C return_type;
static inline void push (lua_State* L, C const& t)
{
UserdataSharedHelper <C,
TypeTraits::isConst <typename ContainerTraits <C>::Type>::value>::push (L, t);
}
typedef typename TypeTraits::removeConst <
typename ContainerTraits <C>::Type>::Type T;
static return_type get (lua_State* L, int index)
{
return Userdata::get <T> (L, index, true);
}
};
template <class T>
struct RefStackHelper <T, false>
{
typedef T const& return_type;
static inline void push (lua_State* L, T const& t)
{
UserdataPtr::push (L, &t);
}
static return_type get (lua_State* L, int index)
{
T const* const t = Userdata::get <T> (L, index, true);
if (!t)
luaL_error (L, "nil passed to reference");
return *t;
}
};
template <class T>
struct Stack <T const&>
{
typedef RefStackHelper <T, TypeTraits::isContainer <T>::value> helper_t;
static inline void push (lua_State* L, T const& t)
{
helper_t::push (L, t);
}
static typename helper_t::return_type get (lua_State* L, int index)
{
return helper_t::get (L, index);
}
};