Generating C bindings through C++ templates?

Started by
4 comments, last by Antheus 12 years, 11 months ago
Ok, so I'm making a subsystem that can manipulate a GUI using Lua scripts by exposing a bunch of C++ member functions to the Lua script. However, since Lua is written in C, it only accepts C function pointers, not C++ pointer-to-member-functions.

The way I got around it was defining these macros:


#define LUA_CPP_NAME(CLASS,METHOD) luaExp_ ## CLASS ## _ ## METHOD

#define LUA_CPP_PROTO(CLASS,METHOD) int LUA_CPP_NAME(CLASS,METHOD)(lua_State* L)

#define LUA_CPP_EXPORT(CLASS,METHOD) \
LUA_CPP_PROTO(CLASS,METHOD) { \
CLASS* ptr = (CLASS*) lua_touserdata(L, 1); \
return ptr->METHOD(L); }


Ugly, yes. Now, the way I actually use it is like this:

(in the class header file)

class MyLuaClass
{
public:

// lua exports
int SomeLuaExport(lua_State* L);

private:

lua_State* mLua;
}


(in the class implementation)


#include "MyLuaClass.hpp"

MyLuaClass::MyLuaClass()
{
mLua = lua_open();
lua_pushlightuserdata(mLua, this); // this allows member functions to be called by the macro-generated functions
lua_register(mLua, "SomeLuaExport", LUA_CPP_NAME(LuaClassTest,SomeLuaExport)); // matches up the proper generated func name
}

MyLuaClass::~MyLuaClass()
{
lua_pop(mLua, 1); // remove the light user data "this" ptr
lua_close(mLua);
}

int MyLuaClass::SomeLuaExport(lua_State* L)
{
// this function gets called if lua code is executed that contains "SomeLuaExport()"
}


Now, ideally I want to reduce all of this to a single template call in the middle of lua_register, like this:


lua_register(mLua, "SomeLuaExport", MakeLuaExport<&MyLuaClass::SomeLuaExport>);


Unfortunately, this doesn't work as you can't pass a pointer-to-member function as a parameter to a template, apparently. So, I was wondering, is there a clean way of doing this or should I just stick to my ugly-but-works macro system?
Advertisement
Here is a way to make member function as static call:template < typename Base, typename R, typename P1, typename P2, R (Base::*f)(P1, P2) >
static R invoke(void * instance, P1 p1, P2 p2)
{
Base * typed_ptr = (Base*)instance;

return (typed_ptr.*f)(p1, p2);
}

The horror... the horror... But it works reliably.

So given:struct Foo {
int bar(float x, float y);
};


template < typename Base, typename R, typename P1, typename P2, R (B::*f)(P1, P2) >Template describes the syntax of the function call fully at compile-time.
Base is the type of instance we're invoking upon. Foo, in our case.
P1, P2, ..., Pn are the types of parameters. In our case we have two, both float.
R is return type. int
The last monstrosity is the pointer of member function, encoded as fully typed template parameter. That one remains the same.

Base * typed_ptr = (Base*)instance;void * is the opaque pointer to instance. In Lua it can be passed via stack so instead of passing void * to our instance one could pass LuaState * and work from there. Either way, once we have the instance pointer to our Foo, we cast it properly. No need for reinterpret_cast or similar, we're doing evil stuff so it wouldn't help.

return (typed_ptr.*f)(p1, p2); Just invocation of member function with parameters p1 and p2 returning type R.

The trick comes here:invoke<Foo, int, float, float, &Foo::bar>This is now a regular, plain C function, as long as no parameters use C++ classes.

---

I used to write a library to support this, callbacks looked like this:template < class T, typename R, typename P1, R (T::*f)(P1) >
class cbo1 : private Luna<T> {
public:
typedef cbo1<T,R,P1,f> type;
typedef Luna<T> base;
typedef typename base::RegType RegType;

type( lua_State * L ) : base(L,false) {}

static int invoke( lua_State * L ) { return type(L).invoke(); }
private:
int invoke()
{
// this pointer and 1 argument
if (base::gettop() != 2) base::method_error<RegType>("expects 1 argument");

T *ptr = base::checktopptr();
base::remove(1);


call<R>(ptr);
return lua::ReturnCount<R>::VALUE;
}

template < class RV > void call ( T * ptr ) {
R r( (ptr->*f)( base::as<P1>(1) ) );
base::pop(1);
base::push(r);
}
template < > void call<void>( T * ptr ) {
(ptr->*f)( as<P1>(1) );
base::pop(1);
}
};

