Question

Je suis en train d'utiliser une bibliothèque C dans un C ++ app et ai trouvé mon moi dans la situation suivante (je sais que mon C, mais je suis assez nouveau pour C ++). Du côté C j'ai une collection de fonctions qui prend un pointeur de fonction comme argument. Du côté C j'ai des objets avec un foncteur qui a la même signature que le pointeur de la fonction requise par la fonction C. Est-il possible d'utiliser le foncteur C en tant que pointeur de fonction pour passer à la fonction C?

Était-ce utile?

La solution

Vous ne pouvez pas passer directement un pointeur vers un objet foncteur C ++ comme pointeur de fonction au code C (Ou même à un code C ++).

En outre, pour passer un rappel à portably code C, il doit être au moins déclaré en tant que fonction non extern "C" membre. Au moins, parce que certaines API nécessitent des conventions d'appel de fonction spécifiques et donc modificateurs de déclaration supplémentaire.

Dans de nombreux environnements C et C ++ ont les mêmes conventions d'appel et ne diffèrent que au nom de mutiler, de sorte que toute fonction globale ou un membre statique fonctionnera. Mais vous avez encore besoin d'envelopper l'appel à operator() dans une fonction normale.

  • Si votre foncteur n'a pas d'état (il est un objet juste pour satisfaire un peu formel exigences, etc.):

    class MyFunctor {
      // no state
     public:
      MyFunctor();
      int operator()(SomeType &param) const;
    }
    

    vous pouvez écrire une fonction normale extern « C » qui crée foncteur et exécute son opérateur ().

    extern "C" int MyFunctorInC(SomeType *param)
    {
      static MyFunctor my_functor;
      return my_functor(*param);
    }
    
  • Si votre foncteur a l'état, par exemple:

    class MyFunctor {
      // Some fields here;
     public:
      MyFunctor(/* some parameters to set state */);
      int operator()(SomeType &param) const;
      // + some methods to retrieve result.
    }
    

    et la fonction de rappel de C prend un certain type de paramètre d'état de l'utilisateur (habituellement void *):

    void MyAlgorithmInC(SomeType *arr,
                        int (*fun)(SomeType *, void *),
                        void *user_state);
    

    vous pouvez écrire une fonction normale extern « C » qui projette son paramètre d'état à votre objet foncteur:

    extern "C" int MyFunctorInC(SomeType *param, void *user_state)
    {
      MyFunctor *my_functor = (MyFunctor *)user_state;
      return (*my_functor)(*param);
    }
    

    et l'utiliser comme ceci:

    MyFunctor my_functor(/* setup parameters */);
    MyAlgorithmInC(input_data, MyFunctorInC, &my_functor);
    
  • Sinon, la seule façon normale de le faire (Normal comme dans « sans générer de code machine en cours d'exécution », etc.) est d'utiliser une partie statique (global) ou d'un fil de stockage local pour passer le foncteur à une extern fonction « C ». Cela limite ce que vous pouvez faire avec votre code et est laid, mais fonctionne.

Autres conseils

Non, bien sûr. La signature de votre fonction C prendre un argument en fonction.

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

Toutes les astuces avec foncteurs sont utilisés par modèle Fonctions :

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

Une fonction de rappel C écrit en C ++ doit être déclarée comme une fonction extern "C" - donc l'aide d'un foncteur est directement. Vous aurez besoin d'écrire une sorte de fonction d'emballage à utiliser comme ce rappel et ont cette wrapper appeler le foncteur. Bien sûr, le protocole de rappel devra avoir un moyen de passer contexte à la fonction afin qu'il puisse se rendre à l'foncteur, ou la tâche devient assez délicat. La plupart des régimes ont un rappel moyen de passer contexte, mais j'ai travaillé avec les morts du cerveau qui ne sont pas.

Voir cette réponse pour certains plus de détails (et regarder dans les commentaires de preuves anecdotiques que le rappel doit être extern "C" et non pas seulement une fonction membre statique):

Je ne pense pas que vous pouvez: operator() dans un objet de fonction est vraiment une fonction membre, et C ne sait rien sur les

.

Ce que vous devriez être en mesure d'utiliser sont libres fonctions C ++ ou fonctions statiques des classes.

Cela dépend si cela est une méthode statique ou une instance, si elle est statique, alors vous pouvez passer à travers la fonction className :: functionName, si elle est une méthode d'instance, il est juste plus compliqué, parce que vous devez évidemment attacher à une certaine instance, mais ne peut le faire de la même manière que vous le feriez avec des délégués en C #, etc.

La meilleure façon que j'ai trouvé de le faire est de créer une classe de retenue qui est instancié avec l'instance de l'objet ainsi que le pointeur de la fonction, la classe de maintien peut alors invoquer directement la fonction.

Je dirais non, parce qu'un foncteur C ++ a un opérateur surchargé () qui est une fonction membre , et aurait donc besoin d'un pointeur de fonction membre. Ce type de données totalement différent d'un pointeur de fonction normale C, car il ne peut pas être invoquée sans une instance de la classe. Vous auriez besoin de passer une fonction normale ou une fonction membre statique à la bibliothèque C. Etant donné qu'un opérateur () surchargé ne peut pas être statique, vous ne pouvez pas le faire. Vous auriez besoin de passer la bibliothèque C une normale, fonction non-membre ou la fonction de membre statique, à partir duquel vous pouvez alors invoquer le foncteur C ++.

Hm, peut-être vous pourriez écrire une fonction de modèle gratuit qui enroule autour de votre fonction des objets. S'ils ont tous la même signature, cela devrait fonctionner. Comme cela (non testé):

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

    return funcion_object_instance( a, b );
}

Cela faire pour toutes les fonctions qui prennent deux ints et retourner un int.

De nombreuses API C qui prennent callbacks pointeur de fonction ont un paramètre void * pour l'état de l'utilisateur. Si vous avez un de ceux-ci, vous avez de la chance - vous pouvez utiliser une fonction exterm C qui traite les données de l'utilisateur comme une sorte de référence ou clé pour rechercher le foncteur, puis l'exécuter

.

Dans le cas contraire, non.

scroll top