Wie kann ich eine Klassenmitgliedsfunktion als Rückruf übergeben?

76

Ich verwende eine API, bei der ich einen Funktionszeiger als Rückruf übergeben muss. Ich versuche, diese API aus meiner Klasse zu verwenden, erhalte jedoch Kompilierungsfehler.

Folgendes habe ich von meinem Konstruktor gemacht:

m_cRedundencyManager->Init(this->RedundencyManagerCallBack);

Dies wird nicht kompiliert - ich erhalte die folgende Fehlermeldung:

Fehler 8 Fehler C3867: 'CLoggersInfra :: RedundencyManagerCallBack': Liste der fehlenden Argumente des Funktionsaufrufs; Verwenden Sie '& CLoggersInfra :: RedundencyManagerCallBack', um einen Zeiger auf das Mitglied zu erstellen

Ich habe versucht, den Vorschlag zu verwenden &CLoggersInfra::RedundencyManagerCallBack- hat bei mir nicht funktioniert.

Irgendwelche Vorschläge / Erklärungen dafür?

Ich benutze VS2008.

Vielen Dank!!

ofer
quelle

Antworten:

55

Dies funktioniert nicht, da ein Elementfunktionszeiger nicht wie ein normaler Funktionszeiger behandelt werden kann, da er ein "this" -Objektargument erwartet.

Stattdessen können Sie eine statische Elementfunktion wie folgt übergeben, die in dieser Hinsicht wie normale Nichtmitgliedsfunktionen ist:

m_cRedundencyManager->Init(&CLoggersInfra::Callback, this);

Die Funktion kann wie folgt definiert werden

static void Callback(int other_arg, void * this_pointer) {
    CLoggersInfra * self = static_cast<CLoggersInfra*>(this_pointer);
    self->RedundencyManagerCallBack(other_arg);
}
Johannes Schaub - litb
quelle
4
Obwohl dies eine Lösung / Problemumgehung für das OP sein kann, sehe ich nicht, wie dies eine Antwort auf die eigentliche Frage ist.
Stefan Steiger
1
@StefanSteiger Die Antwort (Erklärung) befindet sich im letzten Absatz (im Wesentlichen: "Ein Elementfunktionszeiger kann nicht wie ein Zeiger auf eine freie Funktion behandelt werden"), und der Vorschlag, was sonst zu tun ist, befindet sich in den anderen Teilen meiner Antwort. Es ist wahr, dass es ausführlicher sein könnte. Aber das ist in Ordnung und deshalb hat meine Antwort nicht so viele positive Stimmen erhalten wie die anderen. Manchmal helfen knappere Antworten, die im Wesentlichen nur den benötigten Code enthalten, besser als längere, und deshalb wurde meine Antwort akzeptiert.
Johannes Schaub - litb
Schaub: Ja, genau mein Punkt. Aber ich verstehe - Sie hätten zuerst den letzten Teil schreiben und dann sagen sollen: Stattdessen können Sie dies tun + (erster Teil)
Stefan Steiger
124

Dies ist eine einfache Frage, aber die Antwort ist überraschend komplex. Die kurze Antwort lautet: Sie können das tun, was Sie mit std::bind1stoder versuchen boost::bind. Die längere Antwort ist unten.

Der Compiler schlägt vor, dass Sie verwenden &CLoggersInfra::RedundencyManagerCallBack. Erstens, wenn RedundencyManagerCallBackes sich um eine Mitgliedsfunktion handelt, gehört die Funktion selbst nicht zu einer bestimmten Instanz der Klasse CLoggersInfra. Es gehört zur Klasse selbst. Wenn Sie schon einmal eine statische Klassenfunktion aufgerufen haben, haben Sie möglicherweise bemerkt, dass Sie dieselbe SomeClass::SomeMemberFunctionSyntax verwenden. Da die Funktion selbst in dem Sinne 'statisch' ist, dass sie eher zur Klasse als zu einer bestimmten Instanz gehört, verwenden Sie dieselbe Syntax. Das '&' ist notwendig, da Sie Funktionen technisch gesehen nicht direkt übergeben - Funktionen sind in C ++ keine realen Objekte. Stattdessen übergeben Sie technisch die Speicheradresse für die Funktion, dh einen Zeiger darauf, wo die Anweisungen der Funktion im Speicher beginnen. Die Konsequenz ist die gleiche, Sie sind effektiv '

