Question

La question est la suivante :considérez ce morceau de code :

#include <iostream>


class aClass
{
public:
    void aTest(int a, int b)
    {
        printf("%d + %d = %d", a, b, a + b);
    }
};

void function1(void (*function)(int, int))
{
    function(1, 1);
}

void test(int a,int b)
{
    printf("%d - %d = %d", a , b , a - b);
}

int main (int argc, const char* argv[])
{
    aClass a();

    function1(&test);
    function1(&aClass::aTest); // <-- How should I point to a's aClass::test function?

    return 0;
}

Comment puis-je utiliser le ac'est aClass::test comme argument pour function1?Je suis coincé en faisant ça.

Je souhaite accéder à un membre de la classe.

Était-ce utile?

La solution

Il n'y a rien de mal à utiliser des pointeurs de fonction.Cependant, les pointeurs vers des fonctions membres non statiques ne sont pas comme les pointeurs de fonctions normaux :les fonctions membres doivent être appelées sur un objet qui est passé comme argument implicite à la fonction.La signature de votre fonction membre ci-dessus est donc

void (aClass::*)(int, int)

plutôt que le type que vous essayez d'utiliser

void (*)(int, int)

Une approche pourrait consister à faire fonctionner le membre static auquel cas il ne nécessite l'appel d'aucun objet et vous pouvez l'utiliser avec le type void (*)(int, int).

Si vous avez besoin d'accéder à un membre non statique de votre classe et vous devez vous en tenir aux pointeurs de fonction, par exemple, parce que la fonction fait partie d'une interface C, votre meilleure option est de toujours passer un void* à votre fonction en prenant des pointeurs de fonction et appelez votre membre via une fonction de transfert qui obtient un objet du void* puis appelle la fonction membre.

Dans une interface C++ appropriée, vous souhaiterez peut-être que votre fonction prenne un argument basé sur un modèle pour que les objets de fonction utilisent des types de classe arbitraires.Si l'utilisation d'une interface basée sur un modèle n'est pas souhaitable, vous devez utiliser quelque chose comme std::function<void(int, int)>:vous pouvez créer un objet fonction convenablement appelable pour ceux-ci, par exemple en utilisant std::bind().

Les approches de type sécurisé utilisent un argument de modèle pour le type de classe ou un std::function<...> sont préférables à l'utilisation d'un void* interface car ils suppriment le potentiel d’erreurs dues à une conversion vers le mauvais type.

Pour clarifier comment utiliser un pointeur de fonction pour appeler une fonction membre, voici un exemple :

// the function using the function pointers:
void somefunction(void (*fptr)(void*, int, int), void* context) {
    fptr(context, 17, 42);
}

void non_member(void*, int i0, int i1) {
    std::cout << "I don't need any context! i0=" << i0 << " i1=" << i1 << "\n";
}

struct foo {
    void member(int i0, int i1) {
        std::cout << "member function: this=" << this << " i0=" << i0 << " i1=" << i1 << "\n";
    }
};

void forwarder(void* context, int i0, int i1) {
    static_cast<foo*>(context)->member(i0, i1);
}

int main() {
    somefunction(&non_member, 0);
    foo object;
    somefunction(&forwarder, &object);
}

Autres conseils

La réponse de @Pete Becker est bonne mais vous pouvez aussi le faire sans passer le class instance comme paramètre explicite pour function1 en C++11 :

#include <functional>
using namespace std::placeholders;

void function1(std::function<void(int, int)> fun)
{
    fun(1, 1);
}

int main (int argc, const char * argv[])
{
   ...

   aClass a;
   auto fp = std::bind(&aClass::test, a, _1, _2);
   function1(fp);

   return 0;
}

Un pointeur vers une fonction membre est différent d’un pointeur vers une fonction.Pour utiliser une fonction membre via un pointeur, vous avez besoin d'un pointeur vers celle-ci (évidemment) et d'un objet auquel l'appliquer.Donc la version appropriée de function1 serait

void function1(void (aClass::*function)(int, int), aClass& a) {
    (a.*function)(1, 1);
}

