Forum: Compiler & IDEs Object und Callback


von Matze T. (gruetzwurschd)


Lesenswert?

Hallo Leute,

ich hab eine Frage.

Ich habe eine Klasse die zweimal instanzier wird.
Diese Klasse hat einen "Callback_Funktionspointer" und eine Funktion.
Nun will ich dass sobald die Callbackunktion aufgerufen wird, die 
Funktion der anderen Instanz aufgeufen wird.

Sozusagen ruft die eine Instanz der klasse die andere instanz der klasse 
auf.

Hier ein kleiner Codeschnipsel der es hoffentlich etwas anschaulicher 
macht! :)
1
class MyClass
2
{
3
  public:
4
    MyClass() {};
5
    void (*Send)( char Daten );
6
    void Receive( char Daten );
7
}
8
9
MyClass* MyObject1, MyObject2;
10
11
int main()
12
{
13
MyObject1 = new MyClass;
14
MyObject2 = new MyClass;
15
16
MyObject1->*Send = &MyObject2->Receive;
17
// Hier möchte ich festlegen dass der CallbackPointer der einen Instanz auf die Funktion der anderen Instanz zeigt.
18
19
MyObject1->Send(0x23);
20
//Hier würde ich hoffen dass die ReceiveFunktion der Instanz2 aufgerufen wird :S
21
}

Leider funktionier das so nicht!
Wäre super wenn jemand einen Rat wüsste!

Grüße Tarkan

von Oliver (Gast)


Lesenswert?

Wenn MyObject1 und MyObject2 wie in deinem Beispiel vom selben Typ sind, 
ist deren send-Funktion sowieso dieselbe (die gibt es nur einmal). Was 
soll dein Ansatz daher einen Sinn haben?

Oliver

von Matze T. (gruetzwurschd)


Lesenswert?

Ich habe ein protocol Implementiert dass daten verschlüsslen und einem 
Port übergeben soll (Send_Callback)

auf der "Anderen seite* soll die gleiche Protocolklasse sitzen und daten 
von einem Port entgegen nehmen (receive)

normalerweise laufen die beiden protocol instanzen auf verschiedenen 
maschinen.

aber um die sache zu debuggen möchte ich gerne beide protocol instanzen 
auf dem gleichen pc laufen lassen. und sie sozusagen kurzschließen(ohne 
port, denn man braucht ja keinen innerhalb eines PCs)

von Klaus W. (mfgkw)


Lesenswert?

Ich versuche das mal einzukreisen...

1. Es gibt Zeiger auf Funktionen, hier egal.

2. Es gibt Zeiger auf Methoden. Das wird hier interessanter.

3. Es gibt Objekte und Zeiger darauf.

4. Ein Zeiger auf eine Methode hat nichts damit zu tun, zu welchem
   Objekt die Methode gehört, sondern zu welcher Klasse.

5. Wenn du eine bestimmte Methode für ein bestimmtes Objekt
   aufrufen musst, dann brauchst du dazu folglich zwei Sachen:
   a) das Objekt oder einen Zeiger darauf
   b) einen Zeiger auf die Methode

Wenn ich jetzt deine Aufgabe richtig verstanden habe, dann willst
du aus dem ersten Objekt die Methode nicht nur aufrufen, sondern
die Methode für das zweite Objekt aufrufen.
Dazu müsstest du also vorher a) das Objekt und b) die Methode
setzen, weil eben die Methode nicht zu einem Objekt gehört.

von Matze T. (gruetzwurschd)


Lesenswert?

Klaus Wachtler schrieb:
> Wenn ich jetzt deine Aufgabe richtig verstanden habe, dann willst
> du aus dem ersten Objekt die Methode nicht nur aufrufen, sondern
> die Methode für das zweite Objekt aufrufen.

Exakt !

Klaus Wachtler schrieb:
> Dazu müsstest du also vorher a) das Objekt und b) die Methode
> setzen, weil eben die Methode nicht zu einem Objekt gehört.

Genau. Geht das irgendwie in einem Abwasch?

von ... (Gast)


Lesenswert?

