可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
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?
回答1:
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;
}
回答2:
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.
回答3:
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.
回答4:
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);
}
回答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);