views:

651

answers:

2

Hello Stackoverflow,

I'm currently using GCC 4.4, and I'm having quite the headache casting between void * and a pointer to member function. I'm trying to write an easy-to-use library for binding C++ objects to a Lua interpreter, like so:

LuaObject<Foo> lobj = registerObject(L, "foo", fooObject);
lobj.addField(L, "bar", &Foo::bar);

I've got most of it done, except for the following function (which is specific to a certain function signature until I have a chance to generalize it):

template <class T>
int call_int_function(lua_State *L) 
{
    // this next line is problematic
    void (T::*method)(int, int) = reinterpret_cast<void (T::*)(int, int)>(lua_touserdata(L, lua_upvalueindex(1)));
    T *obj = reinterpret_cast<T *>(lua_touserdata(L, 1));

    (obj->*method)(lua_tointeger(L, 2), lua_tointeger(L, 3));
    return 0;
}

For those of you unfamiliar with Lua, lua_touserdata(L, lua_upvalueindex(1)) gets the first value associated with a closure (in this case, it's the pointer to member function) and returns it as a void *. GCC complains that void * -> void (T::*)(int, int) is an invalid cast. Any ideas on how to get around this?

Thanks, Rob

+7  A: 

You cannot cast a pointer-to-member to void * or to any other "regular" pointer type. Pointers-to-members are not addresses the way regular pointers are. What you most likely will need to do is wrap your member function in a regular function. The C++ FAQ Lite explains this in some detail. The main issue is that the data needed to implement a pointer-to-member is not just an address, and in fact varies tremendously based on the compiler implementation.

I presume you have control over what the user data lua_touserdata is returning. It can't be a pointer-to-member since there isn't a legal way to get this information back out. But you do have some other choices:

  • The simplest choice is probably to wrap your member function in a free function and return that. That free function should take the object as its first argument. See the code sample below.

  • Use a technique similar to that of Boost.Bind's mem_fun to return a function object, which you can template on appropriately. I don't see that this is easier, but it would let you associate the more state with the function return if you needed to.

Here's a rewrite of your function using the first way:

template <class T>
int call_int_function(lua_State *L) 
{
    void (*method)(T*, int, int) = reinterpret_cast<void (*)(T*, int, int)>(lua_touserdata(L, lua_upvalueindex(1)));
    T *obj = reinterpret_cast<T *>(lua_touserdata(L, 1));

   method(obj, lua_tointeger(L, 2), lua_tointeger(L, 3));
   return 0;
}
quark
I don't believe pointer to member functions are any different from normal pointers. What makes you think they have special properties? They just point at a piece of code.
Martin York
Martin I got schooled on this point on SO recently. Let me point you at the discussion here: http://stackoverflow.com/questions/1207106/calling-base-class-definition-of-virtual-member-function-with-function-pointer/1207396#1207396.
quark
Martin: Also read the link marked "varied tremendously": http://www.codeproject.com/KB/cpp/FastDelegate.aspx. Pointers to member functions are not necessarily implemented as pointers at all, nor even the same way from system to system. They can be combinations of a table and an index, full on thunks or any of a number of variant implementations.
quark
SPoke too quickly. Re-cant.
Martin York
So did I in that original question :). It's clearly a subtle subject.
quark
A: 

As a workaround given the restrictions of casting a pointer-to-member-function to void* you could wrap the function pointer in a small heap-allocated struct and put a pointer to that struct in your Lua user data:

template <typename T>
struct LuaUserData {
    typename void (T::*MemberProc)(int, int);

    explicit LuaUserData(MemberProc proc) :
        mProc(proc)
    { }

    MemberProc mProc;
};

LuaObject<Foo> lobj = registerObject(L, "foo", fooObject);
LuaUserData<Foo>* lobj_data = new LuaUserData<Foo>(&Foo::bar);

lobj.addField(L, "bar", lobj_data);

// ...

template <class T>
int call_int_function(lua_State *L) 
{
    typedef LuaUserData<T>                       LuaUserDataType;
    typedef typename LuaUserDataType::MemberProc ProcType;

    // this next line is problematic
    LuaUserDataType* data =
        reinterpret_cast<LuaUserDataType*>(lua_touserdata(L, lua_upvalueindex(1)));
    T *obj = reinterpret_cast<T *>(lua_touserdata(L, 1));

    (obj->*(data.mMemberProc))(lua_tointeger(L, 2), lua_tointeger(L, 3));
    return 0;
}

I'm not savvy with Lua so I have likely overlooked something in the above example. Keep in mind, too, if you go this route you'll have to manage the LuaUserData's allocation.

fbrereto