Forum: PC-Programmierung this Pointer in C++ verändert sich ungewollt


von A. R. (redegle)


Lesenswert?

Hallo,

ich habe in C++ eine Klasse erstellt. Diese verfügt über mehrere 
Methoden.
Rufe ich eine Methode der Klasse auf (von der main oder von innerhalb 
der Klasse), dann verfügt diese über alle Attribute der Klasse.

Nun verwende ich innerhalb der Klasse die Bibliothek curl.
Dieser muss ich einen Pointer auf eine "Callbackfunktion" übergeben.
Sprich ich konfiguriere die Bibliothek und sobald ich die Bibliothek 
ausführe werden zu bestimmten Zeitpunkten immer wieder Daten an die 
Callbackfunktion gesendet.

Nun möchte ich, dass die Callbackfunktion teil meiner Klasse ist.
Also habe ich den Pointer auf die Callbackmethode wie folgt übergeben.
1
curl_easy_setopt(curl_handle_chunked, CURLOPT_WRITEFUNCTION, 
2
&Klasse::Callbackmethode);

Die Methode wird auch erfolgreich aufgerufen jedoch hat die 
Callbackfunktionen einen anderen Pointer auf this. Kann sich das jemand 
erklären?

von Klaus T. (gauchi)


Lesenswert?

der this pointer wird normalerweise als erster (unsichtbarer) Parameter 
übergeben. Die Callback-Funktion hat aber den Prototyp
1
size_t function( char *ptr, size_t size, size_t nmemb, void *userdata);

d.h. wenn du eine Klassenmethode da übergibst, bekommt die als this den 
Parameter ptr (was auch immer der tut)

Funktionieren sollte es, wenn du einen Funktionseinzeiler als Glue-code 
verwendest:
1
size_t glue(char *ptr, size_t size, size_t nmemb, void* userdata){
2
  return ((*Klasse)userdata)->Callbackmethode(ptr, size, nmemb);
3
}

und diese Funktion an die Bibliothek übergibst

Edit:

du musst natürlich noch dafür sorgen, dass userdata dann auf dein Objekt 
zeigt, wie das geht steht sicher irgendwo in der curl Dokumentation

von Martin (Gast)


Lesenswert?

Ist deine Methode denn wenigstens static?

von Rolf Magnus (Gast)


Lesenswert?

A. R. schrieb:
> Nun möchte ich, dass die Callbackfunktion teil meiner Klasse ist.
> Also habe ich den Pointer auf die Callbackmethode wie folgt übergeben.
> curl_easy_setopt(curl_handle_chunked, CURLOPT_WRITEFUNCTION,
> &Klasse::Callbackmethode);
>
> Die Methode wird auch erfolgreich aufgerufen jedoch hat die
> Callbackfunktionen einen anderen Pointer auf this. Kann sich das jemand
> erklären?

Anders als was? Woher soll curl denn wissen, für welches Objekt die 
Funktion aufgerufen werden soll?

Klaus T. schrieb:
> der this pointer wird normalerweise als erster (unsichtbarer) Parameter
> übergeben. Die Callback-Funktion hat aber den Prototyp
> size_t function( char *ptr, size_t size, size_t nmemb, void *userdata);
>
> d.h. wenn du eine Klassenmethode da übergibst, bekommt die als this den
> Parameter ptr (was auch immer der tut)

Eigentlich dürfte das aber gar nicht erst durch den Compiler gehen, weil 
Zeiger auf  Memberfunktionen und Zeiger auf normale Funktionen nicht 
kompatibel sind.

> Funktionieren sollte es, wenn du einen Funktionseinzeiler als Glue-code
> verwendest:
> size_t glue(char *ptr, size_t size, size_t nmemb, void* userdata){
>   return ((*Klasse)userdata)->Callbackmethode(ptr, size, nmemb);
> }

Abgesehen von der falschen Position des Sternchen sind C-Style-Casts in 
C++ "pfui". So wäre es schöner:
1
   return static_cast<Klasse*>(userdata)->Callbackmethode(ptr, size, nmemb);

von A. R. (redegle)


Lesenswert?

>Anders als was? Woher soll curl denn wissen, für welches Objekt die
>Funktion aufgerufen werden soll?

Ich glaube du hast recht. Bzw. ich glaube nun zu Wissen wo das 
eigendlich Problem ist.

Ich erstelle eine Instanz (Objekt) einer Klasse.
Innerhalb dieser Instanz wird wird curl aufgerufen.
Die Memberfunktion, welche ich curl übergebe soll auch zu dieser Instanz 
gehöhren. Also die Memberfunktion, welche curl aufruft soll Zugriff auf 
alle Attribute der Instanz haben.

Die Frage müsste also sein, wie erstelle ich einen Pointer auf eine 
Memberfunktion eines Objektes. Richtig?

Auf der Homepage von curl steht folgender Text:

........................
libcurl with C++

There's basically only one thing to keep in mind when using C++ instead 
of C when interfacing libcurl:

The callbacks CANNOT be non-static class member functions

Example C++ code:

class AClass {   static size_t write_data(void *ptr, size_t size, size_t 
nmemb,   void *ourpointer)   {   /* do what you want with the data */ 
}  }
.......................

Ist mein Vorhaben also überhaupt möglich?
1
size_t function( char *ptr, size_t size, size_t nmemb, void *userdata);

Also eigendlich ist *ptr ein Pointer auf die Daten (Text),welche die 
Bibliothek empfängt. Die einzigen Daten die ich an die Callbackfunktion 
übergeben kann sind Daten in void *userdata.

von Läubi .. (laeubi) Benutzerseite


Lesenswert?

