Как использовать функцию члена С++ в качестве функции обратного вызова для C-структуры
Существует библиотека C (которую я не могу изменить), которая поддерживает функцию обратного вызова типа
void (*callback)(void *appContext, int eventid)
Я хочу установить функцию С++ в качестве обратного вызова.
В частности, у меня есть следующие вопросы:
-
Нужно ли объявлять функцию обратного вызова в блоке "extern C"
?
-
Должна ли функция-член быть статической, чтобы быть функцией обратного вызова? Можно ли использовать нестационарную функцию-член? Если да, то как? И когда рекомендуется использовать нестационарную функцию-член?
-
Имеет ли значение, является ли функция функцией шаблона или нет?
-
Имеет ли какая-либо функция класса non-class C функции класса?
Я пытаюсь использовать эти варианты на старом компиляторе VС++, который не поддерживает последний стандарт С++. Но код должен быть независимым от платформы и должен работать на большинстве компиляторов С++. Я хочу знать, что рекомендуется применять с обратными вызовами?
Ответы
Ответ 1
Должна ли быть указана функция обратного вызова под внешним "C" ?
НЕТ. extern "C" необходим только тогда, когда вы вызываете функцию С++ напрямую, без использования указателей функций, из C. Если используются указатели на функции, extern "C" не требуется.
Могу ли я использовать нестатические функции-члены в качестве обратного вызова?
НЕТ. Нестатические функции-члены класса A имеют неявный первый параметр, соответствующий этому указателю.
Могу ли я использовать статические функции-члены в качестве обратного вызова?
ДА, если подпись совпадает с сигналом обратного вызова.
Имеет ли значение, является ли функция функцией шаблона или нет?
НЕТ, функция шаблона может использоваться как обратные вызовы, пока подпись экземпляра-шаблона совпадает с обратным вызовом.
Ответ 2
Это должно работать, если ваша функция-член статична.
Ответ 3
Предполагая, что appContext
- непрозрачный указатель, который вы передаете функции, вызывающей обратный вызов, вы можете получить обратный вызов к функции-члену определенного объекта, подобного этому:
class myclass {
void do_something() {
// call function making the callback using _event_handler
// as the callback function and the "this" pointer as appContext
}
// make sure the raw callback uses the correct calling convention (cdecl, stdcall, etc.)
static void _handle_event(void* appContext, int eventid) {
// forward the event to the actual object
static_cast<myclass *>(appContext)->handle_event(eventid);
}
void handle_event(int eventid) {
// do object-specific event handling
}
};
В нескольких ответах упоминается extern "C"
как требование. Это просто неверно.
extern "C"
необходимо, только если вы вызываете функцию C непосредственно из С++. Он использовал, чтобы сказать компилятору С++ "не применять манипулирование именами при создании имени символа для этой функции". Вы передаете указатель на функцию С++ на функцию C. Пока согласующие соглашения совпадают, все будет работать нормально. Имя функции никогда не участвует.
Ответ 4
Это не так просто, как объявление функции обратного вызова в блоке extern "C"
. Вам нужно выяснить, какое соглашение вызова использует библиотека C для своих функций.
Термины, которые я собираюсь использовать, являются специфичными для Microsoft, но те же правила должны применяться и к другим платформам.
По умолчанию компилятор Visual С++ выполняет функции C __stdcall
и С++ __cdecl
. Вы не можете смешивать и сопоставлять эти соглашения, так как каждый из них делает разные предположения о том, кто очищает стек. Ниже приведено более подробное объяснение.
Как только вы согласовали соглашения о вызовах, самый простой способ - объявить свою функцию обратного вызова С++ как функцию постоянной постоянной видимости пространства имен; или если это должна быть функция-член, то статическая функция-член. В обоих случаях вы можете привязать указатель к экземпляру класса с помощью std::bind
. Вы даже можете использовать std::bind
для привязки нестатической функции-члена, но я не могу вспомнить синтаксис с верхней части головы.
Ответ 5
-
Убедитесь, что в глобальной области
-
Используйте extern "C"
-
Используйте __cdecl
, если необходимо: void (_cdecl *callback)
Ответ 6
Строго говоря, вы не можете. Обратный вызов C должен быть обозначен extern "C"
, что невозможно для функций-членов. Могут использоваться только автономные функции. Вы можете переадресовывать вызовы оттуда к любой функции С++, включая статические или нестатические функции-члены.
В зависимости от платформы вы иногда можете уйти с пропуском extern "C"
, но я бы не испытал свою удачу.