Если вы используете MSVC и целевой x86-32 (не x86-64), вы можете использовать соглашение __stdcall для функции-члена.(__cdecl тоже работает)
С __stdcall этот будет передан в качестве первого параметра, поэтому вы можете написать
typedef void(__stdcall *callback_t)(void* arg);
void set_callback(callback_t callback, void* callbackParameter);
struct Foo
{
int x;
void __stdcall someCallback() { this->x = 1; }
};
, но
Foo foo;
set_callback((callback_t)&Foo::someCallback, this);
Не будет работать: вы не можете напрямую привести указатель на функцию-член к стандартному указателю.Чтобы это сработало, вы должны использовать обходной путь:
Сначала убедитесь, что & Foo :: someCallback имеет тот же размер, что и void *
static_assert(sizeof(&Foo::someCallback) == sizeof(void*), "");
// or assert()
Это утверждение может завершиться ошибкой, если их может быть нескольконаследование, а SomeCallback является виртуальным.Чтобы запретить множественное наследование, используйте ключевое слово __single_inheritance:
struct __single_inheritance Foo { /* ....*/ };
Во-вторых, вы должны привести & Foo :: someCallback к callback_t, используя
union
{
void(__stdcall Foo::*src)();
callback_t dst;
} u = {&Foo::someCallback};
set_callback(u.dst, this);
или
void(__stdcall Foo::*temp)() = &Foo::someCallback;
set_callback(*(callback_t*)&temp, this);
Это работает, только если вы можете передать this в функцию обратного вызова в качестве первого параметра.
Если вы не можете, вы можете попытаться динамически генерировать заглушки обратного вызова в сборке :) Выделите исполняемую память и запишите туда
B9 XX XX XX XX mov ecx, <this>
68 XX XX XX XX push <function member address>
C3 ret
Это заглушка обратного вызова, которая преобразует __stdcallдо __thiscall.