So, hows this all work then??
#define mbind_both(obj, ret, name, ...) \
typedef ret (obj::*name_both) (__VA_ARGS__); \
static int name(lua_State *L) { \
return mcall<obj, name_both, ret, __VA_ARGS__>(L, *obj::getInstance(), &obj::name);\
}
The macro calls a function template determined by the number of arguments. This one has a return and arguments; the ones with no return need to be separate. It creates a method pointer based on this information and passes it as a template argument. The function takes the lua state, an object of the class (this could be a bit restrictive), and a reference to the method which conforms to the method pointer format.
template<typename Obj, typename F, typename ret, typename T>
static int mcall(lua_State *L, Obj &o, F func) {
int i = 0;
T one = luaGet<T>(L, ++i);
return luaReturn<ret>(L, CALL_MEMBER_FN(o,func)(one));
}
There are several function templates in this format which vary the number of argument types. I've done 5 so far (I don't think we would need many more). Each type is passed as an argument to another function template which returns the desired type each time incrementing the index. The member function is called with the given arguments and the result passed to luaReturn function template. I think the number of arguments could be checked in these functions.
template<typename T>
T luaGet(lua_State *L, int ndx);
template<>
int luaGet<int>(lua_State *L, int ndx) {
return luaL_checkint(L, ndx);
}
Each type is determined by a template specialization. Error checking could be added here and used as a wrapper for other types like LuaScript::getStringSet. The returns work in a similar way.
template<typename T>
int luaReturn(lua_State *L, T val) {return 0;}
template<>
int luaReturn<int>(lua_State *L, int val) {
lua_pushinteger(L, val);
return 1;
}
The number of values returned to lua is returned by the function.
The full code.
but do we lose all the nice error reporting? (wrong number of args or bad arg type).
At the moment yes, but I think it can be added in.
If we can make this work, I think we can probably adapt for entities too, they all have factories now, and they all have ids, so from an id we can get the instance from the appropriate factory, which would replace the Class::getInstance() for singletons.
Yer, any way to get an object should work.
A problem is you can only use it for one overloaded method since it uses the name for the static lua function. I looked at
MLuaBind which is like LuaBind but uses a lightweight Boost-like library. It uses a dispatch system. I could probably get a simple method dispatcher working so we could register in one call and not need the macros. I'm reading through
Programming in Lua now.
From MLuaBind:
int GenericClass::LuaMethodCallProxieFunction(lua_State *L)
{
LuaCustomVariable *lcv = (LuaCustomVariable *)lua_touserdata(L, lua_upvalueindex(1));
const char *name = (const char*)lua_touserdata(L, lua_upvalueindex(2));
const GenericClass *gc = lcv->getclass();
assert(gc != 0);
MethodsT::const_iterator i = gc->m_Methods.find(name);
if(i != gc->m_Methods.end())
{
return lcv->getclass()->MethodCallDispatcher(CHost::GetFromLua(L), L, lcv, name, (*i).second);
}
else
{
CHost::GetFromLua(L)->Error("GenericClass::LuaMethodCallProxieFunction: panic - can't find GenericMethod %s!\n", name);
return 0;
}
};
int GenericClass::MethodCallDispatcher(CHost *_host, lua_State *L, LuaCustomVariable *_lcv, const char *_name, const std::vector<GenericMethod*> &_overloads) const
{
...
return overloads[0]->PerformCall(_host, L, _lcv);
};
Update 23 January:
I have one line method registration working. I haven't bothered with functions this time. It still requires the class to be static and have getInstance method. Error checking still needs to be implemented. The id of methods is currently the name so that will need to change to be unique.
ScriptManager sm(L);
sm.register_method("display", &Gui::display);
Overloaded methods will need to use their full prototype.
The full code of v2.