передача функтора в качестве указателя функции

Я пытаюсь использовать библиотеку 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 &param) 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 &param) 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" функция. Это ограничивает то, что вы можете сделать с вашим кодом и уродливо, но будет работать.


Я нашел этот "gem " использование google. По-видимому, возможно, но я бы не рекомендовал это. Прямая ссылке пример исходного кода.


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

в противном случае, нет.