20

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?

Jarod42
  • 203,559
  • 14
  • 181
  • 302
  • 6
    http://www.parashift.com/c++-faq-lite/pointers-to-members.html – Sean Bright Aug 20 '09 at 16:16
  • +1 the above... specifically section 33.7 & 33.8 – fbrereto Aug 20 '09 at 16:24
  • 1
    Just out of curiosity, why are you trying to store C functions in Lua userdata like that? there's probably a safer way to accomplish your goal. – Alex Aug 21 '11 at 17:05
  • You can use asm to do the job if you know the platform. C++ doesn't bitch about it then. Useful in some cases(such as testing code). Simply store the func ptr in a var, create a void*, go into asm and copy the value over. – AbstractDissonance Jul 17 '16 at 04:54

5 Answers5

26

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;
}
zyndor
  • 1,418
  • 3
  • 20
  • 36
quark
  • 15,590
  • 3
  • 42
  • 30
  • 1
    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 Aug 20 '09 at 16:42
  • 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 Aug 20 '09 at 16:44
  • 2
    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 Aug 20 '09 at 16:45
  • So did I in that original question :). It's clearly a subtle subject. – quark Aug 20 '09 at 16:54
11

It is possible to convert pointer to member functions and attributes using unions:

// helper union to cast pointer to member
template<typename classT, typename memberT>
union u_ptm_cast {
    memberT classT::*pmember;
    void *pvoid;
};

To convert, put the source value into one member, and pull the target value out of the other.

While this method is practical, I have no idea if it's going to work in every case.

paniq
  • 1,109
  • 1
  • 11
  • 19
  • 7
    I believe member-pointers are actually large-ish objects; it's very likely `sizeof(void*) < sizeof(memberT classT::*)`; thus `pvoid` can not represent all of `pmember`. – Charles L Wilcox Feb 15 '13 at 18:32
  • 2
    I've tried this approach with the MSVC November CTP. I added a `static_assert` to ensure that `sizeof(u_ptm_cast::pmember) == sizeof(u_ptm_cast::pvoid)`. It _appears_ to work in most situations, e.g. when pmember is non-virtual, virtual or when `classT` is substituted for a different class when converting back to a member function pointer. However, if `classT` employs multiple inheritance, the pointer is indeed larger and the assert fails. – John McFarlane Mar 15 '13 at 06:55
  • 1
    Under GCC 4.7, all member function pointers appear to be the size of two regular pointers. Accounting for this by making `pvoid` an array of double the size, @paniq's technique involving a `union` works for the same cases as with the MS compiler. Overall though, it's a pretty fragile solution and not much use in the Lua binding problem. – John McFarlane Mar 20 '13 at 12:32
  • @JohnMcFarlane You think if adding #pragma pointers_to_members( full_generality, multiple_inheritance ) would work if classT employ multiple inheritance? https://msdn.microsoft.com/en-us/library/83cch5a6.aspx – Matthew Beck May 16 '18 at 21:42
  • Maybe, but I would follow @quark's advice to get something truly portable. – John McFarlane May 19 '18 at 11:26
  • How can I invoke the member function if I have the class object? MyClass->pvoid ? – schanti schul Aug 21 '22 at 09:26
5

Here, just change the parameters of the function void_cast for it to fit your needs:

template<typename T, typename R>
void* void_cast(R(T::*f)())
{
    union
    {
        R(T::*pf)();
        void* p;
    };
    pf = f;
    return p;
}

example use:

auto pvoid = void_cast(&Foo::foo);
1

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
  • 35,429
  • 19
  • 126
  • 178
1

Unlike the address of a nonstatic member function, which is a pointer-to-member type with a complicated representation, the address of a static member function is usually a just a machine address, compatible with a conversion to void *.

If you need to bind a C++ non-static member function to a C or C-like callback mechanism based on void *, what you can try to do is write a static wrapper instead.

The wrapper can take a pointer to an instance as an argument, and pass control to the nonstatic member function:

void myclass::static_fun(myclass *instance, int arg)
{
   instance->nonstatic_fun(arg);
}
Kaz
  • 55,781
  • 9
  • 100
  • 149
  • I've seen this pattern in various libraries- but how is the instance pointer populated in the callback function? Compiler magic? – andig Jul 14 '16 at 17:04
  • 1
    @andig The callback interface has to have a context argument for the passage of registered user data. I.e. "please pass me my pointer when you call me back". If the callback interface doesn't have this, it can be hacked with trampolines. You can put a small piece of machine code (the trampoline) at the front of the callback object, and that machine code is used as the callback function. It calculates the object pointer relative to its instruction pointer, knowing that the object is at a fixed offset from its own address. Then it calls the real function. – Kaz Jul 14 '16 at 18:01