Aber das ist in diesem Fall nur das halbe Problem. Wie gesagt, RedundencyManagerCallBackdie Funktion gehört nicht zu einer bestimmten Instanz. Es hört sich jedoch so an, als ob Sie es als Rückruf für eine bestimmte Instanz übergeben möchten. Um zu verstehen, wie dies zu tun ist, müssen Sie verstehen, was Elementfunktionen wirklich sind: reguläre Funktionen, die in keiner Klasse definiert sind, mit einem zusätzlichen versteckten Parameter.

Zum Beispiel:

class A {
public:
    A() : data(0) {}
    void foo(int addToData) { this->data += addToData; }

    int data;
};

...

A an_a_object;
an_a_object.foo(5);
A::foo(&an_a_object, 5); // This is the same as the line above!
std::cout << an_a_object.data; // Prints 10!

Wie viele Parameter braucht A::fooman? Normalerweise würden wir sagen 1. Aber unter der Haube braucht foo wirklich 2. Wenn man die Definition von A :: foo betrachtet, braucht es eine bestimmte Instanz von A, damit der Zeiger 'this' aussagekräftig ist (der Compiler muss wissen, was '). das ist). Die Art und Weise, wie Sie normalerweise angeben, was "dies" sein soll, erfolgt über die Syntax MyObject.MyMemberFunction(). Dies ist jedoch nur syntaktischer Zucker zum Übergeben der Adresse von MyObjectals erstem Parameter anMyMemberFunction. Wenn wir Elementfunktionen in Klassendefinitionen deklarieren, fügen wir 'this' nicht in die Parameterliste ein, aber dies ist nur ein Geschenk der Sprachdesigner, um die Eingabe zu sparen. Stattdessen müssen Sie angeben, dass eine Elementfunktion statisch ist, um sie zu deaktivieren und automatisch den zusätzlichen Parameter 'this' zu erhalten. Wenn der C ++ - Compiler das obige Beispiel in C-Code übersetzen würde (der ursprüngliche C ++ - Compiler hat tatsächlich so funktioniert), würde er wahrscheinlich Folgendes schreiben:

struct A {
    int data;
};

void a_init(A* to_init)
{
    to_init->data = 0;
}

void a_foo(A* this, int addToData)
{ 
    this->data += addToData;
}

...

A an_a_object;
a_init(0); // Before constructor call was implicit
a_foo(&an_a_object, 5); // Used to be an_a_object.foo(5);

Zurück zu Ihrem Beispiel: Es gibt jetzt ein offensichtliches Problem. 'Init' möchte einen Zeiger auf eine Funktion, die einen Parameter akzeptiert. Ist &CLoggersInfra::RedundencyManagerCallBackaber ein Zeiger auf eine Funktion, die zwei Parameter akzeptiert, den normalen Parameter und den geheimen 'this'-Parameter. Aus diesem Grund wird immer noch ein Compilerfehler angezeigt (als Randnotiz: Wenn Sie jemals Python verwendet haben, ist diese Art von Verwirrung der Grund, warum für alle Mitgliedsfunktionen ein 'self'-Parameter erforderlich ist).

Die ausführliche Methode, um damit umzugehen, besteht darin, ein spezielles Objekt zu erstellen, das einen Zeiger auf die gewünschte Instanz enthält und über eine Elementfunktion verfügt, die so etwas wie "run" oder "execute" heißt (oder den Operator "()" überlädt), der die Parameter übernimmt für die Member-Funktion und ruft einfach die Member-Funktion mit diesen Parametern in der gespeicherten Instanz auf. Dies würde jedoch erfordern, dass Sie 'Init' ändern, um Ihr spezielles Objekt anstelle eines rohen Funktionszeigers zu verwenden, und es klingt so, als wäre Init der Code eines anderen. Und wenn Sie für jedes Problem eine spezielle Klasse erstellen, wird der Code aufgebläht.

Nun endlich die gute Lösung boost::bindund boost::functiondie Dokumentation für jede, die Sie hier finden:

boost :: bind docs , boost :: function docs

boost::bindMit dieser Funktion können Sie eine Funktion und einen Parameter für diese Funktion übernehmen und eine neue Funktion erstellen, bei der dieser Parameter "gesperrt" ist. Wenn ich also eine Funktion habe, die zwei Ganzzahlen hinzufügt, kann boost::bindich eine neue Funktion erstellen, bei der einer der Parameter auf 5 gesperrt ist. Diese neue Funktion akzeptiert nur einen Ganzzahlparameter und fügt immer 5 speziell hinzu. Mit dieser Technik können Sie den versteckten Parameter "this" als eine bestimmte Klasseninstanz "sperren" und eine neue Funktion generieren, die nur einen Parameter verwendet, genau wie Sie möchten (beachten Sie, dass der versteckte Parameter immer der erste Parameter ist). und die normalen Parameter kommen in der Reihenfolge danach). Schaue auf dieboost::bindDokumente zum Beispiel, sie diskutieren sogar speziell die Verwendung für Mitgliedsfunktionen. Technisch gesehen gibt es eine Standardfunktion [std::bind1st][3], die Sie ebenfalls verwenden können, die jedoch boost::bindallgemeiner ist.

