передача функтора в качестве указателя функции
Я пытаюсь использовать библиотеку C в приложении C++ и нашел себя в следующей ситуации (я знаю свой C, но я довольно новичок в C++). На стороне C у меня есть коллекция функций, которая принимает указатель на функцию в качестве аргумента. На стороне C++ у меня есть объекты с функтором, который имеет ту же сигнатуру, что и указатель функции, необходимый функции C. Есть ли способ использовать функтор C++ в качестве указателя функции для передачи функции C?
10 ответов
вы не можете напрямую передать указатель на объект функтора C++ в качестве указателя функции на код C (или даже в код C++).
кроме того, чтобы переносимо передать обратный вызов в код C, он должен быть по крайней мере объявлен
как extern "C"
функция non-члена.
По крайней мере, потому что некоторые API требуют определенных соглашений о вызове функций и, таким образом,
дополнительные модификаторы объявления.
во многих средах C и C++ имеют одинаковые соглашения о вызовах и отличаются только
в названии mangling, так любой глобальная функция или статический член будет работать.
Но вам все равно нужно обернуть вызов operator()
в нормальной функции.
-
если ваш функтор не имеет состояния (это объект только для удовлетворения некоторых формальных требования и т. д.):
class MyFunctor { // no state public: MyFunctor(); int operator()(SomeType ¶m) const; }
вы можете написать нормальную внешнюю функцию "C", которая создает функтор и выполняет его оператор.)(
extern "C" int MyFunctorInC(SomeType *param) { static MyFunctor my_functor; return my_functor(*param); }
-
если функтор имеет состояние, например:
class MyFunctor { // Some fields here; public: MyFunctor(/* some parameters to set state */); int operator()(SomeType ¶m) const; // + some methods to retrieve result. }
и в C функция обратного вызова принимает какой-то параметр состояния пользователя (обычно void *):
void MyAlgorithmInC(SomeType *arr, int (*fun)(SomeType *, void *), void *user_state);
вы можете написать нормальную функцию extern "C", которая приводит свой параметр состояния к ваш объект функтора:
extern "C" int MyFunctorInC(SomeType *param, void *user_state) { MyFunctor *my_functor = (MyFunctor *)user_state; return (*my_functor)(*param); }
и используйте его так:
MyFunctor my_functor(/* setup parameters */); MyAlgorithmInC(input_data, MyFunctorInC, &my_functor);
в противном случае единственный нормальный способ сделать это (нормально, как в "без генерации машинного кода во время выполнения" и т. д.) использовать некоторое статическое (глобальное) или потоковое локальное хранилище для передачи функтора к extern " C" функция. Это ограничивает то, что вы можете сделать с вашим кодом и уродливо, но будет работать.
Нет, конечно. Сигнатура вашей функции C принимает аргумент как функцию.
void f(void (*func)())
{
func(); // Only void f1(), void F2(), ....
}
все трюки с функторами используются шаблон функции:
template<class Func>
void f (Func func)
{
func(); // Any functor
}
функция обратного вызова C, написанная на C++ , должна быть объявлена как extern "C"
функция - поэтому использование функтора напрямую отсутствует. Вам нужно будет написать какую-то функцию-оболочку для использования в качестве обратного вызова и вызвать эту оболочку функтором. Конечно, протокол обратного вызова должен иметь какой-то способ передачи контекста функции, чтобы он мог добраться до функтора, или задача становится довольно сложной. Большинство схем обратного вызова имеют способ передать контекст, но я работал с некоторыми мертвыми мозгами это не так.
см. этот ответ для получения дополнительной информации (и посмотрите в комментариях для анекдотических доказательств того, что обратный вызов должен быть extern "C"
и не только статическая функция-член):
Я не думаю, что вы можете: operator()
в функции объект действительно является функцией-членом, и C ничего не знает об этом.
то, что вы должны иметь возможность использовать, - это свободные функции C++ или статические функции классов.
GCC позволяет конвертировать указатели функций-членов в простые указатели функций (первый аргумент функции, вызываемой указателем простой функции, тогда this
).
Проверьте соответствующая ссылка в руководстве.
для этого требуется -Wno-pmf-conversions
флаг, чтобы заставить замолчать соответствующее предупреждение для явно нестандартной функции. Очень удобно для сопряжения библиотек стилей C с программированием в стиле c++. Когда функция-член указатель является константой, это даже не нужно генерировать какой-либо код вообще: API будет использовать этот порядок аргументов в любом случае.
если у вас уже есть функтор, сплющивание функтора таким образом, скорее всего, будет означать сплющивание его operator()
, давая вам функцию, которая должна быть вызвана с указателем класса функтора в качестве первого аргумента. Не обязательно поможет, но по крайней мере имеет с рычага.
но, по крайней мере, когда вы не идете через функторы это полезно и обеспечивает надежную замену c-связи для std::mem_fn
С <functional>
.
Это зависит от того, является ли это статическим или методом экземпляра, если он статичен, то вы можете пройти через функцию как className:: functionName, если это метод экземпляра, это намного сложнее, потому что вам, очевидно, нужно привязать к определенному экземпляру, но не можете сделать это так же, как с делегатами в C# и т. д.
лучший способ, который я нашел для этого, - создать класс хранения, который создается с экземпляром объекта, а также функции указатель, класс holding может затем вызвать функцию напрямую.
Я бы сказал нет, потому что функтор C++ имеет перегруженный оператор ()
что это функции-члена, и, таким образом, потребуется указатель функции-члена. Это совершенно другой тип данных, чем обычный указатель функции C, так как он не может быть вызван без экземпляра класса. Вам нужно пройти обычную функцию или статическую функцию-член в C-библиотеку. Поскольку перегруженная ()
оператор не может быть статическим, вы не можете этого сделать. Вам нужно будет пройти C-library нормальная, не являющаяся членом функция или статическая функция-член, из которой затем можно вызвать функтор c++.
Hm, возможно, вы могли бы написать бесплатную функцию шаблона, которая обертывается вокруг ваших объектов-функций. Если они все имеют одинаковую подпись, это должно сработать. Вот так (не проверено):
template<class T>
int function_wrapper(int a, int b) {
T function_object_instance;
return funcion_object_instance( a, b );
}
Это будет сделано для всех функций, которые принимают два входа и возвращают int.
многие API C, которые принимают обратные вызовы указателя функции, имеют параметр void* для состояния пользователя. Если у вас есть один из них, вам повезло - вы можете использовать функцию exterm C, которая рассматривает пользовательские данные как своего рода ссылку или ключ для поиска функтора, а затем выполнить его.
в противном случае, нет.