Forum: PC-Programmierung C++ member Methode als Callback-Funktion übergeben


von JoJo (Gast)


Lesenswert?

Hallo zusammen,

ich versuche  eine Klasse zuschreiben in der sich eine Callback Funktion 
befindet. Ich möchte sie als nicht-statischen deklariert,weil ich in der 
Callback-Funktion  die anderen member Funktion und die andern Membern 
Werte zugreifen möchte. meine Frage Ob es möglich ist, denn die meisten 
beispiele und mein Programm kriege ich zum laufen nur wenn die 
Callbackfunktion als globale Funktion definiere oder als statische 
Funktion.

Hier ist mein Code:

myclass.hpp
1
//void * callBackFunction(void *arg) ; //callback als globale Funktion
2
class myClass
3
{
4
5
  public:
6
    myClass();
7
    void                              flaeche();
8
    void                              creatTreads();
9
    void*                             callBackFunction(void *arg);
10
    //static void*  callBackFunction(void *arg);
11
    vtkSmartPointer                   <vtkMultiThreader> threads;
12
  private:
13
       double  breite ;
14
       double  laenge;
15
 };

myclass.cpp
1
myClass::myClass(){
2
 
3
   this->laenge = 0.0;
4
   this->breite = 0.0;
5
     
6
}
7
8
void* myClass::callBackFunction(void *arg ){
9
 
10
    // explicitly cast to a pointer
11
    int id          = *((int *)arg);        // process ID
12
    myClass *mySelf = (myClass*) arg;
13
    mySelf->flaeche();
14
15
   std::cout<<"thread Function: process :prozess.id="<<id<<std::endl;
16
}
17
void myClass::flaeche(){
18
 
19
  double sr =this->laenge * this->breite;
20
 cout<<"flaeche():sr ="<<sr<< "lange="<<this->laenge<<"breite ="<<this->breit<<endl;
21
22
}
23
24
void myClass::creatTreads(){
25
 
26
   this->laenge =10.5;
27
   this->breite = 10.5;
28
 
29
  threads = vtkSmartPointer<vtkMultiThreader>::New();
30
   cout << "Initialization DONE" << endl;
31
   int thID = 0;
32
   thID = threads->SpawnThread((vtkThreadFunctionType)(*callBackFunction), &thID);
33
34
}
35
int main(int argc, char *argv[]) {
36
 
37
     myClass C;
38
     C.creatTreads();
Allerdings erhalte ich dann einen Fehler:
1
:104:57: error: invalid use of member function (did you forget the ‘()’ ?)

Wie könnte man diese Problem umgehen? Die CallBackfunktion als static 
deklarieren aber wie kann man auf den aktullen Object zugreifen oder 
gibts auch andere Lösungen?

Danke schön mal im Voraus
Jojo

von Karl H. (kbuchegg)


Lesenswert?

Man kann das schon machen, allerdings ist das in C++ wenig hilfreich.
Du kannst den Funktionspointer nicht so machen

    void*                             callBackFunction(void *arg);

Denn der Funktionspointer soll ja nicht auf eine freistehende Funktion 
zeigen, sondern auf eine Memberfunktion. Eine Memberfunktion gehört aber 
zu einer bestimmten Klasse, daher muss in der Deklaration des 
Funtkionspointers auch der Name der Klasse auftauchen.

http://www.parashift.com/c++-faq/pointers-to-members.html


2-tens hilft dir der Funktionspointer nicht viel. Denn du brauchst ja 
auch immer ein Objekt, für das eine Funktion aufgerufen werden soll. Ein 
Funktionspointer alleine speichert diese Information aber nicht.

D.h. Member-Callbackpointer sind in C++ meistens nicht das was man will. 
Will man unbedingt mit Funktionspointern arbeiten, dann geht man 
meistens über eine freistehende Funktion (also keine Memberfunktion) und 
aus dieser Funktion heraus ruft man dann die Memberfunktion für ein 
spezifisches Objekt auf.

Oft aber ist der ganze Ansatz mit Callbackpointer schon der falsche Weg. 
Was man statt dessen macht, ist eine Basisklasse mit einer virtuellen 
Funktion, die vom Caller aufgerufen wird. Die eigentliche Callbackklasse 
leitet sich dann von dieser Klasse ab und implementiert ihrerseits dann 
diese virtuelle Funktion. Der Caller bekommt dann einen Pointer auf so 
ein Objekt und ruft mit diesem Objekt-Pointer die Funktion auf - der 
'virtual' Mechanismus sorgt dann dafür, dass die richtige Funktion 
aufgerufen wird.
Viele Problemstellungen löst man in C++ dadurch, dass man in der 
Klassenhierarchie eine Zwischenklasse einzieht, die einfach nur als 
gemeinsamer Ankerpunkt dient um dort eine virtuelle Funktion in die 
Hierarchie einzubringen, von der man dann weiter arbeiten kann.

von PittyJ (Gast)


Lesenswert?

Ich kenne das so, dass immer eine statische Function verwendet wird.
Diese bekommt als ersten Parameter den this-Pointer mit, und kann dann 
damit die eigentliche Memberfunktion aufrufen.
1
class ServerThread {
2
    // callback entry points
3
    static void * ServerCB(void *pthis, void *p_Data) ;
4
    void* ServerCallback(ServerControllerData *p_Data);
5
};
6
7
//=========================================================
8
// statische
9
void * ServerThread::ServerCB(void *pthis, void *p_Data)
10
{
11
  ServerThread * pc_This;
12
  pc_This = (ServerThread *) pthis;
13
14
  // an die member-function weiterleiten
15
  return pc_This->ServerCallback((ServerControllerData *)p_Data);
16
} // ServerCB()
17
18
// Member Callback
19
void* ServerThread::ServerCallback(ServerControllerData *p_Data)
20
{
21
}

von JoJo (Gast)


Lesenswert?

PittyJ schrieb:

>   // an die member-function weiterleiten
>   return pc_This->ServerCallback((ServerControllerData *)p_Data);
> } // ServerCB()
>
> // Member Callback
> void* ServerThread::ServerCallback(ServerControllerData *p_Data)
> {
> }
Hallo  Pitty,
Danke Sehr für dein Tipps,
wo ist ServerControllerData deklariert ?

