Forum: Mikrocontroller und Digitale Elektronik C++ Function Pointer non-static Member


von Pascal H. (_pascal_)


Lesenswert?

Hi,

nachdem lwip nun laeuft wollte ich ein kleines programm schreiben.
Leider scheitert es daran in meiner EchoClient Class einen Function 
Pointer an lwip zu uebergeben...
1
#ifndef ECHOCLIENT_H_
2
#define ECHOCLIENT_H_
3
4
#include "lwip/opt.h"
5
#include "lwip/debug.h"
6
#include "lwip/stats.h"
7
#include "lwip/tcp.h"
8
#include "functional"
9
10
class EchoClient {
11
12
public:
13
  EchoClient();
14
  void init();
15
  virtual ~EchoClient();
16
17
  err_t accept(void *arg, struct tcp_pcb *newpcb, err_t err);
18
19
private:
20
  tcp_pcb *m_pcb;
21
22
};
23
24
#endif /* ECHOCLIENT_H_ */
25
26
#include <EchoClient.h>
27
28
EchoClient::EchoClient() :
29
  m_pcb(0)
30
{
31
32
}
33
34
void EchoClient::init() {
35
36
  m_pcb = tcp_new();
37
  if (m_pcb != NULL) {
38
39
    err_t err;
40
41
    err = tcp_bind(m_pcb, IP_ADDR_ANY, 7);
42
    if (err == ERR_OK) {
43
44
      auto test = std::mem_fn(&EchoClient::accept);
45
46
      m_pcb = tcp_listen(m_pcb);
47
      tcp_accept(m_pcb, test);
48
49
    }
50
51
  }
52
53
}
54
55
err_t EchoClient::accept(void *arg, struct tcp_pcb *newpcb, err_t err) {
56
57
  return ERR_OK;
58
59
}
60
61
EchoClient::~EchoClient() {
62
  // TODO Auto-generated destructor stub
63
}

Fehlermeldung:
../EchoClient.cpp:29:26: error: cannot convert 'std::_Mem_fn<signed char 
(EchoClient::*)(void*, tcp_pcb*, signed char)>' to 'tcp_accept_fn {aka 
signed char (*)(void*, tcp_pcb*, signed char)}' for argument '2' to 
'void tcp_accept(tcp_pcb*, tcp_accept_fn)'

von Frank (Gast)


Lesenswert?

Möchte man auf einem uC wirklich member function pointer oder reicht 
auch der Umweg über eine statische stub function?

http://www.codeproject.com/Articles/7150/Member-Function-Pointers-and-the-Fastest-Possible

von Rolf Magnus (Gast)


Lesenswert?

Pascal H. schrieb:
> nachdem lwip nun laeuft wollte ich ein kleines programm schreiben.
> Leider scheitert es daran in meiner EchoClient Class einen Function
> Pointer an lwip zu uebergeben...

std::mem_fun gibt aber keinen Funktionszeiger zurück. Auf eine 
nicht-statische Memberfunktion kann man gar keine C-Funktionszeiger 
erzeugen. Wie auch? Es fehlt ja das Objekt.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Oft bieten C-Libraries, die mit Funktionspointern arbeiten, die 
Möglichkeit, einen opaken Wert durchzureichen, d.h. diesen beim 
Bekanntgeben des Funktionspointer mit anzugeben, und dieser Wert wird 
beim Aufrufen der Funktion als ein Argument übergeben.

Damit kann man den this -Pointer transportieren.

Der Funktionspointer verweist auf eine statische Memberfunktion, die 
wiederum erzeugt aus dem opaken Wert den fehlenden this-Pointer und kann 
dann die nichtstatische Memberfunktion aufrufen.

Dabei geht natürlich einiges an Typsicherheit & Co. verloren, weil bei 
der Operation "erzeuge this-Pointer aus opakem Wert" darauf vertraut 
werden muss, daß dieser Wert tatsächlich die Adresse eines passenden 
Objektes ist, aber wenn man diese ... Unschönheit verschmerzen kann, 
lässt sich auf diesem Weg recht problemlos eine Schnittstelle zwischen 
C-Funktionspointergebrauch und C++ nutzen.

Möchte man auf Nummer Sicher gehen, kann man sich natürlich auch eine 
Klasse zur Verwaltung von im Zusammenhang mit Funktionspointern 
verwendeten this-Pointern basteln, und die Pointer durch Nummern 
referenzieren, die dann als opaker Wert genutzt werden. Das ist halt 
mehr Aufwand als die oben beschriebene Methode, aber dafür auch 
sicherer, weil wenn Müll anstelle des gewünschten opaken Werts ankommt, 
dieser klar erkannt werden kann.

von Pascal H. (_pascal_)


Lesenswert?