Natürlich gibt es nur noch einen Haken. boost::bindwird eine schöne boost :: function für dich machen, aber dies ist technisch immer noch kein roher Funktionszeiger, wie Init es wahrscheinlich will. Glücklicherweise bietet Boost eine Möglichkeit, Boost :: -Funktionen in Rohzeiger zu konvertieren, wie hier in StackOverflow dokumentiert . Wie es dies umsetzt, geht über den Rahmen dieser Antwort hinaus, obwohl es auch interessant ist.

Machen Sie sich keine Sorgen, wenn dies lächerlich schwierig erscheint - Ihre Frage schneidet mehrere der dunkleren Ecken von C ++ und boost::bindist unglaublich nützlich, sobald Sie sie gelernt haben.

C ++ 11-Update: Stattdessen boost::bindkönnen Sie jetzt eine Lambda-Funktion verwenden, die 'this' erfasst. Dies bedeutet im Grunde, dass der Compiler dasselbe für Sie generiert.

Joseph Garvin
quelle
2
Das ist eine großartige Antwort!
Erdferkel
5
Zu Beginn der Antwort wird std :: bind1st als Möglichkeit zur Implementierung der Lösung vorgeschlagen, der letzte Teil der Antwort bezieht sich jedoch ausschließlich auf boost :: bind. Wie kann std :: bind1st verwendet werden?
Mabraham
1
@mabraham Ok, ich habe ein kurzes Beispiel hinzugefügt, obwohl es nicht vollständig zur Frage passt (VS2008): stackoverflow.com/a/45525074/4566599
Roi Danton
3
Dies sollte die akzeptierte Antwort sein! Die akzeptierte Antwort funktioniert nicht, wenn Sie die Bibliothek nicht ändern oder optionale Argumente übergeben können.
Carson McNeil
1
@ Joseph Garvin: Ich kann nicht sehen, wie std :: bind die Antwort ist. Dies erfordert, dass das Argument vom Typ std :: function anstelle eines normalen C-Funktionszeigers ist. Nur weil du dich versteckst, ist es nicht besser als die akzeptierte Antwort. Nun, ok, wenn Sie Zugriff auf Quellenebene auf die betreffende Funktionssignatur haben, können Sie foo * in std :: function <foo_signature> ändern, und dann müssen Sie dies nur ändern, vorausgesetzt, alle Compiler haben auf C ++ 11 aktualisiert Wenn Sie jedoch keinen Quellzugriff haben, sind Sie F * ED, da die Signaturen nicht kompatibel sind. Dies entspricht einem C ++ - Lambda-Ausdruck.
Stefan Steiger
20

Diese Antwort ist eine Antwort auf einen Kommentar oben und funktioniert nicht mit VisualStudio 2008, sollte jedoch bei neueren Compilern bevorzugt werden.


