Подтвердить что ты не робот

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

Я пытаюсь использовать библиотеку C в приложении С++ и нашел себя в следующей ситуации (я знаю свой C, но я довольно новичок в С++). На стороне C у меня есть набор функций, который принимает указатель на функцию в качестве аргумента. На стороне С++ у меня есть объекты с функтором, который имеет ту же подпись, что и указатель функции, необходимый функции C. Есть ли способ использовать функтор С++ в качестве указателя функции для перехода к функции C?

4b9b3361

Ответ 1

Вы не можете напрямую передать указатель на объект-объект С++ в качестве указателя функции на код C (или даже код С++).

Дополнительно, чтобы переносить обратный вызов на код C, он должен быть объявлен как минимум как функция extern "C" non-member. По крайней мере, поскольку некоторые API требуют специальных вызовов вызова функций и, следовательно, дополнительные модификаторы декларации.

Во многих средах 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);
    

    вы можете написать нормальную внешнюю функцию "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);
    
  • В противном случае единственный нормальный способ сделать это (нормальный как в "без генерации машинного кода во время выполнения" и т.д.) заключается в использовании некоторого статического (глобального) или локального хранилища потоков для передачи функтора к внешней функции "С". Это ограничивает то, что вы можете сделать с вашим кодом и является уродливым, но будет работать.

Ответ 2

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

Ответ 3

Функция обратного вызова C, написанная на С++, должна быть объявлена ​​как функция extern "C", поэтому непосредственное использование функтора отсутствует. Вам нужно будет написать какую-то функцию обертки для использования в качестве этого обратного вызова, и эта оболочка вызовет функтор. Конечно, протокол обратного вызова должен иметь некоторый способ передачи контекста функции, чтобы он мог добраться до функтора, или задача становится довольно сложной. Большинство схем обратного вызова имеют способ передать контекст, но я работал с некоторыми мертвыми мозгами, которые этого не делают.

См. этот ответ для получения более подробной информации (и посмотрите в комментариях для анекдотических доказательств того, что обратный вызов должен быть extern "C", а не только статическая функция-член):

Ответ 4

Я не думаю, что вы можете: operator() в объекте функции действительно является функцией-членом, а C ничего не знает об этом.

То, что вы должны использовать, - это бесплатные функции С++ или статические функции классов.

Ответ 5

Нет, конечно. Подпись вашей функции C принимает аргумент как функцию.

void f(void (*func)())
{
  func(); // Only void f1(), void F2(), ....
}

Все трюки с функторами используются шаблонами:

template<class Func>
void f (Func func)
{
    func(); // Any functor
}

Ответ 6

GCC позволяет конвертировать указатели на функции функций в простые указатели функций (первый аргумент функции, называемый указателем простой функции, тогда this).

Проверьте соответствующую ссылку в руководстве.

Для этого требуется флаг -Wno-pmf-conversions, чтобы отключить соответствующее предупреждение для явно нестандартной функции. Очень удобно для взаимодействия библиотек стилей C с программированием на языке С++. Когда указатель функции-члена является константой, для этого вообще не требуется генерировать какой-либо код: API будет использовать этот аргумент в любом случае.

Если у вас уже есть функтор, сглаживание функтора таким образом, вероятно, будет означать сглаживание его operator(), что даст вам функцию, которую нужно вызвать с помощью указателя класса функтора в качестве первого аргумента. Это не обязательно помогает в этом, но, по крайней мере, имеет C-связь.

Но, по крайней мере, когда вы не проходите через функторы, это полезно и обеспечивает замену C-ссылки на несущую способность для std::mem_fn от <functional>.

Ответ 7

Это зависит, если это статический или экземплярный метод, если он статичен, то вы можете передать функцию как className:: functionName, если это метод экземпляра, это будет более сложным, потому что вам, очевидно, нужно связать определенный экземпляр, но не может сделать это так же, как с делегатами на С# и т.д.

Лучший способ, который я нашел для этого, - создать класс холдинга, который создается экземпляром объекта, а также указателем функции, класс-удержание может затем вызвать функцию напрямую.

Ответ 8

Я бы сказал "нет", потому что у функтора С++ есть перегруженный оператор (), который является функцией-членом, и поэтому требует указателя функции-члена. Это совершенно другой тип данных, чем обычный указатель функции C, поскольку он не может быть вызван без экземпляра класса. Вам нужно будет передать нормальную функцию или статическую функцию-член в библиотеку C. Поскольку перегруженный оператор () не может быть статичным, вы не можете этого сделать. Вам нужно будет передать C-библиотеке нормальную, не-членную функцию или статическую функцию-член, из которой вы затем можете вызвать функтор С++.

Ответ 9

Hm, возможно, вы могли бы написать бесплатную функцию шаблона, которая обтекает ваши объекты-функции. Если все они имеют одну и ту же подпись, это должно работать. Как это (не тестировалось):

template<class T>
int function_wrapper(int a, int b) {
    T function_object_instance;

    return funcion_object_instance( a, b );
}

Это будет делать для всех функций, которые принимают два int и возвращают int.

Ответ 10

Многие API-интерфейсы C, которые выполняют обратные вызовы функций, имеют параметр void * для пользовательского состояния. Если у вас есть один из них, вам повезло - вы можете использовать функцию exterm C, которая обрабатывает данные пользователя как своего рода ссылку или ключ для поиска функтора, а затем выполняет его.

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