Скажем, что это C-функция, которую нужно обернуть:
void foo(int(__stdcall *callback)());
Две основные ошибки с обратными вызовами функции функции C:
- Невозможно сохранить выражения привязки
- Невозможно сохранить захват lambdas
Я хотел бы знать, как наилучшим образом обернуть такие функции, как это сделать. Первое особенно полезно для обратного вызова функции-члена, а второе для встроенного определения, которое использует окружающие переменные, но это не единственное использование.
Другим свойством этих конкретных указателей на функции является необходимость использования соглашения о вызове __stdcall
. Это, насколько мне известно, полностью исключает лямбда как вариант, и в любом случае это неприятно. Я хотел бы разрешить хотя бы __cdecl
.
Это лучшее, что я могу придумать без того, чтобы начать сгибаться, опираясь на поддержку, которую не имеют указатели функций. Обычно это будет в заголовке. Ниже приведен пример на Coliru.
#include <functional>
//C function in another header I have no control over
extern "C" void foo(int(__stdcall *callback)()) {
callback();
}
namespace detail {
std::function<int()> callback; //pretend extern and defined in cpp
//compatible with the API, but passes work to above variable
extern "C" int __stdcall proxyCallback() { //pretend defined in cpp
//possible additional processing
return callback();
}
}
template<typename F> //takes anything
void wrappedFoo(F f) {
detail::callback = f;
foo(detail::proxyCallback); //call C function with proxy
}
int main() {
wrappedFoo([&]() -> int {
return 5;
});
}
Есть, однако, главный недостаток. Это не повторный вход. Если переменная переназначена до ее использования, старая функция никогда не будет вызываться (не принимая во внимание проблемы многопоточности).
Одна вещь, которую я попробовал, которая в итоге удвоила себя, заключалась в хранении std::function
в качестве элемента данных и использовании объектов, поэтому каждый из них будет работать с другой переменной, но не было способа передать объект в прокси-сервер, Взятие объекта в качестве параметра приведет к несоответствию сигнатуры и привязке, это не позволит сохранить результат как указатель функции.
Одна идея, которую я имею, но не играющая вокруг, - это вектор std::function
. Тем не менее, я думаю, что единственное реальное безопасное время для его удаления - это очистить его, когда ничего не использует. Однако каждая запись сначала добавляется в wrappedFoo
, а затем используется в proxyCallback
. Мне интересно, если счетчик, который был увеличен в первом и уменьшен в последнем, затем проверил на ноль, прежде чем очистка вектора будет работать, но это звучит как более запутанное решение, чем необходимо в любом случае.
Есть ли способ обернуть функцию C с помощью обратного вызова указателя функции, чтобы завершенная версия С++:
- Позволяет использовать любой объект функции
- Позволяет больше, чем просто вызов вызова callback C (если он критичен, что он то же самое, пользователь может передать что-то с правом вызова)
- Является потокобезопасным/повторно входящим
Примечание. Очевидным решением, заявленным как часть ответа Микаэла Персона, является использование параметра void *
, который должен существовать. Тем не менее, это, к сожалению, не исключение, все-таки вариант, в основном из-за некомпетентности. Какие возможности существуют для тех функций, которые не имеют этой опции, это то, где это может стать интересным, и является основным путем к очень полезному ответу.