welcher Compiler hat denn das übersetzt.
Der (Member-)FunctionsPtr "Send" speichert nur die Adresse von 
"Receive", aber nicht den für den Aufruf notwendigen "this"-Pointer auf 
das andere Objekt.

von Klaus W. (mfgkw)


Lesenswert?

Tarkan D. schrieb:
> Klaus Wachtler schrieb:
>> Dazu müsstest du also vorher a) das Objekt und b) die Methode
>> setzen, weil eben die Methode nicht zu einem Objekt gehört.
>
> Genau. Geht das irgendwie in einem Abwasch?

Mach doch in die Klasse eine set-Methode, die einen Zeiger auf das 
zweite Objekt und den Zeiger auf die Methode bekommt.
Das merkt sie sich in zwei entsprechenden Menbern.

von Klaus W. (mfgkw)


Lesenswert?

... schrieb:
> welcher Compiler hat denn das übersetzt.

Keiner.

von Matze T. (gruetzwurschd)


Lesenswert?

... schrieb:
> welcher Compiler hat denn das übersetzt.
> Der (Member-)FunctionsPtr "Send" speichert nur die Adresse von
> "Receive", aber nicht den für den Aufruf notwendigen "this"-Pointer auf
> das andere Objekt.

Keiner hat das übersetzt. Oder zumindest nicht meiner. Daher bin ich ja 
hier!

von Matze T. (gruetzwurschd)


Lesenswert?

Klaus Wachtler schrieb:
> Mach doch in die Klasse eine set-Methode, die einen Zeiger auf das
> zweite Objekt und den Zeiger auf die Methode bekommt.
> Das merkt sie sich in zwei entsprechenden Menbern.

Da hapert es ein bischen.
Wie kann ich das machen?

MyClass::SetCallback( MyClass* InstanzName  )
{
  // was muss hier stehen?

}

von Oliver (Gast)


Lesenswert?

Zum obigen Thema mag dir das hier noch helfen:

http://manderc.manderby.com/types/functionpointertype/index.php

Oliver

von Klaus W. (mfgkw)


Lesenswert?

So etwa sollte es das sein, was du vorhast:
1
#include <iostream>
2
#include <string>
3
4
5
class MyClass
6
{
7
public:
8
    MyClass( const char * werBinIch )
9
        : werBinIch( werBinIch ),
10
          dasAndere( NULL )
11
        {
12
        };
13
14
    void Receive( char Daten )
15
        {
16
            std::cout << "ich bin " << werBinIch << " und bekomme ein " << Daten << std::endl;
17
        }
18
19
    void setDenAnderen( MyClass *p_derAndere, void (MyClass::*p_dieMethodeDesAnderen)( char Daten ) )
20
        {
21
            dasAndere = p_derAndere;
22
            dieMethodeDesAnderen = p_dieMethodeDesAnderen;
23
        }
24
25
    void Send( char Daten )
26
        {
27
            if( dasAndere && dieMethodeDesAnderen )
28
            {
29
                (dasAndere->*dieMethodeDesAnderen)( Daten );
30
            }
31
        }
32
33
private:
34
35
    MyClass    *dasAndere;
36
    void (MyClass::*dieMethodeDesAnderen)( char Daten );
37
38
    std::string   werBinIch;;
39
};
40
41
MyClass  * MyObject1;
42
MyClass  * MyObject2;
43
44
int main()
45
{
46
     MyObject1 = new MyClass( "erster" );
47
     MyObject2 = new MyClass( "zweiter" );
48
49
     MyObject1->setDenAnderen( MyObject2, &MyClass::Receive );
50
51
     MyObject1->Send( 'X' );
52
}

Ausgabe mit Visual C++:
1
ich bin zweiter und bekomme ein X

von Karl H. (kbuchegg)


Lesenswert?

Eine andere, etwas allgemeinere Methode leitet sich vom Grundsatz ab:

Wenn du ein Problem nicht dingfest machen kannst, dann mach eine Klasse 
draus.


Das funktioniert so:
Anstatt eines Funktionspointers kriegt die Instanz ein Objekt übergeben, 
welches darüber bescheid weiß, welche andere Methode in welchem anderen 
Objekt aufzurufen ist. Der Auslöser der Funktionalität bemüht nicht den 
Funktionspointer um eine Methode in einem anderen Objekt aufzurufen, 
sondern er wendet sich an einen Proxy.