In der Zwischenzeit müssen Sie keinen Void-Zeiger mehr verwenden und es besteht auch kein Boost mehr, da std::bindund std::functionverfügbar sind. Ein Vorteil (im Vergleich zu ungültigen Zeigern) ist die Typensicherheit, da der Rückgabetyp und die Argumente explizit angegeben werden mit std::function:

// std::function<return_type(list of argument_type(s))>
void Init(std::function<void(void)> f);

Anschließend können Sie den Funktionszeiger mit erstellen std::bindund an Init übergeben:

auto cLoggersInfraInstance = CLoggersInfra();
auto callback = std::bind(&CLoggersInfra::RedundencyManagerCallBack, cLoggersInfraInstance);
Init(callback);

Vollständiges Beispiel für die Verwendung std::bindmit Funktionen für Mitglieder, statische Mitglieder und Nichtmitglieder:

#include <functional>
#include <iostream>
#include <string>

class RedundencyManager // incl. Typo ;-)
{
public:
    // std::function<return_type(list of argument_type(s))>
    std::string Init(std::function<std::string(void)> f) 
    {
        return f();
    }
};

class CLoggersInfra
{
private:
    std::string member = "Hello from non static member callback!";

public:
    static std::string RedundencyManagerCallBack()
    {
        return "Hello from static member callback!";
    }

    std::string NonStaticRedundencyManagerCallBack()
    {
        return member;
    }
};

std::string NonMemberCallBack()
{
    return "Hello from non member function!";
}

int main()
{
    auto instance = RedundencyManager();

    auto callback1 = std::bind(&NonMemberCallBack);
    std::cout << instance.Init(callback1) << "\n";

    // Similar to non member function.
    auto callback2 = std::bind(&CLoggersInfra::RedundencyManagerCallBack);
    std::cout << instance.Init(callback2) << "\n";

    // Class instance is passed to std::bind as second argument.
    // (heed that I call the constructor of CLoggersInfra)
    auto callback3 = std::bind(&CLoggersInfra::NonStaticRedundencyManagerCallBack,
                               CLoggersInfra()); 
    std::cout << instance.Init(callback3) << "\n";
}

Mögliche Ausgabe:

Hello from non member function!
Hello from static member callback!
Hello from non static member callback!

Darüber hinaus können std::placeholdersSie Argumente dynamisch an den Rückruf übergeben (z. B. ermöglicht dies die Verwendung von return f("MyString");in, Initwenn f einen Zeichenfolgenparameter hat).

Roi Danton
quelle
1
Ein echtes Dankeschön von mir für diese Antwort! Ich verbringe bereits über zwei Stunden damit, verschiedene Ansätze zu suchen und auszuprobieren, nichts hat wirklich funktioniert. Aber dieser ist so einfach, dass er erst nach 1 Minute funktioniert hat.
BadK
3

Welches Argument braucht Inites? Was ist die neue Fehlermeldung?

Methodenzeiger in C ++ sind etwas schwierig zu verwenden. Neben dem Methodenzeiger selbst müssen Sie (in Ihrem Fall this) auch einen Instanzzeiger angeben . Vielleicht Initerwartet es als separates Argument?

Konrad Rudolph
quelle
3

Ein Zeiger auf eine Klassenmitgliedsfunktion ist nicht dasselbe wie ein Zeiger auf eine Funktion. Ein Klassenmitglied verwendet ein implizites zusätzliches Argument (den Zeiger this ) und verwendet eine andere Aufrufkonvention.

Wenn Ihre API eine Rückruffunktion erwartet, die kein Mitglied ist, müssen Sie diese an sie übergeben.

jalf
quelle
3

Können m_cRedundencyManagerMitgliedsfunktionen verwendet werden? Die meisten Rückrufe sind so eingerichtet, dass sie reguläre Funktionen oder statische Elementfunktionen verwenden. Weitere Informationen finden Sie auf dieser Seite unter C ++ FAQ Lite.

