Обратные вызовы функций C API в код функции-члена C ++ - PullRequest
10 голосов
/ 10 марта 2010

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

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

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

FMOD_Channel_SetCallback( channel, callbackFunc ) ;

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

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

Теперь мне нужно разобрать мой код, чтобы сделать некоторые члены статичными, просто чтобы объяснить C-ness FMOD.возможно в FMOD или если есть обходной путь для связывания обратного вызова с функцией-членом экземпляра определенного объекта C ++ (не статической функцией).Было бы намного плавнее.

Ответы [ 5 ]

12 голосов
/ 10 марта 2010

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

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

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

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

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

5 голосов
/ 11 марта 2010

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

2 голосов
/ 11 марта 2010

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

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

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

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

1 голос
/ 10 марта 2010

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

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

0 голосов
/ 10 марта 2010

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

Object *x;
void callback_trampoline() { x->foobar(); }
...
FMOD_Channel_SetCallback(CHANNEL, callback_trampoline);
...