von Fux (Gast)


Lesenswert?

JoJo schrieb:
> Danke Sehr für dein Tipps,
> wo ist ServerControllerData deklariert ?

Das ist doch nur ein beliebiger Parameter als Beispiel.

von Klemens W. (klemens)


Lesenswert?

Hallo!

Schau dir mal boost.bind & boost.function an:
http://www.boost.org/doc/libs/1_54_0/libs/bind/bind.html#with_boost_function

lg

von Ronny S. (duselbaer)


Lesenswert?

Callbacks auf Member-Funktionen sind an sich kein Problem. Dazu kannst 
Du Dir std::mem_fn und boost::function anschauen.

- http://en.cppreference.com/w/cpp/utility/functional/mem_fn
- http://www.boost.org/doc/libs/1_54_0/doc/html/function.html

Boost.Function benutze ich ab und an mal. Deine Member-Funktion hat wie 
schon beschrieben als ersten Parameter den this-Pointer auf Dein Objekt. 
Mit Boost.Bind kannst Du da schon einen Parameter anknüpfen und damit 
aus deinem Funktionspointer auf die Member-Funktion und einem 
this-Pointer einen zu Deinem Callback-Typen kompatiblen Funktionspointer 
machen.

Jetzt musst Du "nur" noch darauf achten, dass dein Objekt am Leben 
bleibt, so lange wie der FUnktionspointer benutzt werden könnte. Das 
kann man bspw. mit smart-Pointern lösen, die man statt this bindet.

Die Möglichkeiten sind interessant und vielfältig - kann aber auch jede 
Menge kaputtgehen dabei und die Fehlermeldungen des Compilers passen 
meisst nicht auf eine DIN-A4 Seite - auch, wenn nur ein Zeichen falsch 
ist :)

Rooney

von JoJo (Gast)


Lesenswert?

Hallo Buchegger,

Danke sehr für deine Tipps, ich habe deine erste vorschlag eben probiert 
und

Karl Heinz Buchegger schrieb:
> 2-tens hilft dir der Funktionspointer nicht viel. Denn du brauchst ja
> auch immer ein Objekt, für das eine Funktion aufgerufen werden soll. Ein
> Funktionspointer alleine speichert diese Information aber nicht.
>
> D.h. Member-Callbackpointer sind in C++ meistens nicht das was man will.
> Will man unbedingt mit Funktionspointern arbeiten, dann geht man
> meistens über eine freistehende Funktion (also keine Memberfunktion) und
> aus dieser Funktion heraus ruft man dann die Memberfunktion für ein
> spezifisches Objekt auf.