Vielen Dank fuer die Anworten.

Ich fand die Idee recht schoen alles was den EchoClient angeht in dieser 
Klasse zu kapseln. Klar koennte ich einfach eine globale accept function 
bereitstellen aber das ist immer alles so verwoben dann.

Ich werde mir die Idee mit dem uebergeben eines this pointers in einer 
wrapper Klasse mal ansehen.

von Rolf Magnus (Gast)


Lesenswert?

Rufus Τ. F. schrieb:
> Der Funktionspointer verweist auf eine statische Memberfunktion, die
> wiederum erzeugt aus dem opaken Wert den fehlenden this-Pointer und kann
> dann die nichtstatische Memberfunktion aufrufen.

Die Funktion muss für den Aufruf von C aus als extern "C" deklariert 
sein, was für statische Memberfunktionen nicht möglich ist. Man braucht 
eine freie Funktion.

von Pascal H. (_pascal_)


Lesenswert?

Ich habe mir jetzt mal das hier gebastelt

Auch wenn es mir nicht gefaellt mit der statischen Funktion.

Wenn ich das umschiffen wollen wuerde, muesste ich das lwip struct 
welches die callback functions haelt erweitern und den this pointer dort 
mitschleifen oder?
1
#include <EchoClient.h>
2
3
extern "C" {
4
5
std::map<tcp_pcb*, EchoClient*> acceptMap;
6
err_t globalAccept(void *arg, struct tcp_pcb *newpcb, err_t err) {
7
8
  std::map<tcp_pcb*, EchoClient*>::iterator it = acceptMap.find(newpcb);
9
  if (it != acceptMap.end()) {
10
    return it->second->accept(arg, newpcb, err);
11
  }
12
13
  return ERR_ARG;
14
15
}
16
17
}
18
19
EchoClient::EchoClient() :
20
    m_pcb(0) {
21
}
22
23
void EchoClient::init() {
24
25
  m_pcb = tcp_new();
26
  if (m_pcb != NULL) {
27
28
    err_t err;
29
30
    err = tcp_bind(m_pcb, IP_ADDR_ANY, 7);
31
    if (err == ERR_OK) {
32
33
      m_pcb = tcp_listen(m_pcb);
34
      acceptMap.insert(std::pair<tcp_pcb*, EchoClient*>(m_pcb, this));
35
36
      tcp_accept(m_pcb, globalAccept);
37
38
    }
39
40
  }
41
42
}
43
44
err_t EchoClient::accept(void *arg, struct tcp_pcb *newpcb, err_t err) {
45
46
  return ERR_OK;
47
48
}
49
50
EchoClient::~EchoClient() {
51
  // TODO Auto-generated destructor stub
52
}

: Bearbeitet durch User
von Pascal H. (_pascal_)


Lesenswert?

