Binding luabind::object to a boost::function

classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view

Binding luabind::object to a boost::function

Reko Tiira

I've been implementing lua as a scripting language on top of set of classes, which I want to be able to be used from lua.

Everything is working good so far, and luabind has made my job a lot easier than I thought. Now I want to do something like this.. I have a set of functions that register event callbacks in my class, the callbacks are simply instances of boost::function. I want to be able to transparently bind a function from lua into a boost::function so that I can register lua functions as callbacks.

I noticed that luabind::object has overloads for operator() so it can call a lua function directly, so I attempted this first:

// obj is a luabind::object
boost::function<void (int)> func = obj;

This works very well, and I thought that this is going to be easier than I thought. However the first issue was apparent when I tried to add a return value:

boost::function<int (int)> func = obj;
int res = func(100);

Now this doesn't work anymore. I noticed that the reason is because the proxy object that operator() returns, will return another luabind::object as a result, instead of the actual C++ datatype. To make sure I understood what I was seeing correctly, I attempted the following:

boost::function<luabind::object (int)> func = obj;
int n = luabind::object_cast<int>(func(50));

This indeed worked and gave the expected result. However the problem is that for this to work I'd have to go back to the existing classes and change the callbacks to return luabind::object instead, which would break existing code. Additionally I want lua to just be an additional component, the classes should still be available to be used directly from C++, and I definitely don't want to maintain two lists of callbacks (one for C++, one for lua). I absolutely want to get this done transparently to the original class.

Next thing I noticed was that luabind::call_function lets me automatically convert the return value to the type I want, like this:

int n = luabind::call_function<int>(obj, 100);

So I attempted to get this bound to boost::function next. With some boost::bind magic I should be able to make this work. My first attempt was one without passing parameters like this:

boost::function<int ()> func = boost::bind(&luabind::call_function<int>, obj);
int n = func();

This indeed worked as intended, but next I wanted to add passing parameters to the callback. I tried:

boost::function<int (int)> func = boost::bind(&luabind::call_function<int>, obj, _1);
int n = func(100);

However things didn't go so smoothly, I got the following error:

error C2664: 'luabind::detail::proxy_function_caller<Ret,Tuple> (lua_State *,const char *)' : cannot convert parameter 2 from 'int' to 'const char *'

Apparently it picked the wrong overload for luabind::call_function<int>. Looking at the signature of call_function, I can see that it returns some whacky detail function wrapper, that nobody right in their mind would want to cast to by hand. So I attempted to resolve the issue with the new C++0x decltype operator:

typedef decltype(luabind::call_function<int>(obj, int())) ret_type;
boost::function<int (int)> func = boost::bind((ret_type (*)(const luabind::object&, const int&))&luabind::call_function<int>, obj, _1);
int n = func(100);

This indeed works well! However I'm not very satisfied with this solution because first of all it requires decltype operator to work, and nobody wants to write the actual return type of the expression by hand. It also feels a bit too difficult way to do it. There must be a better way, seeing how luabind already provides two different facilities:

1) luabind::object operator() that can pass arguments to boost::function just fine but can't deal with return type transparently.
2) luabind::call_function that can return the value just fine, but can't deal with the parameters due to boost::bind not being able to deduce the correct overload automatically.

My question is that is there any better way to do it that is already supported by luabind. I did come up with a solution that worked for me, but I really think it should be part of luabind core, instead of having to do this myself. What I did in the end was something like this:

template <typename T> struct function { function(const luabind::object& obj) : m_func(obj) { if (luabind::type(obj) != LUA_TFUNCTION) { throw std::invalid_argument("invalid lua object"); } } T operator()() { return luabind::call_function<T>(m_func); } template <typename A1> T operator()(const A1& a) { return luabind::call_function<T>(m_func, a); }

// ... bunch of more overloads for operator() private: luabind::object m_func; };

Now I can just do:

boost::function<int (int)> func = function<int>(obj); int n = func(100);

Hence I have a nice and clean solution for transparently binding lua functions in C++ to existing callback mechanisms.

If this is not possible out of the box in luabind, I strongly suggest it to be added, as I'd think it is something many lua embedders tackle with and would appreciate. I understand the need of luabind::object operator() returning luabind::object since you have to be able to deal with values that are not convertible to C++, hence it'd be awesome to have something like luabind::function to provide this extra functionality to luabind::object, without having to do insane casts for overload resolution on luabind::call_function.

On an unrelated note, can someone explain to me why luabind::object's operator() is not callable on const objects? For example:

void foo(const luabind::object& obj)
luabind::call_function<void>(obj); // works
obj(); // doesn't

For the later to work you have to pass by luabind::object. I know it's cheap to copy these objects since all they do is hold a reference to lua's registry, but I'm still curious

Reko Tiira

Achieve unprecedented app performance and reliability
What every C/C++ and Fortran developer should know.
Learn how Intel has extended the reach of its next-generation tools
to help boost performance applications - inlcuding clusters.
luabind-user mailing list
[hidden email]