Приведение между void * и указателем на функцию-член - PullRequest
13 голосов
/ 20 августа 2009

В настоящее время я использую 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*. GCC жалуется, что void* -> void (T::*)(int, int) является недействительным приведением. Есть идеи как обойти это?

Ответы [ 5 ]

20 голосов
/ 20 августа 2009

Вы не можете привести указатель к члену к void * или к любому другому «обычному» типу указателя. Указатели на членов - это не адреса, как обычные указатели. Скорее всего, вам нужно будет обернуть функцию-член в обычную функцию. C ++ FAQ Lite объясняет это в некоторых деталях. Основная проблема заключается в том, что данные, необходимые для реализации указателя на член, - это не просто адрес, а фактически сильно отличается в зависимости от реализации компилятора.

Я предполагаю, что вы контролируете, что возвращают пользовательские данные lua_touserdata. Он не может быть указателем на члена, поскольку нет законного способа вернуть эту информацию обратно. Но у вас есть другие варианты:

  • Самый простой выбор - это, вероятно, заключить вашу функцию-член в свободную функцию и вернуть ее. Эта свободная функция должна принимать объект в качестве первого аргумента. См. Пример кода ниже.

  • Используйте технику, аналогичную mem_fun Boost.Bind для возврата функционального объекта, который можно соответствующим образом шаблонировать. Я не вижу, что это проще, но это позволит вам связать большее состояние с функцией return, если вам нужно.

Вот переписать вашу функцию, используя первый способ:

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;
}
8 голосов
/ 21 августа 2011

Можно преобразовать указатель на функции-члены и атрибуты, используя объединения:

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

Чтобы преобразовать, поместите исходное значение в один элемент и извлеките целевое значение из другого.

Хотя этот метод практичен, я понятия не имею, будет ли он работать в каждом случае.

1 голос
/ 28 мая 2016

Здесь просто измените параметры функции 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);
1 голос
/ 06 января 2016

В отличие от адреса нестатической функции-члена , которая является типом указатель на элемент со сложным представлением, адрес статической функции-члена обычно является просто адрес машины, совместимый с преобразованием в void *.

Если вам нужно связать нестатическую функцию-член C ++ с C или C-подобным механизмом обратного вызова, основанным на void *, вы можете вместо этого написать статическую оболочку.

Оболочка может принимать указатель на экземпляр в качестве аргумента и передавать управление нестатической функции-члену:

void myclass::static_fun(myclass *instance, int arg)
{
   instance->nonstatic_fun(arg);
}
1 голос
/ 20 августа 2009

В качестве обходного пути, учитывая ограничения приведения функции указателя на член к 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.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...