Приведение между void * и указателем на функцию-член

Я в настоящее время использую GCC 4.4, и яу меня довольно головная боль междуvoid* и указатель на функцию-член. Я'я пытаюсь написать простую в использовании библиотеку для привязки объектов C ++ к интерпретатору Lua, например:

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

Мы сделали большую часть этого, за исключением следующей функции (которая специфична для определенной сигнатуры функции, пока у меня не будет возможности обобщить ее):

template 
int call_int_function(lua_State *L) 
{
    // this next line is problematic
    void (T::*method)(int, int) = reinterpret_cast(lua_touserdata(L, lua_upvalueindex(1)));
    T *obj = reinterpret_cast(lua_touserdata(L, 1));

    (obj->*method)(lua_tointeger(L, 2), lua_tointeger(L, 3));
    return 0;
}

Для тех из вас, кто не знаком с Луа,lua_touserdata(L, lua_upvalueindex(1)) получает первое значение, связанное с замыканием (в этом случае это 'указатель на функцию-член) и возвращает его какvoid*, GCC жалуется, что ->void* void (T::*)(int, int) неверный актерский состав. Есть идеи как обойти это?

 fbrereto20 авг. 2009 г., 18:24
+1 выше ... в частности раздел 33.7 и 33,8
 Alex21 авг. 2011 г., 19:05
Просто из любопытства, почему вы пытаетесь хранить функции C в пользовательских данных Lua таким образом? там'Вероятно, это более безопасный способ достижения вашей цели.
 Sean Bright20 авг. 2009 г., 18:16
 AbstractDissonance17 июл. 2016 г., 06:54
Вы можете использовать asm для выполнения работы, если знаете платформу. C ++ не делаетT сука об этом тогда. Полезно в некоторых случаях (например, при тестировании кода). Просто сохраните func ptr в var, создайте void *, перейдите в asm и скопируйте значение.

Ответы на вопрос(5)

Здесь просто измените параметры функции void_cast, чтобы она соответствовала вашим потребностям:

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

пример использования:

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

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

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

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

void myclass::static_fun(myclass *instance, int arg)
{
   instance->nonstatic_fun(arg);
}
 Kaz14 июл. 2016 г., 20:01
@andig Интерфейс обратного вызова должен иметь аргумент контекста для передачи зарегистрированных пользовательских данных. То есть "пожалуйста, передайте мне мой указатель, когда вы перезвоните мне ", Если интерфейс обратного вызова неЭто может быть взломано батутами. Вы можете поместить небольшой фрагмент машинного кода (батут) в передней части объекта обратного вызова, и этот машинный код используется в качестве функции обратного вызова. Он вычисляет указатель объекта относительно его указателя инструкции, зная, что объект находится на фиксированном смещении от своего собственного адреса. Затем он вызывает реальную функцию.
 andig14 июл. 2016 г., 19:04
мы видели этот шаблон в разных библиотеках, но как заполнить указатель экземпляра в функции обратного вызова? Магия компилятора?

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

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

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

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

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

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;
}
</t></void></class>
 quark20 авг. 2009 г., 18:44
Мартин Я недавно получил образование по этому вопросу на SO. Позвольте мне указать вам на обсуждение здесь:stackoverflow.com/questions/1207106/....
 Martin York20 авг. 2009 г., 18:42
Я неНе думаю, что указатель на функции-члены отличается от обычных указателей. Что заставляет вас думать, что они обладают особыми свойствами? Они просто указывают на кусок кода.
 quark20 авг. 2009 г., 18:54
Я тоже в этом оригинальном вопросе :). Это'Явно тонкий предмет.
 Martin York20 авг. 2009 г., 18:49
Спок слишком быстро. Re-жаргон.
 quark20 авг. 2009 г., 18:45
Мартин: Также прочитайте ссылку с пометкойсильно изменился ":codeproject.com/KB/cpp/FastDelegate.aspx, Указатели на функции-члены не обязательно реализованы как указатели вообще, или даже одинаково от системы к системе. Они могут быть комбинациями таблицы и индекса, заполненными единицами или любым из множества вариантов реализации.

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

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

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

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

 John McFarlane19 мая 2018 г., 13:26
Возможно, но я бы следовал за @quark 'Совет получить что-то действительно портативное.
 Matthew Hooker16 мая 2018 г., 23:42
@JohnMcFarlane Вы думаете, что если бы добавление #pragma pointers_to_members (full_generality, multiple_inheritance) сработало бы, если classT использует множественное наследование?msdn.microsoft.com/en-us/library/83cch5a6.aspx
 John McFarlane15 мар. 2013 г., 07:55
Мы попробовали этот подход с ноябрьской ОСАГО MSVC. Я добавилstatic_assert чтобы убедиться, чтоsizeof(u_ptm_cast::pmember) == sizeof(u_ptm_cast::pvoid), Этопоявляется работать в большинстве ситуаций, например когда pmember не виртуален, виртуален или когдаclassT заменяется другим классом при преобразовании обратно в указатель на функцию-член. Однако еслиclassT использует множественное наследование, указатель действительно больше и утверждение не выполняется.
 John McFarlane20 мар. 2013 г., 13:32
В GCC 4.7 все указатели на функции-члены имеют размер двух обычных указателей. Учет этого, сделавpvoid массив двойного размера, @paniq 'Техника с участиемunion работает в тех же случаях, что и с компилятором MS. В целом, хотя этоЭто довольно хрупкое решение, и его мало используют в проблеме привязки Lua.
 Charles L Wilcox15 февр. 2013 г., 19:32
Я полагаю, что указатели членов на самом деле являются объектами большого размера; Это'очень вероятноsizeof(void*) < sizeof(memberT classT::*); таким образомpvoid не может представлять все .I 'pmember

ателя на член кvoid* Вы можете обернуть указатель функции в небольшую структуру, выделенную кучей, и поместить указатель на эту структуру в ваших пользовательских данных Lua: I '

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 LuaUserD,ata<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;
}
</t></luauserdatatype*></t></class></foo></foo></foo></typename>

я не разбираюсь в Lua, так что я, вероятно, что-то упустил из примера Имейте в виду, тоже, если вы идете по этому пути, вы 'придется управлять LuaUserData 'распределение.

Ваш ответ на вопрос