在void *和指向成员函数的指针之间进行转换

时间:2009-08-20 16:14:08

标签: c++ pointers lua pointer-to-member

我目前正在使用GCC 4.4,而且我在void*和指向成员函数的指针之间投入了很多头痛。我正在尝试编写一个易于使用的库,用于将C ++对象绑定到Lua解释器,如下所示:

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

我已经完成了大部分工作,除了以下函数(特定于某个函数签名,直到我有机会对其进行概括):

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;
}

对于那些不熟悉Lua的人,lua_touserdata(L, lua_upvalueindex(1))获取与闭包关联的第一个值(在这种情况下,它是指向成员函数的指针)并将其作为void*返回。海湾合作委员会抱怨void* - &gt; void (T::*)(int, int)是一个无效的演员。关于如何解决这个问题的任何想法?

5 个答案:

答案 0 :(得分:20)

cannot cast a pointer-to-member to void *或任何其他“常规”指针类型。指向成员的指针不是常规指针的方式。您最有可能需要做的是将您的成员函数包装在常规函数中。 C ++ FAQ Lite explains this有一些细节。主要问题是实现指向成员指针所需的数据不仅仅是一个地址,实际上varies tremendously基于编译器实现。

我认为您可以控制用户数据lua_touserdata返回的内容。它不能成为指向成员的指针,因为没有合法的方法来获取此信息。但你确实有其他选择:

  • 最简单的选择可能是将您的成员函数包装在一个自由函数中并返回它。该自由函数应该将对象作为其第一个参数。请参阅下面的代码示例。

  • 使用类似于Boost.Bind's mem_fun的技术返回一个函数对象,您可以适当地模板化。我不认为这更容易,但如果需要,它会让你将更多的状态与函数返回相关联。

这是使用第一种方式重写函数:

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;
}

答案 1 :(得分:7)

可以使用联合将指针转换为成员函数和属性:

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

要进行转换,请将源值放入一个成员中,然后将目标值拉出另一个成员。

虽然这种方法很实用,但我不知道它是否适用于所有情况。

答案 2 :(得分:1)

作为一种解决方法,考虑到将指针指向成员函数转换为void*的限制,您可以将函数指针包装在一个小的堆分配结构中,并在Lua用户数据中放置一个指向该结构的指针:

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;
}

我不熟悉Lua所以我可能忽略了上面例子中的一些东西。请记住,如果你走这条路线,你将不得不管理LuaUserData的分配。

答案 3 :(得分:1)

非静态成员函数的地址不同,后者是具有复杂表示的指向成员的类型,静态成员函数的地址通常是只是一个机器地址,与转换为void *兼容。

如果需要将C ++非静态成员函数绑定到基于void *的C或C类回调机制,那么您可以尝试编写静态包装器。

包装器可以将指向实例的指针作为参数,并将控制传递给非静态成员函数:

void myclass::static_fun(myclass *instance, int arg)
{
   instance->nonstatic_fun(arg);
}

答案 4 :(得分:1)

在这里,只需更改函数void_cast的参数即可满足您的需求:

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

使用示例:

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