To declare a pointer to function, one would do:lua::cbo1<Foo, void, int, &Foo::foo >::invokeObviously to a 'void Foo::foo(int)'.

Downside is obvious, one needs separate declaration for each different set of parameters and return values. It gets trickier since Lua supports multiple return parameters, but I opted not to support it.


Now pardon me, I have a sudden urge to go cry in a corner.
Whilst Antheus has covered the how, I would ask why?

[color="#1C2837"]Ok, so I'm making a subsystem that can manipulate a GUI using Lua scripts by exposing a bunch of C++ member functions to the Lua script. However, since Lua is written in C, it only accepts C function pointers, not C++ pointer-to-member-functions.
[color="#1C2837"]...
[color="#1C2837"]Unfortunately, this doesn't work as you can't pass a pointer-to-member function as a parameter to a template, apparently. So, I was wondering, is there a clean way of doing this or should I just stick to my ugly-but-works macro system?
[color="#1C2837"][/quote]
[color="#1C2837"]You do know that there are plenty of libraries to do this for you? Some use macro madness (cough) and/or metaprogramming madness :D

[color="#1C2837"]See the wiki for a list of libraries in addition there was another released today

[color="#1C2837"]Note: For some reason the wiki link is refusing to work with gamedev linking http://lua-users.org/wiki/BindingCodeToLua

Whilst Antheus has covered the how, I would ask why?

[color="#1C2837"]Ok, so I'm making a subsystem that can manipulate a GUI using Lua scripts by exposing a bunch of C++ member functions to the Lua script. However, since Lua is written in C, it only accepts C function pointers, not C++ pointer-to-member-functions.
[color="#1C2837"]...
[color="#1C2837"]Unfortunately, this doesn't work as you can't pass a pointer-to-member function as a parameter to a template, apparently. So, I was wondering, is there a clean way of doing this or should I just stick to my ugly-but-works macro system?
[color="#1C2837"]

[color="#1C2837"]You do know that there are plenty of libraries to do this for you? Some use macro madness (cough) and/or metaprogramming madness :D

[color="#1C2837"]See the wiki for a list of libraries in addition there was another released today

[color="#1C2837"]Note: For some reason the wiki link is refusing to work with gamedev linking http://lua-users.org...indingCodeToLua
[/quote]

The why I'm reinventing the wheel is two-fold. First off, I don't really need a "big" class interface between my code and Lua, so I'm just making it as simple as possible. Two, I'm doing it as a learning experience because I've always wanted to know what goes on behind the scenes when embedding a scripting language, and Lua is the easiest to embed from what I've seen. Hell, I'll probably end up using one of the existing solutions on the lua-users wiki when my code grows too complex for my simple wrapper (mainly what I'm envisioning is just being able to call methods and do event callbacks, not really using classes in Lua), but for now I'm just using it mainly as a learning experience.
Yes Lua is supper easy to embed and that was one of it's goals which the redis developer just learnt recently [1]. Whilst writing your own is an excellent way of learning what is needed, I thought you were:

making a subsystem that can manipulate a GUI using Lua[/quote]

Personally I would grab something like LunaWrapper [2] and could then be using/modifying it in 10 minutes. As you see from Antheus's code and the origin of the library in my signature. we are both standing on the shoulders of giants :)

class cbo1 : private Luna<T> {




[1] http://twitter.com/#...646964494598145
[2] http://lua-users.org/wiki/LunaWrapper

Personally I would grab something like LunaWrapper [2] and could then be using/modifying it in 10 minutes. As you see from Antheus's code and the origin of the library in my signature. we are both standing on the shoulders of giants :)

class cbo1 : private Luna<T> {




I don't recall why I used custom wrapper over Luna (it's probably 3 or more years since I wrote that code), but I think there were two available at the time with one being outdated in then-current version of Lua. Luna<T> was just a typesafe stateful wrapper over LuaState.

In retrospect, if doing it again I'd probably just learn lua_ API and use that directly. The C++ looks fancy, but at the end of the day it doesn't buy enough for all the boilerplate it needs, not to mention the inability to conveniently support multiple return values. I remember there were also some lifecycle issues that do not map conveniently to RAII or auto-allocation. And the user types should ideally be PODs. And then there's the numeric types...

Lua API maps cleanly to C++ encapsulation, but some of the concepts do not, or at least they didn't. The biggest gain is probably in error checking and type safety, but as said, boilerplate is a killer.

This topic is closed to new replies.

Advertisement