2016-03-20 15 views
0

Ich habe zwei Strukturen, Struktur B erbt von Struktur A. Ich möchte eine Funktion als Parameter von Struktur B an Struktur A für Struktur A übergeben.C++ Übergabe der Strukturfunktion als Parameter für eine Funktion in einer geerbten Struktur

Dies ist ein Beispiel für das, was ich erreichen möchte und das Problem, in das ich renne, wäre der vollständige Code TL; DR.

struct A 
{ 
    int32 mynum; 

    void Tick(float delta, bool doThis, void (funcparam)(float, bool, int32)) 
    { 
     funcparam(delta, doThis, mynum); 
    } 
}; 

struct B : public A 
{ 
    void RunThis(float deltaTime, bool doIt, int32 which) 
    { 
     // do something when called by Struct A 
    }; 

    void DoSomething() 
    { 
     Tick(0.1f, true, &B::RunThis); 
    }; 
}; 

Das Problem ist mit dieser Zeile: Tick(0.1f, true, &B::RunThis); von der Funktion void DoSomething(), wenn ich es falsch von Anfang an getan habe, aber ich glaube, ich es falsch bin vorbei, da es im Header noch ist der Struktur, die ich gerade definiere?

Fehler (ich den Fehler geändert haben, meinem Beispiel zu passen, ich glaube nicht, dass ich vermasselt ..):

error C2664: 'void A::Tick(float,bool,void (__cdecl *)(float,bool))': cannot convert argument 3 from 'void (__cdecl B::*)(float,bool)' to 'void (__cdecl *)(float,bool)'

die B:: von &B::RunThis natürlich Weglassen löst nichts.

+0

Schauen Sie in 'std :: function()', Ihr Funktionszeiger 'void (funcparam) (float, bool, int32)' kann keine Mitgliedsfunktionszeiger aufnehmen. –

+1

'' void (funcparam) (float, bool, int32) '' ist nicht was du willst. Zuerst ist die Signatur eines (Nicht-Mitglieds) Funktionszeigers '' void (* funcparam) (float, bool, int32) '' (Sie haben das * verpasst). Zweitens könnten Sie Tick() eine Funktion der Basisklasse machen, die dann eine virtuelle Funktion aufruft, die Sie in der abgeleiteten Klasse überschreiben. Dies ist immer noch ein besserer Ansatz im Vergleich zur Verwendung von '' std: function'', solange Sie eine 1: 1-Beziehung von "abgeleiteter Klasse" haben: "Funktion, die Sie ausführen möchten." – BitTickler

+0

@BitTickler - das war eine viel bessere Lösung für meinen Zweck, danke! Wie für die Antwort auf die spezifische Frage, ich werde immer noch darauf warten, dass es veröffentlicht wird, so dass Leute, die ähnliche Probleme googlen, eine genaue Antwort haben – Vii

Antwort

1

Erste Option: Verwenden Sie stattdessen eine virtuelle Funktion.

Funktioniert am besten, wenn Sie 1 und nur 1 Funktion pro abgeleitete Klasse sowieso haben.

struct A { 
    virtual void RunThis(float deltaTime, bool doIt, int32 which) = 0; 
    void Tick(float delta, bool doThis) 
    { 
     //... 
     RunThis(delta, doThis, which); 
     // ... 
    } 
    virtual ~A() // virtual destructor... 
    {} 
}; 

struct B : public A 
{ 
    virtual void RunThis(float deltaTime, bool doIt, int32 which) 
    { 
    } 
    void DoSomething(/*...*/) 
    { 
     // ... 
     Tick(/*...*/); 
    } 

}; 

Zweite Option: std :: function + Lambda oder Member-Funktion in struct B

#include <functional> 
struct A 
{ 
    void Tick(float delta, bool doit, std::function<void(float,bool,int32)> action) 
    { 
    } 
}; 

struct B : public struct A 
{ 
    void DoSomething(/*...*/) 
    { 
     // you can use a lambda, if convenient... 
     Tick(/*...*/, 
      [this](float delta, bool doit, int32_t which) -> void { 
       // ... 
      }); 
    } 

    // or you can also use a pointer to member: 
    void RunThis(/*...*/) { /* ... */ } 
    void DoSomething(/*...*/) 
    { 
     std::function<void(float,bool,int32)> f = std::bind(&B::RunThis, this, _1,_2,_3); 
     Tick(/*...*/, f); 
    } 
}; 
+0