Dazu machen wir einfach mal eine Basisklasse, von der dann später die 
einzelnen konkreten Proxies abgeleitet werden:
1
class Proxy
2
{
3
  public:
4
    virtual ~Proxy() {}
5
    void CallIt( char Daten ) = 0;
6
};

So weit so gut.
Dein MyClass Objekt hält jetzt so einen Proxy und da das ganze Polymorph 
verwendet werden soll, hält es genau genommen einen Pointer (einen 
non-owning Pointer, soll heißen: Ein MyClass Objekt ist nicht für die 
Verwaltung new/delete der Proxies zuständig)
1
class MyClass
2
{
3
  public:
4
    MyClass() : pProxy( NULL ) {};
5
6
    void SetProxy( Proxy* pProxy ) { pProxy_ = pProxy; }
7
    void Receive( char Daten );
8
9
  private:
10
    Proxy* pProxy_;
11
}

Wird, wie gehabt, Receive aufgerufen, so leitet Receive diesen Aufruf 
weiter. Natürlich nur, wenn es überhaupt einen Proxy gibt:
1
void MyClass::Receive( char Daten )
2
{
3
  ....
4
5
  if( pProxy )
6
    pProxy->CallIt( Daten );
7
}

Alles was noch fehlt, ist der konkrete Proxy, der einen CallIt Aufruf an 
ein MyClass Object weitergeben kann, indem er dessen Receive Funktion 
aufruft. Der ist aber schnell gemacht. Auch er bekommt wieder einen 
Pointer auf das Objekt, dessen Receive Methode zuständig ist (und der 
Non-Owning ist)
1
class MyClassProxy : public Proxy
2
{
3
  public:
4
    MyClassProxy( MyClass* pReceiver ) : pReceiver_( pReceiver ) {}
5
    void CallIt( char Daten )   { pRecevier->Receive( Daten ); }
6
}

Damit sind alle Zutaten zusammen und main kann geschrieben werden:
1
int main()
2
{
3
  MyClass MyObject1, MyObject2;
4
5
  MyClassProxy MyProxy( &MyObject2 );   // Der Proxy soll an MyObject2 weitergeben
6
  MyObject1.SetProxy( &MyProxy );       // Und MyObject1 soll den Call über
7
                                        // den Proxy MyProxy weitergeben
8
9
  MyObject1.Receive( 'A' );             // MyObject1 benachrichtigt jetzt
10
                                        // mithilfe des Proxies das Obj MyObject2
11
                                        // das Daten eingetroffen sind.
12
}

Der Vorteil eines Proxies besteht darin, dass MyClass jetzt nicht mehr 
daran gebunden ist, den exakten Typ des aufzurufenden Objektes zu 
kennen. Diese Information steckt im Proxy selber und der kann an die 
jeweilige Situation angepasst werden. Haargenau das gleiche MyClass 
Objekt kann mit einem anderen Proxy dazu gebracht werden, eine Methode 
in einem MyOtherClass Object aufzurufen. Dazu braucht es lediglich einen 
dafür massgeschneiderten Proxy.

Der Nachteil ist natürlich, dass die Aufrufe über den Proxy ein wenig 
langsamer sind, weil ja eine Abstraktionsschicht zwischengeschoben 
wurde. Aber so dramatisch sind die Speed-Unterschiede dann auch wieder 
nicht.

von XTerminator (Gast)


Lesenswert?

Oliver schrieb:
> Wenn MyObject1 und MyObject2 wie in deinem Beispiel vom selben Typ sind,
> ist deren send-Funktion sowieso dieselbe (die gibt es nur einmal). Was
> soll dein Ansatz daher einen Sinn haben?

Aber die Objekte sind nicht dieselben! Er möchte offenbar die Objekte 
alös Kommunikationsendpunkte nutzen.

von Matze T. (gruetzwurschd)


Lesenswert?

Vielen dank für die Zahlreichen Antworten.

Ich muss mich da jetz mal ein bischen durchwühlen und alles "verdauen"

Vielen Dank leute! :)

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.