et pour l'appeler :

aClass a; // note: no parentheses; with parentheses it's a function declaration
function1(&aClass::test, a);

Depuis 2011, si vous pouvez changer function1, faites-le, comme ceci :

#include <functional>
#include <cstdio>

using namespace std;

class aClass
{
public:
    void aTest(int a, int b)
    {
        printf("%d + %d = %d", a, b, a + b);
    }
};

template <typename Callable>
void function1(Callable f)
{
    f(1, 1);
}

void test(int a,int b)
{
    printf("%d - %d = %d", a , b , a - b);
}

int main()
{
    aClass obj;

    // Free function
    function1(&test);

    // Bound member function
    using namespace std::placeholders;
    function1(std::bind(&aClass::aTest, obj, _1, _2));

    // Lambda
    function1([&](int a, int b) {
        obj.aTest(a, b);
    });
}

(démo en direct)

Notez également que j'ai corrigé la définition de votre objet cassé (aClass a(); déclare une fonction).

J'ai posé une question similaire (Openframeworks C++ passant le vide des autres classes) mais la réponse que j'ai trouvée était plus claire donc voici l'explication pour les futurs enregistrements :

il est plus facile d'utiliser std::function comme dans :

 void draw(int grid, std::function<void()> element)

puis appelez comme :

 grid.draw(12, std::bind(&BarrettaClass::draw, a, std::placeholders::_1));

ou encore plus simple :

  grid.draw(12, [&]{a.draw()});

où vous créez un lambda qui appelle l'objet le capturant par référence

J'ai fait fonctionner le membre comme statique et tout fonctionne :

#include <iostream>

class aClass
{
public:
    static void aTest(int a, int b)
    {
        printf("%d + %d = %d\n", a, b, a + b);
    }
};

void function1(int a,int b,void function(int, int))
{
    function(a, b);
}

void test(int a,int b)
{
    printf("%d - %d = %d\n", a , b , a - b);
}

int main (int argc, const char* argv[])
{
    aClass a;

    function1(10,12,test);
    function1(10,12,a.aTest); // <-- How should I point to a's aClass::test function?

    getchar();return 0;
}

Vous pouvez arrêter de vous cogner la tête maintenant.Voici le wrapper que la fonction membre doit prendre en charge existant fonctions prenant en clair C fonctionne comme des arguments. thread_local La directive est la clé ici.

http://cpp.sh/9jhk3

// Example program
#include <iostream>
#include <string>

using namespace std;

typedef int FooCooker_ (int);

// Existing function
extern "C" void cook_10_foo (FooCooker_ FooCooker) {
    cout << "Cooking 10 Foo ..." << endl;
    cout << "FooCooker:" << endl;
    FooCooker (10);
}

struct Bar_ {
    Bar_ (int Foo = 0) : Foo (Foo) {};
    int cook (int Foo) {
        cout << "This Bar got " << this->Foo << endl;
        if (this->Foo >= Foo) {
            this->Foo -= Foo;
            cout << Foo << " cooked" << endl;
            return Foo;
        } else {
            cout << "Can't cook " <<  Foo << endl;
            return 0;
        }
    }
    int Foo = 0;
};

// Each Bar_ object and a member function need to define
// their own wrapper with a global thread_local object ptr
// to be called as a plain C function.
thread_local static Bar_* BarPtr = NULL;
static int cook_in_Bar (int Foo) {
    return BarPtr->cook (Foo);
}

thread_local static Bar_* Bar2Ptr = NULL;
static int cook_in_Bar2 (int Foo) {
    return Bar2Ptr->cook (Foo);
}

int main () {
  BarPtr = new Bar_ (20);
  cook_10_foo (cook_in_Bar);

  Bar2Ptr = new Bar_ (40);
  cook_10_foo (cook_in_Bar2);

  delete BarPtr;
  delete Bar2Ptr;
  return 0;
}

Veuillez commenter tout problème lié à cette approche.

Les autres réponses ne parviennent pas à appeler existant plaine C les fonctions: http://cpp.sh/8exun

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top