Oder ein Lambda und eine Methode anstelle von 'std :: bind' mit' [this] (float delta, bool doit, int32_t welche) {return this-> RunThis (delta, doit, which); } ' – 6502

+0

@ 6502 Ja, das einzige, was nicht funktioniert, wäre zu versuchen, das Lambda an A :: tick() ohne eine std :: -Funktion zu übergeben, da dann der Typ unbekannt wäre ... was ich immer noch nicht mache Ich denke, eine gute Sache :) – BitTickler

0

Leider C++ Funktionszeiger nicht Kontext haben ... mit anderen Worten einen Zeiger auf Funktion eine ganze Zahl zu akzeptieren

dürfen keine Daten an den Zeiger "gebunden" haben und nur auf Parameter und globale Variablen zugreifen. Ein für einige Compiler implementierter Workaround ist "trampoline" libraries, die das Binden von benutzerdefinierten Daten an Funktionszeiger ermöglichen, aber alle basieren auf dynamischer Codeerstellung zur Laufzeit, was innerhalb der Grenzen von Standard-C++ nicht möglich ist.

Die Lösung in Standard-C++ ist

std::function<void(int)> 

anstelle von

void (*)(int) 

Ein std::function Objekt verwenden kann mit der Funktion zugeordneten Daten halten und erlaubt die Erstellung von Verschlüssen. Es ist auch abwärtskompatibel mit Funktionszeigern (d. H. Ein Funktionszeiger kann implizit in ein std::function Objekt umgewandelt werden).

verwenden, dass der Code beispielsweise werden können

struct A { 
    int32 mynum; 
    void Tick(float delta, bool doThis, 
       std::function<void(float, bool, int32)> funcparam) { 
     funcparam(delta, doThis, mynum); 
    } 
}; 

struct B : public A { 
    void RunThis(float deltaTime, bool doIt, int32 which) { 
     // do something when called by Struct A 
    }; 
    void DoSomething() { 
     Tick(0.1f, true, [this](float dt, bool di, int32 w) { 
      this->RunThis(dt, di, w); 
     }); 
    } 
}; 

wo ein Lambda verwendet wurde, um die Methode aufzurufen.

Eine portable Problemumgehung, wenn Sie den Code nicht ändern können, indem Sie einen Funktionszeiger akzeptieren, ist eine vordefinierte Gruppe von Funktionszeigern für eine bestimmte Signatur ...zum Beispiel

void *closure_data[10]; 
void (*closure_code[10])(void *, int); 

void closure0(int x){ closure_code[0](closure_data[0], x); } 
void closure1(int x){ closure_code[1](closure_data[1], x); } 
void closure2(int x){ closure_code[2](closure_data[2], x); } 
... 
void closure9(int x){ closure_code[9](closure_data[9], x); } 

void (*closures[10])(int) = {closure0, 
          closure1, 
          ... 
          closure9}; 

und dann müssen Sie „zuweisen“, um eine Schließung i, Ihre Kontextdaten in den entsprechenden closure_data[i] setzen, den Code in closure_code[i] und übergeben Sie als Funktionszeiger closures[i].

Normalerweise anständiger C++ 03-Code oder C-Code zu akzeptieren Funktionszeiger für Rückrufe immer einen Kontextparameter bieten ... dh die Schnittstelle hat die Form

void Tick(void *context, 
      void (*callback)(float, bool, int32, void *)) 

und wird der Rückruf ruft auch die undurchsichtigen void *context vorbei spezifiziert durch den Aufrufer, der den Funktionszeiger lieferte.

+0

Dort/ist/Zeiger auf Mitglied ('' void (Foo :: * funcptr)() '') in C++. Damit dies jedoch in diesem Fall funktioniert, müsste Struktur A über Struktur B wissen, was offensichtlich unerwünscht ist. – BitTickler

+0

@BitTickler: Ein Zeiger auf Member in C++ enthält keinen Kontext und erfordert, dass ein Objekt angewendet wird. Wenn ein API-Aufruf einen Funktionszeiger akzeptiert, können Sie leider keinen Zeiger an das Element übergeben ... es sind verschiedene Abstraktionen (die Ausnahme sind natürlich 'statische' Elementfunktionen ... das sind in der Tat nur normale Funktionen mit einem funkigen Namen). – 6502

Verwandte Themen