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

13 c++ lua pointers pointer-to-member

我目前正在使用GCC 4.4,而且我之间void*有一个令人头痛的问题,并且指向成员函数.我正在尝试编写一个易于使用的库,用于将C++对象绑定到Lua解释器,如下所示:

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

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

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;
}
Run Code Online (Sandbox Code Playgroud)

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

qua*_*ark 21

不能将指向成员的指针强制转换void *为任何其他"常规"指针类型.指向成员的指针不是常规指针的方式.您最有可能需要做的是将您的成员函数包装在常规函数中.C++ FAQ Lite 详细解释了这一点.主要问题是实现指向成员指针所需的数据不仅仅是一个地址,实际上根据编译器实现而有很大差异.

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

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

  • 使用类似于Boost.Bind的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;
}
Run Code Online (Sandbox Code Playgroud)

  • 马丁:还阅读标记为"变化巨大"的链接:http://www.codeproject.com/KB/cpp/FastDelegate.aspx.指向成员函数的指针根本不必实现为指针,甚至从系统到系统也不一样.它们可以是表和索引的组合,完全在thunk上或任何多种变体实现中. (2认同)

pan*_*niq 9

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

// helper union to cast pointer to member
template<typename classT, typename memberT>
union u_ptm_cast {
    memberT classT::*pmember;
    void *pvoid;
};
Run Code Online (Sandbox Code Playgroud)

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

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

  • 我相信成员指针实际上是大型对象; 它很可能是`sizeof(void*)<sizeof(memberT classT ::*)`; 因此`pvoid`不能代表所有`pmember`. (2认同)
  • 我已经在 MSVC 十一月 CTP 中尝试过这种方法。我添加了一个 `static_assert` 以确保 `sizeof(u_ptm_cast::pmember) == sizeof(u_ptm_cast::pvoid)`。它_似乎_可以在大多数情况下工作,例如,当 pmember 是非虚拟的、虚拟的或当转换回成员函数指针时`classT` 被替换为不同的类时。但是,如果`classT` 使用多重继承,则指针确实更大并且断言失败。 (2认同)

小智 5

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

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

使用示例:

auto pvoid = void_cast(&Foo::foo);
Run Code Online (Sandbox Code Playgroud)