und es läuft ohne Problem:
1
void callBackFunctionHelper(void *arg);
2
class myClass
3
{
4
5
  public:
6
7
    myClass();
8
    void          flaeche();
9
    vtkSmartPointer<vtkMultiThreader> threads;
10
    void          creatTreads();
11
    void         callBackFunction(void *arg);
12
    private:
13
       double  breite ;
14
       double  laenge;
15
    
16
  
17
};
18
19
myClass::myClass(){
20
 
21
   this->laenge = 0.0;
22
   this->breite = 0.0;
23
   cout<<"Konstruktur:"<<endl;   
24
}
25
26
void myClass::callBackFunction(void *arg ){
27
28
     int id          = *((int *)arg);        // process ID
29
     this->flaeche();
30
31
   // boost::bind(&myClass::flaeche, &mySelf);
32
    
33
    std::cout<<"thread Function: process : gl ="<<gl<<"prozess.id="<<id<<std::endl;
34
  
35
}
36
void myClass::flaeche(){
37
 
38
  double sr =this->laenge * this->breite;
39
    
40
  cout<<"flaeche():sr ="<<sr<< "lange="<<this->laenge<<"breite ="<<this->breite<<endl;
41
42
}
43
44
void myClass::creatTreads(){
45
  
46
 //  this->laenge =10.5;
47
  // this->breite = 10.5;
48
   
49
   vtkSmartPointer<vtkMultiThreader> threads =  vtkSmartPointer<vtkMultiThreader>::New();
50
   cout << "Initialization DONE" << endl;
51
   int thID = 0;
52
   thID = threads->SpawnThread((vtkThreadFunctionType)(callBackFunctionHelper),&thID);
53
  sleep(1);
54
 
55
56
}
57
 
58
myClass C;
59
void callBackFunctionHelper(void *arg){  // standalone Funktion 
60
61
      C.callBackFunction(arg);  
62
}
aber wie du schön erwähnt hast,ist schlechte variante, da der Instanz 
global sein muss.ich bin dabei zweite variante zu probieren

von old man (Gast)


Lesenswert?

Ich empfehle dir anzusehen was z.B. die mbed-Leute gemacht haben. Da 
gibts eine Klasse FunktionPointer. Da kann sowohl eine statisch Funtion 
als auch eine Memberfunktion einer Klasse die nicht! statisch ist als 
Handler gesetzt werden. Der Code ist mittlerweile OpenSource:

http://mbed.org/users/mbed_official/code/mbed-src/file/4263a77256ae/api/FunctionPointer.h
http://mbed.org/users/mbed_official/code/mbed-src/file/4263a77256ae/common/FunctionPointer.cpp

von Karl H. (kbuchegg)


Lesenswert?

JoJo schrieb:

> aber wie du schön erwähnt hast,ist schlechte variante, da der Instanz
> global sein muss.

'muss' nicht.
Schau dir mal das von PittyJ weiter oben gepostete Beispiel an. Da wird 
der Pointer zum Objekt ebenfalls gespeichert und der Funktion übergeben. 
Sinnvollerweise wird man rund um das Gespann 
Objektpointer/Funktionspointer eine Klasse legen und diesem 
Spezialpointer den Aufruf überlassen. ....

> .ich bin dabei zweite variante zu probieren

... womit man dann mehr oder weniger genau hier landet.

Edit: Und 'old man' zeigt dann auch, wies geht.

von Dr. Sommer (Gast)


Lesenswert?

Seit C++11 brauchts keine externen Libraries mehr für sowas:
1
#include <functional>
2
#include <iostream>
3
4
class X {
5
  public:
6
    X (int y) : m_y (y) {}
7
    void fun (int x) {
8
      std::cout << m_y << " " << x << std::endl;
9
    }
10
    
11
    int m_y;
12
};
13
14
int main () {
15
  X x (7);
16
  // Alternative 1
17
  std::function<void(int)> memPtr1 = std::bind(&X::fun, x, std::placeholders::_1);
18
  // Alternative 2
19
  std::function<void(int)> memPtr2 = [&](int a) { x.fun (a); };
20
  
21
  memPtr1 (3);
22
  memPtr1 (2);
23
}

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.