C обратные вызовы API-функции в код функции членства C++

Так, я использую API FMOD, и это действительно - API C.

Не то, чтобы это плохо или что-либо. Его просто это не взаимодействует через интерфейс хорошо с кодом C++.

Например, использование

FMOD_Channel_SetCallback( channel, callbackFunc ) ;

Это хочет функцию C-стиля для callbackFunc, но я хочу передать его функция членства класса.

Я закончил тем, что использовал прием Win32 для этого, делая функцию членства статичной. Это затем работает обратным вызовом в FMOD.

Теперь я должен взломать независимо свой код, чтобы сделать некоторых участников статичными, только составлять C-мыс FMOD.

Интересно, если ее возможное в FMOD или если существует работа вокруг для соединения обратного вызова к функции члена экземпляра определенного объекта C++ (не статическая функция). Это было бы намного более гладко.

10
задан bobobobo 10 March 2010 в 20:30
поделиться

5 ответов

Вы не можете напрямую передать функцию-член. Функция-член имеет неявный параметр this , а функции C - нет.

Вам нужно будет создать батут (не уверен в сигнатуре обратного вызова, поэтому просто сделайте что-нибудь случайное).

extern "C" int fmod_callback( ... args ...)
{
    return object->member();
}

Одна из проблем заключается в том, откуда берется указатель на объект. Надеюсь, fmod дает вам общее значение контекста, которое будет предоставлено вам при выполнении вашего обратного вызова (затем вы можете передать указатель объекта).

Если нет, вам просто нужно сделать его глобальным, чтобы получить к нему доступ.

12
ответ дан 3 December 2019 в 19:32
поделиться

Существует непереносимое и довольно хакерское решение, которое имеет то преимущество, что, по крайней мере, является поточно-ориентированным, чего нельзя сказать о методах «батут».

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

Это беспорядочно, и вам придется предоставить реализацию для любой новой платформы (в любое время, когда изменяется соглашение о вызовах), но это работает, является потокобезопасным. (Конечно, вам также придется остерегаться DEP). Другое поточно-ориентированное решение - прибегнуть к локальному хранилищу потока (при условии, что вы знаете, что обратный вызов будет происходить в том же потоке, что и сделанный вами вызов).

См. http://www.codeproject.com/KB/cpp/GenericThunks.aspx для примера того, как вы могли бы генерировать преобразователи.

2
ответ дан 3 December 2019 в 19:32
поделиться

По моему скромному мнению, использование только указателя на функцию (без дополнительного отдельного указателя на объект) для обратного вызова C является неправильной конструкцией.

Если вместо этого функция была FMOD_Channel_SetCallback (channel, callbackFunc, callbackObj) , то ваш статический метод просто принимает экземпляр объекта, а затем вызывает callbackObj-> func () (который, очевидно, может быть нестатическим).

1
ответ дан 3 December 2019 в 19:32
поделиться

Думаю, это должно было работать так:
Вы можете назначить некоторые пользовательские данные для канала, вызвав FMOD_Channel_SetUserData . Эти пользовательские данные должны быть указателем на ваш объект C ++, который обрабатывает события. Затем вы должны написать обратный вызов в стиле C, который извлекает этот объект, вызывая FMOD_Channel_GetUserData , а затем вызывает метод вашего экземпляра C ++ для этого объект.

5
ответ дан 3 December 2019 в 19:32
поделиться

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

Object *x;
void callback_trampoline() { x->foobar(); }
...
FMOD_Channel_SetCallback(CHANNEL, callback_trampoline);
0
ответ дан 3 December 2019 в 19:32
поделиться