Update: Die von Ihnen angegebene Funktionsdeklaration zeigt, m_cRedundencyManagerdass eine Funktion des Formulars erwartet wird : void yourCallbackFunction(int, void *). Mitgliedsfunktionen sind daher in diesem Fall als Rückrufe nicht akzeptabel. Eine statische Elementfunktion funktioniert möglicherweise , aber wenn dies in Ihrem Fall nicht akzeptabel ist, funktioniert auch der folgende Code. Beachten Sie, dass es eine böse Besetzung von verwendet void *.


// in your CLoggersInfra constructor:
m_cRedundencyManager->Init(myRedundencyManagerCallBackHandler, this);

// in your CLoggersInfra header:
void myRedundencyManagerCallBackHandler(int i, void * CLoggersInfraPtr);

// in your CLoggersInfra source file:
void myRedundencyManagerCallBackHandler(int i, void * CLoggersInfraPtr)
{
    ((CLoggersInfra *)CLoggersInfraPtr)->RedundencyManagerCallBack(i);
}
James
quelle
3

Nekromantie.
Ich denke, die bisherigen Antworten sind etwas unklar.

Machen wir ein Beispiel:

Angenommen, Sie haben ein Array von Pixeln (Array von ARGB int8_t-Werten)

// A RGB image
int8_t* pixels = new int8_t[1024*768*4];

Jetzt möchten Sie ein PNG generieren. Dazu rufen Sie die Funktion toJpeg auf

bool ok = toJpeg(writeByte, pixels, width, height);

Dabei ist writeByte eine Rückruffunktion

void writeByte(unsigned char oneByte)
{
    fputc(oneByte, output);
}

Das Problem hier: Die Ausgabe von FILE * muss eine globale Variable sein.
Sehr schlecht, wenn Sie sich in einer Multithread-Umgebung befinden (z. B. einem http-Server).

Sie benötigen also eine Möglichkeit, die Ausgabe zu einer nicht globalen Variablen zu machen, während die Rückrufsignatur beibehalten wird.

Die unmittelbare Lösung, die in den Sinn kommt, ist ein Abschluss, den wir mithilfe einer Klasse mit einer Elementfunktion emulieren können.

class BadIdea {
private:
    FILE* m_stream;
public:
    BadIdea(FILE* stream)  {
        this->m_stream = stream;
    }

    void writeByte(unsigned char oneByte){
            fputc(oneByte, this->m_stream);
    }

};

Und dann tun

FILE *fp = fopen(filename, "wb");
BadIdea* foobar = new BadIdea(fp);

bool ok = TooJpeg::writeJpeg(foobar->writeByte, image, width, height);
delete foobar;
fflush(fp);
fclose(fp);

Dies funktioniert jedoch wider Erwarten nicht.

Der Grund dafür ist, dass C ++ - Mitgliedsfunktionen wie C # -Erweiterungsfunktionen implementiert sind.

Also hast du

class/struct BadIdea
{
    FILE* m_stream;
}

und

static class BadIdeaExtensions
{
    public static writeByte(this BadIdea instance, unsigned char oneByte)
    {
         fputc(oneByte, instance->m_stream);
    }

}

Wenn Sie also writeByte aufrufen möchten, müssen Sie nicht nur die Adresse von writeByte übergeben, sondern auch die Adresse der BadIdea-Instanz.

Wenn Sie also ein typedef für die writeByte-Prozedur haben und es so aussieht

typedef void (*WRITE_ONE_BYTE)(unsigned char);

Und Sie haben eine writeJpeg-Signatur, die so aussieht

bool writeJpeg(WRITE_ONE_BYTE output, uint8_t* pixels, uint32_t 
 width, uint32_t height))
    { ... }

Es ist grundsätzlich unmöglich, eine Elementfunktion mit zwei Adressen an einen Funktionszeiger mit einer Adresse zu übergeben (ohne writeJpeg zu ändern), und daran führt kein Weg vorbei.

Das nächstbeste, was Sie in C ++ tun können, ist die Verwendung einer Lambda-Funktion:

FILE *fp = fopen(filename, "wb");
auto lambda = [fp](unsigned char oneByte) { fputc(oneByte, fp);  };
bool ok = TooJpeg::writeJpeg(lambda, image, width, height);