A. R. schrieb:
> Ist mein Vorhaben also überhaupt möglich?

jein ... Ich vermute mal das Userdata ein Pointer auf ein beliebiges 
Datenobjekt ist was man dem Aufruf mitgibt?

Dann kannst du eine statische dispatch Methode machen welche als 
Userdata den Pointer auf die Klasse mitbekommt und auf diesem dann die 
Callbackfunktion aufruft.

von Rolf Magnus (Gast)


Lesenswert?

A. R. schrieb:
> Die Frage müsste also sein, wie erstelle ich einen Pointer auf eine
> Memberfunktion eines Objektes. Richtig?

Die Frage könnte man stellen, aber die Antwort lautet  "gar nicht". 
Einem Pointer auf eine Memberfunktion muß man immer beim Aufruf das 
Objekt übergeben. Der  Pointer enthält nicht selbst noch die Adresse des 
Objekts sondern nur die der  Memberfunktion. Daher geht das also nicht 
so, wie du es dir vorstellst.

> Auf der Homepage von curl steht folgender Text:
>
> ........................
> libcurl with C++
>
> There's basically only one thing to keep in mind when using C++ instead
> of C when interfacing libcurl:
>
> The callbacks CANNOT be non-static class member functions

Richtig. Streng genommen dürfen es gar keine Memberfunktionen sein, auch 
keine statischen, aber die funktionieren in der Praxis üblicherweise 
trotzdem.

> Ist mein Vorhaben also überhaupt möglich?
> size_t function( char *ptr, size_t size, size_t nmemb, void *userdata);
> Also eigendlich ist *ptr ein Pointer auf die Daten (Text),welche die
> Bibliothek empfängt. Die einzigen Daten die ich an die Callbackfunktion
> übergeben kann sind Daten in void *userdata.

Genau. Und da übergibst du einen Zeiger auf dein Objekt. Das Prinzip ist 
wie von Klaus T. beschrieben, und das ist auch das gleiche, was Läubi 
meint.
Du definierst eine nonmember-Funktion, die du als Callback-Funktion für 
curl verwendest. Die bekommt als userdata einen Zeiger auf dein Objekt, 
konvertiert diesen in den passenden Typ und ruft damit dann die 
Memberfunktion auf. Die Syntax, die ich oben beschrieben habe, sollte so 
funtkionieren.
Was oben noch vergessen wurde:  Die Funktion sollte der Vollständigkeit 
halber noch als extern "C" definiert sein, da sie ja von einem C-API aus 
aufgerufen werden soll. Also insgesamt ungefähr sowas:
1
extern "C"
2
size_t glue(char *ptr, size_t size, size_t nmemb, void* userdata)
3
{
4
    return static_cast<Klasse*>(userdata)->Callbackmethode(ptr, size, nmemb);
5
}

Und dann mußt du eben noch mit CURLOPT_WRITEDATA den Zeiger auf dein 
Objekt an curl übergeben, damit es diesen dann nacher als userdata an 
deine Funktion weitergibt.

von Läubi .. (laeubi) Benutzerseite


Lesenswert?

Bei der Gelegenheit könnte man den char* und die Größe auch noch gleich 
in einen std::string wandeln ;)

Gibt es eigentlich keinen fertigen C++ Wrapper für curl?

von Rolf Magnus (Gast)


Lesenswert?

Du meinst sowas wie unter http://curlpp.org/ ? Nö, bestimmt nicht ;-)

von Läubi .. (laeubi) Benutzerseite


Lesenswert?

Rolf Magnus schrieb:
> Nö, bestimmt nicht ;-)

Hatte ich schon fast vermutet ;-)

von A. R. (redegle)


Lesenswert?

Vielen vielen Dank für die Hilfe.

Denke, dass ich alles verstanden habe. Ich habe aber noch eine Frage.
Wie bekomme ich am geschicktesten innerhalb einer Methode einen Pointer 
auf das Objekt der Klasse?

Ich könnte mir eine Settermethode erstellen und somit aus der Main einen 
Pointer an meine Klasse übergeben. Aber kann man auch innerhalb einer 
Memberfunktion überprüfen, für welches Objekt sie aufgerufen worden ist?

von Klaus W. (mfgkw)


Lesenswert?

this

von A. R. (redegle)


Lesenswert?

Danke

von A. R. (redegle)


Lesenswert?

Klappt alles.
1
curl_easy_setopt(curl_handle_chunked, CURLOPT_WRITEFUNCTION, &Callback_glue);//curl kann nur auf normale Funktionen zugreifen
2
//Callback_glue greift auf die Memberfunktion des jeweiligen Objekts zu
3
curl_easy_setopt(curl_handle_chunked, CURLOPT_WRITEDATA, this);//Pointer auf das Objekt (Instanz) der Klasse
4
curl_easy_perform(curl_handle_chunked);//Anfrage ausführen
1
size_t Callback_glue(void *buffer, size_t size, size_t nmemb, void *data)
2
{
3
//Funktion wird von Curl aufgerufen und ruft dann die Callbackfunktion des jeweiligen Objektes auf.
4
//data enthält den this-Pointer, der auch die notwendigen Variablen und Pointer enthält
5
return static_cast<Verbindung*>(data)->WriteMemoryCallback_chunked(buffer,size,nmemb,data);
6
//return, da Anzahl der Verarbeiteten Bytes zurückgegeben werden muss
7
}

Was genau macht das extern "C"?
Wenn ich das einfüge meckert mein Compiler.

von Daniel (root) (Gast)


Lesenswert?

es erzwingt die Deklaration der Funktion nach C Regeln.
Google mal nach "name mangling" und "ABI" application binary
interface.

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.