wake-up-neo.com

Wie kann ich eine Member-Funktion übergeben, bei der eine freie Funktion erwartet wird?

Die Frage ist die folgende: Betrachten Sie diesen 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;
}

Wie kann ich den aClass::test des a als Argument für function1 verwenden? Ich bin dabei fest damit beschäftigt.

Ich möchte auf ein Mitglied der Klasse zugreifen.

89
Jorge Leitão

Die Verwendung von Funktionszeigern stimmt nicht. Zeiger auf nicht statische Elementfunktionen sind jedoch nicht wie normale Funktionszeiger: Elementfunktionen müssen für ein Objekt aufgerufen werden, das als implizites Argument an die Funktion übergeben wird. Die Signatur Ihrer Mitgliedsfunktion oben ist also

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

anstelle des Typs, den Sie verwenden möchten

void (*)(int, int)

Ein Ansatz könnte darin bestehen, die Member-Funktion static zu erstellen. In diesem Fall muss kein Objekt aufgerufen werden, und Sie können es mit dem Typ void (*)(int, int) verwenden.

Wenn Sie auf ein nicht statisches Member Ihrer Klasse und zugreifen müssen, müssen Sie bei Funktionszeigern bleiben, z. B. weil die Funktion Teil einer C-Schnittstelle ist. Die beste Option ist, immer einen void* an Ihre Funktion zu übergeben Nehmen Sie die Funktionszeiger und rufen Sie Ihr Mitglied über eine Weiterleitungsfunktion auf, die ein Objekt aus dem void* abruft und dann die Mitgliedsfunktion aufruft.

In einer richtigen C++ - Schnittstelle können Sie einen Blick darauf werfen, dass Ihre Funktion ein Templatargument für Funktionsobjekte verwendet, um beliebige Klassentypen zu verwenden. Wenn die Verwendung einer Schnittstelle mit Vorlagen nicht erwünscht ist, sollten Sie etwas wie std::function<void(int, int)> verwenden: Sie können ein entsprechend aufrufbares Funktionsobjekt für diese erstellen, z. B. mit std::bind().

Die typsicheren Ansätze mit einem Vorlagenargument für den Klassentyp oder einem geeigneten std::function<...> sind gegenüber der Verwendung einer void*-Schnittstelle zu bevorzugen, da sie das Potenzial für Fehler aufgrund einer Umwandlung in den falschen Typ beseitigen.

Um zu erläutern, wie ein Funktionszeiger zum Aufrufen einer Member-Funktion verwendet wird, folgt ein Beispiel:

// 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);
}
107
Dietmar Kühl

Die Antwort von @Pete Becker ist in Ordnung, aber Sie können dies auch tun, ohne die class-Instanz als expliziten Parameter an function1 in C++ 11 zu übergeben:

#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;
}
62
Matt Phillips

Ein Zeiger auf eine Elementfunktion unterscheidet sich von einem Zeiger auf eine Funktion. Um eine Elementfunktion über einen Zeiger zu verwenden, benötigen Sie einen Zeiger (offensichtlich) und ein Objekt, auf das sie angewendet werden soll. Die entsprechende Version von function1 wäre also

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

und um es zu nennen:

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

Wenn Sie function1 ändern können, tun Sie dies seit 2011:

#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);
    });
}

( Live-Demo )

Beachten Sie auch, dass ich Ihre defekte Objektdefinition korrigiert habe (aClass a(); deklariert eine Funktion).

Ich habe eine ähnliche Frage gestellt ( C++ openframeworks, die Void von anderen Klassen durchläuft ), aber die Antwort, die ich fand, war klarer, also hier die Erklärung für zukünftige Datensätze:

die Verwendung von std :: function ist einfacher als in:

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

und dann anrufen als:

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

oder noch einfacher:

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

hier erstellen Sie ein Lambda, das das Objekt aufruft, das es als Referenz aufnimmt

1

Sie können jetzt aufhören, Ihren Kopf zu schlagen. Hier ist der Wrapper für die Member-Funktion zur Unterstützung der vorhandenen - Funktionen, wobei die einfachen C - Funktionen als Argumente verwendet werden. thread_local Direktive ist hier der Schlüssel.

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;
}

Bitte kommentieren Sie alle Probleme mit diesem Ansatz.

Andere Antworten schlagen keine vorhandenen einfachen C-Funktionen vor: http://cpp.sh/8exun

0
neckTwi

Ich habe die Memberfunktion als statisch und für alle Arbeiten festgelegt:

#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;
}
0
mathengineer