Da Lambda jedoch nichts anderes tut, als eine Instanz an eine versteckte Klasse (wie die "BadIdea" -Klasse) zu übergeben, müssen Sie die Signatur von writeJpeg ändern.

Der Vorteil von Lambda gegenüber einer manuellen Klasse besteht darin, dass Sie nur ein typedef ändern müssen

typedef void (*WRITE_ONE_BYTE)(unsigned char);

zu

using WRITE_ONE_BYTE = std::function<void(unsigned char)>; 

Und dann können Sie alles andere unberührt lassen.

Sie können auch std :: bind verwenden

auto f = std::bind(&BadIdea::writeByte, &foobar);

Dies erzeugt jedoch hinter den Kulissen nur eine Lambda-Funktion, die dann auch die Änderung in typedef benötigt.

Nein, es gibt keine Möglichkeit, eine Elementfunktion an eine Methode zu übergeben, für die ein statischer Funktionszeiger erforderlich ist.

Aber Lambdas sind der einfache Weg, vorausgesetzt, Sie haben die Kontrolle über die Quelle.
Ansonsten hast du kein Glück.
Mit C ++ können Sie nichts anfangen.

Hinweis: Die
Funktion std :: erfordert#include <functional>

Da Sie in C ++ auch C verwenden können, können Sie dies mit libffcall in einfachem C tun, wenn Sie nichts dagegen haben, eine Abhängigkeit zu verknüpfen.

Laden Sie libffcall von GNU herunter (zumindest unter Ubuntu, verwenden Sie nicht das von der Distribution bereitgestellte Paket - es ist kaputt), entpacken Sie es.

./configure
make
make install

gcc main.c -l:libffcall.a -o ma

Haupt c:

#include <callback.h>

// this is the closure function to be allocated 
void function (void* data, va_alist alist)
{
     int abc = va_arg_int(alist);

     printf("data: %08p\n", data); // hex 0x14 = 20
     printf("abc: %d\n", abc);

     // va_start_type(alist[, return_type]);
     // arg = va_arg_type(alist[, arg_type]);
     // va_return_type(alist[[, return_type], return_value]);

    // va_start_int(alist);
    // int r = 666;
    // va_return_int(alist, r);
}



int main(int argc, char* argv[])
{
    int in1 = 10;

    void * data = (void*) 20;
    void(*incrementer1)(int abc) = (void(*)()) alloc_callback(&function, data);
    // void(*incrementer1)() can have unlimited arguments, e.g. incrementer1(123,456);
    // void(*incrementer1)(int abc) starts to throw errors...
    incrementer1(123);
    // free_callback(callback);
    return EXIT_SUCCESS;
}

Wenn Sie CMake verwenden, fügen Sie die Linkerbibliothek nach add_executable hinzu

add_library(libffcall STATIC IMPORTED)
set_target_properties(libffcall PROPERTIES
        IMPORTED_LOCATION /usr/local/lib/libffcall.a)
target_link_libraries(BitmapLion libffcall)

oder Sie können libffcall einfach dynamisch verknüpfen

target_link_libraries(BitmapLion ffcall)

Hinweis:
Möglicherweise möchten Sie die libffcall-Header und -Bibliotheken einschließen oder ein cmake-Projekt mit dem Inhalt von libffcall erstellen.

Stefan Steiger
quelle
1

Ich kann sehen, dass der Init die folgende Überschreibung hat:

Init(CALLBACK_FUNC_EX callback_func, void * callback_parm)

wo CALLBACK_FUNC_EXist

typedef void (*CALLBACK_FUNC_EX)(int, void *);
ofer
quelle
1

Eine einfache Lösung "Problemumgehung" besteht weiterhin darin, eine Klasse von "Schnittstellen" für virtuelle Funktionen zu erstellen und diese an die Aufruferklasse zu erben. Übergeben Sie es dann als Parameter "könnte im Konstruktor sein" der anderen Klasse, die Sie Ihre Aufruferklasse zurückrufen möchten.

DEFINE-Schnittstelle:

class CallBack 
{
   virtual callMeBack () {};
};

Dies ist die Klasse, die Sie zurückrufen möchten:

