Как использовать функцию члена С++ в качестве функции обратного вызова для 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", но я бы не испытал свою удачу.