Allow binding methods in Lua classes

This commit is contained in:
Ilari Liusvaara 2012-07-11 15:17:12 +03:00
parent 70fdf31ad0
commit c6c640bdbc

View file

@ -69,6 +69,11 @@ private:
lua_obj_pin& operator=(const lua_obj_pin&); lua_obj_pin& operator=(const lua_obj_pin&);
}; };
template<class T> struct lua_class_bind_data
{
int (T::*fn)(lua_State* LS);
};
template<class T> class lua_class; template<class T> class lua_class;
template<class T> lua_class<T>& objclass(); template<class T> lua_class<T>& objclass();
@ -102,6 +107,7 @@ public:
lua_pushvalue(LS, -3); lua_pushvalue(LS, -3);
lua_pushvalue(LS, -3); lua_pushvalue(LS, -3);
lua_rawset(LS, -3); lua_rawset(LS, -3);
lua_pop(LS, 1);
} }
void bind(lua_State* LS, const char* keyname) void bind(lua_State* LS, const char* keyname)
@ -110,6 +116,36 @@ public:
lua_pushstring(LS, keyname); lua_pushstring(LS, keyname);
lua_pushvalue(LS, -3); lua_pushvalue(LS, -3);
lua_rawset(LS, -3); lua_rawset(LS, -3);
lua_pop(LS, 1);
}
static int class_bind_trampoline(lua_State* LS)
{
lua_class_bind_data<T>* b = (lua_class_bind_data<T>*)lua_touserdata(LS, lua_upvalueindex(1));
const char* fname = lua_tostring(LS, lua_upvalueindex(2));
T* p = lua_class<T>::get(LS, 1, fname);
return (p->*(b->fn))(LS);
}
void bind(lua_State* LS, const char* keyname, int (T::*fn)(lua_State* LS))
{
load_metatable(LS);
lua_pushstring(LS, keyname);
lua_rawget(LS, -2);
if(!lua_isnil(LS, -1)) {
lua_pop(LS, 2);
return;
}
lua_pop(LS, 1);
lua_class_bind_data<T>* bdata = new lua_class_bind_data<T>;
bdata->fn = fn;
lua_pushstring(LS, keyname);
lua_pushlightuserdata(LS, bdata);
std::string name = std::string("Method ") + keyname;
lua_pushstring(LS, name.c_str());
lua_pushcclosure(LS, class_bind_trampoline, 2);
lua_rawset(LS, -3);
lua_pop(LS, 1);
} }
T* _get(lua_State* LS, int arg, const char* fname, bool optional = false) T* _get(lua_State* LS, int arg, const char* fname, bool optional = false)
@ -121,10 +157,12 @@ public:
goto badtype; goto badtype;
} }
load_metatable(LS); load_metatable(LS);
if(!lua_getmetatable(LS, arg)) if(!lua_getmetatable(LS, arg)) {
goto badtype; goto badtype;
if(!lua_rawequal(LS, -1, -2)) }
if(!lua_rawequal(LS, -1, -2)) {
goto badtype; goto badtype;
}
lua_pop(LS, 2); lua_pop(LS, 2);
return reinterpret_cast<T*>(lua_touserdata(LS, arg)); return reinterpret_cast<T*>(lua_touserdata(LS, arg));
badtype: badtype:
@ -176,6 +214,20 @@ private:
obj->~T(); obj->~T();
} }
static int newindex(lua_State* LS)
{
lua_pushstring(LS, "Writing metatables of classes is not allowed");
lua_error(LS);
}
static int index(lua_State* LS)
{
lua_getmetatable(LS, 1);
lua_pushvalue(LS, 2);
lua_rawget(LS, -2);
return 1;
}
void load_metatable(lua_State* LS) void load_metatable(lua_State* LS)
{ {
again: again:
@ -185,9 +237,17 @@ again:
lua_pop(LS, 1); lua_pop(LS, 1);
lua_pushlightuserdata(LS, this); lua_pushlightuserdata(LS, this);
lua_newtable(LS); lua_newtable(LS);
lua_pushvalue(LS, -1);
lua_setmetatable(LS, -2);
lua_pushstring(LS, "__gc"); lua_pushstring(LS, "__gc");
lua_pushcfunction(LS, &lua_class<T>::dogc); lua_pushcfunction(LS, &lua_class<T>::dogc);
lua_rawset(LS, -3); lua_rawset(LS, -3);
lua_pushstring(LS, "__newindex");
lua_pushcfunction(LS, &lua_class<T>::newindex);
lua_rawset(LS, -3);
lua_pushstring(LS, "__index");
lua_pushcfunction(LS, &lua_class<T>::index);
lua_rawset(LS, -3);
lua_rawset(LS, LUA_REGISTRYINDEX); lua_rawset(LS, LUA_REGISTRYINDEX);
goto again; goto again;
} }