class AnotherClass ()
{
     public void RegisterMe(CallBack *callback)
     {
         m_callback = callback;
     }

     public void DoSomething ()
     {
        // DO STUFF
        // .....
        // then call
        if (m_callback) m_callback->callMeBack();
     }
     private CallBack *m_callback = NULL;
};

Und dies ist die Klasse, die zurückgerufen wird.

class Caller : public CallBack
{
    void DoSomthing ()
    {
    }

    void callMeBack()
    {
       std::cout << "I got your message" << std::endl;
    }
};
M.Hefny
quelle
0

Diese Frage und Antwort aus dem C ++ FAQ Lite behandelt Ihre Frage und die Überlegungen, die mit der Antwort verbunden sind, sehr gut, denke ich. Kurzer Ausschnitt von der Webseite, die ich verlinkt habe:

Tu es nicht.

Da eine Mitgliedsfunktion ohne ein Objekt, auf das sie aufgerufen werden kann, bedeutungslos ist, können Sie dies nicht direkt tun (wenn das X-Fenstersystem in C ++ neu geschrieben wurde, würde es wahrscheinlich Verweise auf Objekte herumgeben, nicht nur auf Zeiger auf Funktionen, natürlich auf die Objekte würde die erforderliche Funktion verkörpern und wahrscheinlich noch viel mehr).

Onorio Catenacci
quelle
1
Der Link lautet jetzt isocpp.org/wiki/faq/pointers-to-members#memfnptr-vs-fnptr ; sieht aus wie er jetzt sagt "Nicht". Aus diesem Grund sind Nur-Link-Antworten nicht gut.
lmat - Wiedereinsetzung Monica
2
Ich habe die Antwort @LimitedAtonement geändert. Vielen Dank für den Hinweis. Sie haben völlig Recht, dass nur Link-Antworten Antworten von geringer Qualität sind. Aber das wussten wir 2008 noch nicht :-P
Onorio Catenacci
0

Der Zeigertyp auf die nicht statische Elementfunktion unterscheidet sich vom Zeiger auf die normale Funktion .
Typ ist, void(*)(int)wenn es sich um eine normale oder statische Elementfunktion handelt.
Typ ist, void(CLoggersInfra::*)(int)wenn es sich um eine nicht statische Elementfunktion handelt.
Sie können also keinen Zeiger an eine nicht statische Elementfunktion übergeben, wenn sie einen normalen Funktionszeiger erwartet.

Darüber hinaus hat eine nicht statische Elementfunktion einen impliziten / versteckten Parameter für das Objekt. Der thisZeiger wird implizit als Argument an den Member-Funktionsaufruf übergeben. Daher können die Elementfunktionen nur durch Bereitstellen eines Objekts aufgerufen werden.

Wenn die API Init nicht geändert werden kann, kann eine Wrapper-Funktion (normale Funktion oder eine statische Elementfunktion der Klasse) verwendet werden, die das Element aufruft. Im schlimmsten Fall wäre das Objekt ein globales Objekt, auf das die Wrapper-Funktion zugreifen kann.

CLoggersInfra* pLoggerInfra;

RedundencyManagerCallBackWrapper(int val)
{
    pLoggerInfra->RedundencyManagerCallBack(val);
}
m_cRedundencyManager->Init(RedundencyManagerCallBackWrapper);

Wenn die API Init kann geändert werden, gibt es viele Alternativen - Objekt nicht-statisches Element Funktionszeiger, Funktionsobjekt, std::functionoder Schnittstellenfunktion.

Im Beitrag zu Rückrufen finden Sie Informationen zu den verschiedenen Varianten mit C ++ - Arbeitsbeispielen .

ap-osd
quelle
0

Sieht so aus, als ob std::mem_fn(C ++ 11) genau das tut, was Sie brauchen:

Die Funktionsvorlage std :: mem_fn generiert Wrapper-Objekte für Zeiger auf Mitglieder, die einen Zeiger auf Mitglieder speichern, kopieren und aufrufen können. Beim Aufrufen von std :: mem_fn können sowohl Verweise als auch Zeiger (einschließlich intelligenter Zeiger) auf ein Objekt verwendet werden.

ben
quelle