Also ich hab das Programm jetzt soweit erweitert, dass ich mit telnet in 
die globalAccept function komme. Nur leider stimmt der newpcb pointer 
mit dem in der map nicht ueberein :(

Keine Ahnung was lwip da intern noch umkopiert, somit ist meine referenz 
futsch. Habt ihr eine Idee?
1
#include <EchoClient.h>
2
3
extern "C" {
4
5
static std::map<struct tcp_pcb*, EchoClient*> acceptMap;
6
err_t globalAccept(void *arg, struct tcp_pcb *newpcb, err_t err) {
7
8
  std::map<struct tcp_pcb*, EchoClient*>::iterator it;
9
  for (it = acceptMap.begin(); it != acceptMap.end(); it++) {
10
11
    struct tcp_pcb *target = newpcb;
12
    struct tcp_pcb *test = it->first;
13
14
    if(target == test) {
15
      return it->second->accept(arg, newpcb, err);
16
    }
17
18
  }
19
20
  return ERR_ARG;
21
22
}
23
24
}
25
26
EchoClient::EchoClient() :
27
    m_pcb(0) {
28
}
29
30
void EchoClient::init() {
31
32
  m_pcb = tcp_new();
33
  if (m_pcb != NULL) {
34
35
    err_t err;
36
37
    err = tcp_bind(m_pcb, IP_ADDR_ANY, 7);
38
    if (err == ERR_OK) {
39
40
      m_pcb = tcp_listen(m_pcb);
41
      tcp_accept(m_pcb, globalAccept);
42
43
      acceptMap.insert(std::pair<struct tcp_pcb*, EchoClient*>(m_pcb, this));
44
45
    }
46
47
  }
48
49
}
50
51
err_t EchoClient::accept(void *arg, struct tcp_pcb *newpcb, err_t err) {
52
53
  err_t ret_err;
54
  struct echo_state *es;
55
56
  LWIP_UNUSED_ARG(arg);
57
  LWIP_UNUSED_ARG(err);
58
59
  /* Unless this pcb should have NORMAL priority, set its priority now.
60
   When running out of pcbs, low priority pcbs can be aborted to create
61
   new pcbs of higher priority. */
62
  tcp_setprio(newpcb, TCP_PRIO_MIN);
63
64
  es = (struct echo_state *) mem_malloc(sizeof(struct echo_state));
65
  if (es != NULL) {
66
67
    es->state = ES_ACCEPTED;
68
    es->pcb = newpcb;
69
    es->retries = 0;
70
    es->p = NULL;
71
72
    /* pass newly allocated es to our callbacks */
73
    tcp_arg(newpcb, es);
74
    //tcp_recv(newpcb, echo_recv);
75
    //tcp_err(newpcb, echo_error);
76
    //tcp_poll(newpcb, echo_poll, 0);
77
78
    ret_err = ERR_OK;
79
80
  } else {
81
    ret_err = ERR_MEM;
82
  }
83
84
  return ret_err;
85
86
}
87
88
EchoClient::~EchoClient() {
89
  // TODO Auto-generated destructor stub
90
}

von Rolf Magnus (Gast)


Lesenswert?

Pascal H. schrieb:
> Wenn ich das umschiffen wollen wuerde, muesste ich das lwip struct
> welches die callback functions haelt erweitern und den this pointer dort
> mitschleifen oder?

Und du müßtest lwip so umbauen, dass es eine C++-Memberfunktion (unter 
Nutzung dieses Zeigers) aufzurufen statt eine C-Funktion.

Generell: Warum so umständlich mit einer Map? Nimm doch einfach arg für 
den this-Zeiger. Für genau solche Sachen ist das doch da.
1
extern "C" err_t globalAccept(void *arg, struct tcp_pcb *newpcb, err_t err) {
2
    static_cast<EchoClient*>(arg)->accept(newpcb, err);
3
}
4
5
//...
6
7
void EchoClient::init() {
8
9
  m_pcb = tcp_new();
10
  if (m_pcb != NULL) {
11
12
    err_t err;
13
14
    err = tcp_bind(m_pcb, IP_ADDR_ANY, 7);
15
    if (err == ERR_OK) {
16
17
      m_pcb = tcp_listen(m_pcb);
18
      tcp_arg(m_pcb, this);
19
      tcp_accept(m_pcb, globalAccept);
20
    }
21
  }


Übrigens: Bist du dir sicher, dass m_pcb = tcp_listen(m_pcb) eine gute 
Idee ist? So wie ich das verstehe, überschreibst du dir damit den 
einzigen Zeiger, den du auf deine mit tcp_new() erstellte pcb hast.

von Daniel A. (daniel-a)


Lesenswert?

Rolf Magnus schrieb:
> Die Funktion muss für den Aufruf von C aus als extern "C" deklariert
> sein, was für statische Memberfunktionen nicht möglich ist. Man braucht
> eine freie Funktion.

Das stimmt in diesem fall nicht. Extern c deaktiviert nur das name 
mangeling und ist für den Linker relevant wenn aus einer c 
compilationsunit ein Symbol einer C++ unit aufgelöst werden soll. Da das 
Referenzieren und somit die Namensauflösung in einer C++ unit 
statfindet,  ist das name mangeling kein Problem und das extern c somit 
überflüssig und das verwenden von statischen Memberfunktionen möglich. 
Die C unit bekommt ja nur einen pointer, und wie die funktion auf welche 
dieser zeigt hiess ist der reichlich egal.

: Bearbeitet durch User
von Rolf Magnus (Gast)


Lesenswert?

Daniel A. schrieb:
> Extern c deaktiviert nur das name mangeling und ist für den Linker relevant
> wenn aus einer c compilationsunit ein Symbol einer C++ unit aufgelöst
> werden soll.

Nein. Es kümmert sich auch noch um weitere Dinge, wie die 
Aufrufkonventionen, also z.B. wie Parameter übergeben werden - eben 
alles, was für den korrekten Aufruf einer Funktion zu beachten ist.
Es funktioniert zwar bei vielen Compilern tortzdem ohne, da die 
Aufrufkonventionen in C und in C++ oft gleich sind, aber das muss nicht 
so sein.
Aus ISO C++98 (hab nix neueres, aber da wird das nicht anders sein):

All function types, function names, and variable names have a language 
linkage. [Note: Some of the properties associated with an entity with 
language linkage are specific to each implementation and are not
described here. For example, a particular language linkage may be 
associated with a particular form of representing names of objects and 
functions with external linkage, or with a particular calling 
convention, etc.] The default language linkage of all function types, 
function names, and variable names is C++ language linkage. Two function 
types with different language linkages are distinct types even if they 
are otherwise
identical.

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.