Automatically expose any (simple) C++ function to Lua

Started by
3 comments, last by Luth 16 years, 10 months ago
What I want to do is take any one, two, or three parameter function, such as: void foo(int i); double bar(string s, int i); and expose it to Lua without having to manually write a glue wrapper for it. Its pretty easy to conceptualize, but I'm having a hell of a time with the implementation. First and foremost, is it possible? Second, which is the approach to take? I tried writing a proxy function, but couldn't get that working. Well, actually, it worked, so long as I hardcoded the parameter retrieval states. (ie: P1 param1 = lua_tostring(L, -1); ) Which works if P1 is always a string, but otherwise its useless. I tried writing a macro to do it, using #names to try to differentiate between what to do, but it wouldn't compile. I tried writing a wrapper class that would contain a pointer to the function, and return/parameter types, but couldn't get that working either; registering a class member as a non-class function is beyond my abilities to date. I need some sort of generic wrapper function or class to act as the glue. The closest I got is (for a single param function returning an int [look at the restrictions already, yuck]):
template<typename P1, int (*pfunc)(P1)>
int IntegerReturnProxy(lua_State* L)
{
	P1 _param1 = lua_tostring(L, -1);
	lua_pop(L, 1);

	lua_pushinteger(L,  pfunc(_param1) );

	return 1;
}
If you didn't notice (I'm sure you did), the problem is the line: lua_tostring(L, -1); It has to assume the type in order to pop it off the stack in the appropriate way. Really, any advice at this point would be great. I'd like to write this myself instead of using a big library because I want to understand how it works, and be able to duplicate it again should the need arise. Its about the learning, not the results. :) Thanks.
Advertisement
I considered a registration function for my uses, something like:
typedef void *(*luaCallback_t)(void *, ...);void bindFunc(luaCallback_t func, char *name, char *sig)
Where sig is a signature similar to Programming in Lua 25.3. bindFunc could bind everything on the lua side to a single callback, which keeps track of that data about the functions, and determines which is being called and pops arguments/calls the func correctly. Unfortunately I couldn't find a way to get the name of the function being called, so that plan kinda fell on its face.

Of course, my project was in pure C. You may have an easier time with C++.
Here's a clue.

Change this:
P1 _param1 = lua_tostring(L, -1);

To this:

P1 _param1;
get_lua_value(P1, -1);

Then implement these functions:

void get_lua_value(int& returnValue, int stackPosition){    returnValue = lua_tonumber(L, -1);}void get_lua_value(string& returnValue, int stackPosition){    returnValue = lua_tostring(L, -1);}...more overloads (or template specialisations)...


Then you just need to write 4 template functions (one for no arguments, one for 1 argument, and for 2 and 3), each of which pulls the relevant types off the stack and converts them, calls the function in question, and pushes the return value back onto the Lua stack (again, an overloaded 'push_cpp_variable_onto_lua_stack' type function helps here). Then binding each function should be a one liner:

fooWrapper = oneParamFunction<void, int>(foo);
barWrapper = twoParamFunction<double, string, int>(bar);

There are probably some ways to automatically deduce whether to use oneParamFunction or twoParamFunction (etc), but I doubt they're worth the effort.

Bear in mind that you may require special cases for functions that return void as you can't instantiate a void object in order to return it, at least not on all compilers.
There is a program out there called ToLua++ that will convert a properly annotated .h file into a complete
wrapper .cpp file that you just have to include with the project. (and call the 'init' function for, but that is trivial)
That way you just write code, and don't have to deal with any wrapper writing at all.
AND it handles data types and classes seamlessly.
KulSeran: Yeah, but that doesn't teach me much. :) This is about learning, not achieving results. (I know, weird, right?)

Kylotan: Oh, why didn't I think of that. Thats exactly what I needed. Its working great, now. Thanks a ton!

int foo(string s){ cout<<s<<endl; return 0; }double bar(string s){ cout<<"|"<<s<<"|"<<endl; return 1.23;}int foo2(int i, string s){ cout<<s<<"{"<<i<<")"<<endl; return (int)s.size(); }


void GetLuaValue(long& ret, lua_State* L, int stackPos){ ret = (long)lua_tointeger(L, stackPos); }...void GetLuaValue(double& ret, lua_State* L, int stackPos){ ret = (double)lua_tonumber(L, stackPos); }...void GetLuaValue(bool& ret, lua_State* L, int stackPos){ ret = (lua_toboolean(L, stackPos) != 0); }void GetLuaValue(string& ret, lua_State* L, int stackPos){ ret = lua_tostring(L, stackPos); }


void ProxyReturn(lua_State* L, double& retVal){ lua_pushnumber(L, retVal); }...void ProxyReturn(lua_State* L, long& retVal){ lua_pushinteger(L, retVal); }...void ProxyReturn(lua_State* L, bool& retVal){ lua_pushboolean(L, retVal); }void ProxyReturn(lua_State* L, string& retVal){ lua_pushstring(L, retVal.c_str()); }


template<typename RetType, typename P1, RetType (*pfunc)(P1)>int Proxy(lua_State* L){	P1 _param1;	GetLuaValue(_param1, L, -1);	lua_pop(L, 1);	RetType _ret = pfunc(_param1);	ProxyReturn(L, _ret);	return 1;}template<typename RetType, typename P1, typename P2, RetType (*pfunc)(P1, P2)>int Proxy(lua_State* L){	P1 _param1;	P2 _param2;	GetLuaValue(_param1, L, -2);	GetLuaValue(_param2, L, -1);	lua_pop(L, 2);	RetType _ret = pfunc(_param1, _param2);	ProxyReturn(L, _ret);	return 1;}...


In main()
LuaFunc Wfoo = &Proxy<int,string,foo>;lua_register(_pLuaBinder->GetLuaState(), "foo", Wfoo);LuaFunc Wbar = &Proxy<double,string,bar>;lua_register(_pLuaBinder->GetLuaState(), "bar", Wbar);LuaFunc Wfoo2 = &Proxy<int,int,string,foo2>;lua_register(_pLuaBinder->GetLuaState(), "foo2", Wfoo2);

This topic is closed to new replies.

Advertisement