mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik C++ Funktionszeiger von static-Methode aus auf nicht-static-Methode einer Klasse


Autor: Alexander I. (daedalus)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

ich muss gerade ein Stückchen Code in KEIL für ARM7 importieren... in 
einem PowerPC-Compiler funktioniert das nachfolgende Konstrukt 
einwandfrei:
class IoPort : public List<IoPort>
{
public:
  static void InitAll(void) { ForEach(Init); } // Fehler!
  virtual void Init(void) = 0;
};

Beim KEIL erhalte ich den Fehler:
#504-D: nonstandard form for taking the address of a member function

Das kommt auch, wenn ich die InitAll "ausschreibe" mit
static void InitAll(void) { ForEach((void (IoPort::*)(void))Init); }

Erklärung der Funktion:
Im System sind lauter von IoPort abgeleitete Klassen instanziert, die 
z.B. einen UART oder andere Hardware abbilden. Alle IoPorts sind durch 
Ableitung von der Templateklasse List in einer verketteten Liste 
verknüpft, die auch die static-Funktion ForEach implementiert. Dadurch 
wird einmal in der main()-Schleife ein
IoPort::InitAll();
aufgerufen, was zur Folge hat, dass die verkettete Liste von IoPorts 
abgearbeitet wird und jede IoPort-Instanz einen "Init"-Aufruf erhält und 
z.B. Pins als Ein- oder Ausgang setzt.

- Wieso spuckt KEIL hier einen Fehler aus, der PowerPC-Compiler aber 
nicht?
- Was bedeutet der Fehler überhaupt?
- Wie sieht eine Alternative aus?

Autor: Klaus Wachtler (mfgkw)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Alexander I. schrieb:
> in
> einem PowerPC-Compiler funktioniert das nachfolgende Konstrukt
> einwandfrei:

> falsch: glaube ich nicht.
> falsch: Wie soll der Compiler dnen wissen, für welches Objekt jeder
> falsch: Init()-Aufruf gemacht werden soll?

Nachtrag:
Sorry, hatte überlesen, daß IoPort von List abgeleitet ist.
Da muß ich nochmal nachdenken.

Von welcher List eigentlich?

Autor: klaus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Klaus Wachtler schrieb:
> Wie soll der Compiler dnen wissen, für welches Objekt jeder
>
> Init()-Aufruf gemacht werden soll?

Habe ich auch erst gedacht. Die vermute die Idee ist, da die 
Basis-Klasse als Template definiert ist, die ForEach Funktion über alle 
enthaltenen Elemente e iterieren zu lassen und dann einfach "e.Init()" 
aufzurufen. Was ich nicht verstehe wie man den Parameter "Init" im 
Template dafür heranziehen kann.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wie sieht das List Template aus. Und wie dessen ForEach Member?

Autor: Alexander I. (daedalus)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Durch Instanzierung von IoPort-Klassen wird jeweils automatisch ein 
neues Element in eine (globale) List aller IoPorts eingetragen.

mit
static void InitAll(void) {for(IoPort* p = First; p; p = p->Next){p->Init();}}

klappt es... da hab ich quasi die ForEach-Funktion neu geschrieben und 
so die Funktionspointerproblematik umgangen. Aber so an sich hätte mich 
der Sachverhalt schon interessiert...

Autor: Alexander I. (daedalus)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
template<class T>
class List
{
public:
  static T* First;
  static T* Last;
  T* Next;
public:
  List()
  {
    T* node = (T*)this;
    if(!First) First = node;          // Dies ist das erste Element
    if(Last) {Last->List::Next = node;}     // Verkette das letzte Element mit Diesem
    Last = node;                      // Dieses ist nun das letzte Element
    Next = 0;                         // Es gibt noch kein nächstes Element
  }
  static void ForEach(void (T::*func)(void))
  {
    for(T* node = First; node; node = node->Next)
    {
      (node->*func)();                // Rufe die zu iterierende Funktion auf
    }
  }
};

Autor: Klaus Wachtler (mfgkw)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Und: welches ForEach?
Sowohl dieses als auch die List sind nicht aus std::?

Abgesehen davon drohe ich gerade mental in eine Endlosschleife zu
kommen, weil IoPort abgeleitet wird von einer Liste (IoPort ist
also eine ganze Liste, nicht ein Port). Diese Liste enthält
wiederum lauter Elemente vom Typ IoPort, das jeweils eine
Liste von....

Ohne es probiert zu haben, weiß ich erstens nicht, ob du
das wirklich so willst, und zweitens ob je ein C++-Compiler
das wirklich übersetzen mag.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
klaus schrieb:

> aufzurufen. Was ich nicht verstehe wie man den Parameter "Init" im
> Template dafür heranziehen kann.

Ich denke da kommen wir der Sache dann schon näher.
Vermutung: ForEach übernimmt einen Funktionspointer.

Probier mal:

  static void InitAll(void) { ForEach(IoPort::Init); }

Eventuell
  static void InitAll(void) { ForEach(&IoPort::Init); }

obwohl das IMHO nicht nötig sein sollte.

Autor: Klaus Wachtler (mfgkw)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Mal so von Klaus zu klaus:
Danke, ist mir danach erst aufgefallen. :-)

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Klaus Wachtler schrieb:

> Abgesehen davon drohe ich gerade mental in eine Endlosschleife zu
> kommen, weil IoPort abgeleitet wird von einer Liste (IoPort ist
> also eine ganze Liste, nicht ein Port). Diese Liste enthält
> wiederum lauter Elemente vom Typ IoPort, das jeweils eine
> Liste von....

Yep. Das stinkt nach einem Designfehler.

Ein einzelner Port ist nunmal keine Liste von Ports.
Ein PortPool könnte eine Liste von Ports sein, oder ein Manager könnte 
so eine Liste von Ports verwalten. Aber ein Port ist ein Port.

Ein einzelnes Auto ist ja schliesslich auch keine Menge von Autos. Ein 
Parkplatz könnte eine Menge von Autos 'verwalten'. Aber ein Parkplatz 
ist sicherlich kein Auto und hat auch nichts damit zu tun (wird also 
nicht von Auto abegeleitet)

Solche Designfehler funktionieren interessanterweise manchmal sogar 
anfänglich. Aber je weiter das Programm sich entwickelt, desto 
unlogischer wird es dann oft und desto mehr Probleme tauchen auf.
Es ist besser, gleich von vorne herein die Klassenstruktur richtig zu 
haben.

Autor: Klaus Wachtler (mfgkw)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Das stinkt nicht nur, es kann gar nicht gehen.
Ein Typ kann doch nicht eine Liste von Elementen seines
Typ enthalten.
Ich mag nicht glauben, daß das so kompiliert wird, egal
wieviel Power dein PowerPC hat.

Autor: Arc Net (arc)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Karl heinz Buchegger schrieb:
> klaus schrieb:
>
>> aufzurufen. Was ich nicht verstehe wie man den Parameter "Init" im
>> Template dafür heranziehen kann.
>
> Ich denke da kommen wir der Sache dann schon näher.
> Vermutung: ForEach übernimmt einen Funktionspointer.
>
> Probier mal:
>
>   static void InitAll(void) { ForEach(IoPort::Init); }
>
> Eventuell
>   static void InitAll(void) { ForEach(&IoPort::Init); }
>
> obwohl das IMHO nicht nötig sein sollte.

VC10 meint ohne IoPort:: "function call missing argument list; use 
'&IoPort::Init' to create a pointer to member"
Hilft aber auch nicht weiter solange eine statische Methode eine 
Instanzmethode aufrufen soll.

Autor: klaus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Nach langem Nachgrübeln kann ich mir kaum vorstellen, dass der gezeigte 
Code Standard konform ist, weil:

Zwar ist die Basisklasse ein Template und wird mit der Ableitung 
instanziiert aber trotz allem bleibt InitAll() static und versucht die 
Adresse einer nicht statischen Member-Funktion Init() zu nehmen, die 
außerdem noch virtuell ist. Wo soll da die Objektreferenz herkommen?

Autor: klaus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Das ganze ist schon deswegen Quark, weil in der 2. Vorlesung zu 
Objektorientierter Programmierung einem jeder Professor erklären wird, 
dass Ableitungen "ist-Beziehungen" modellieren sollen. Angewandt auf das 
vorliegende Beispiel: "Ist ein IOPort eine verkettete Liste?"

Autor: Klaus Wachtler (mfgkw)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
oder noch genauer:
"Ist ein IOPort eine verkettete Liste von IoPort-Objekten?"

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Klaus Wachtler schrieb:
> Das stinkt nicht nur, es kann gar nicht gehen.
> Ein Typ kann doch nicht eine Liste von Elementen seines
> Typ enthalten.
> Ich mag nicht glauben, daß das so kompiliert wird, egal
> wieviel Power dein PowerPC hat.

Doch, das wird compilieren.
Im List Template werden keine Objekte gespeichert, sondern nur Pointer.

Nichts desto trotz ist das IMHO ein Designfehler.

Und auch der static calls non-static Teil klappt, weil der Listenstart 
First sinnigerweise ebenfalls ein static Member ist.

Oh Mann, da ist so ziemlich alles drinn enthalten, was einem den 
Angstschweiß auf die Stirn treiben wird.

Eine Klasse IoPort, dazu eine verwaltende Managerklasse (ausgeführt als 
Singleton). Wäre wohl zu einfach gewesen :-)

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Was is'n nun.
Ist der Keil mit der &IoPort::Init Syntax zufrieden?

Autor: Klaus Wachtler (mfgkw)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Karl heinz Buchegger schrieb:
> Doch, das wird compilieren.
> Im List Template werden keine Objekte gespeichert, sondern nur Pointer.

Nein:
class IoPort : public List<IoPort>
...
Da werden keine Zeiger gespeichert, sondern Kopien der
Objekte, mit denen jedes Element initialisiert wird.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Klaus Wachtler schrieb:
> Karl heinz Buchegger schrieb:
>> Doch, das wird compilieren.
>> Im List Template werden keine Objekte gespeichert, sondern nur Pointer.
>
> Nein:
>
> class IoPort : public List<IoPort>
> ...
> 
> Da werden keine Zeiger gespeichert, sondern Kopien der
> Objekte, mit denen jedes Element initialisiert wird.

Wo genau?

template<class T>
class List
{
public:
  static T* First;
  static T* Last;
  T* Next;


Ich würde mal sagen: Ein IoPort Objekt besteht aus einem Zeiger auf ein 
weiteres IoPort Objekt (Next). Die beiden static zählen erst mal nicht.
Bis hier hin ist alles legal und in Ordnung.

Da ist im Grunde nichts anderes als eine Spielart von

struct Data
{
  ....
  struct Data* next;
};

nur halt im OOP Gewand.

Autor: Alexander I. (daedalus)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Blubb :) Ihr legt ja los! :-)

Also:
Der Code ist so schon seit Jahren exakt so im Einsatz, wurde NICHT von 
mir entwickelt und funktioniert tadellos ohne Warnings usw. Damit werden 
etwa 100-150 IoPorts verwaltet, je nach Systemarchitektur.

Ich finde dieses Konstrukt allerdings ebenfalls höchst seltsam, ich habe 
es in meiner kurzen Karriere bisher noch nicht gesehen. Mich wundert es 
auch, dass der Funktionszeiger auf Instanz->Init() ohne Muh akzeptiert 
wird.
static void InitAll(void) { ForEach(IoPort::Init); }
static void InitAll(void) { ForEach(&IoPort::Init); }
Der Fehler bleibt weiterhin:
#504-D: nonstandard form for taking the address of a member function

Vielleicht muß ich noch dazu sagen, dass die Instanzen ALLE statisch 
(also kein new/delete) und global sind, es gibt keine new/delete-Orgien. 
Da würde es wohl ziemlich mächtig knallen.

Das das ganze ein Designfehler ist, sehe ich auch so, speziell mit der 
"ist ein"-Argumentation.

Autor: Alexander I. (daedalus)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Achso: Mit std::List hat diese Implementierung höchstens den Namen 
gemeinsam.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Alexander I. schrieb:

>
> static void InitAll(void) { ForEach(IoPort::Init); }
> static void InitAll(void) { ForEach(&IoPort::Init); }
> 
> Der Fehler bleibt weiterhin:
> #504-D: nonstandard form for taking the address of a member function

Eigenartig.
Jetzt weiß ich auch nicht mehr weiter.

Spätestens jetzt würd ich den ForEach wegwerfen und die Schleife selbst 
aufdröseln.
  static void InitAll()
  {
    for( T* node = First; node; node = node->Next )
    {
      node->Init();
    }
  }

(Das void in der Argumentliste kannst du dir sparen. Braucht kein 
Mensch)

Das hätte auch den Vorteil, dass leichter zu durchschauen ist, was da 
eigentlich abgeht.

Autor: Klaus Wachtler (mfgkw)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Karl heinz Buchegger schrieb:
> Klaus Wachtler schrieb:
>> Karl heinz Buchegger schrieb:
>>> Doch, das wird compilieren.
>>> Im List Template werden keine Objekte gespeichert, sondern nur Pointer.
>>
>> Nein:
>>
>> class IoPort : public List<IoPort>
>> ...
>> 
>> Da werden keine Zeiger gespeichert, sondern Kopien der
>> Objekte, mit denen jedes Element initialisiert wird.
>
> Wo genau?

Sorry, ich hatte eine richtige Liste im Kopf, nicht dieses
kranke Gerät hier.
Was wiederum die Frage aufwirft, warum es List heißt,
wenn es nur Zeiger auf anderweitig gespeicherte Objekte
verwaltet (falls wir hier die ganze Liste sehen).

> ...

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Karl heinz Buchegger schrieb:
> Alexander I. schrieb:
>
>>
>> static void InitAll(void) { ForEach(IoPort::Init); }
>> static void InitAll(void) { ForEach(&IoPort::Init); }
>> 
>> Der Fehler bleibt weiterhin:
>> #504-D: nonstandard form for taking the address of a member function
>
> Eigenartig.
> Jetzt weiß ich auch nicht mehr weiter.

Einen hab ich noch
(Klaus, bitte wegschauen)
  static void InitAll(void) { ForEach( ((IoPort*)0)->Init ); }

  static void InitAll(void) { ForEach( &(((IoPort*)0)->Init) ); }


Autor: Alexander I. (daedalus)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Die Vergangenheit dieses ganzen "Konstrukts" kenne ich leider nicht. 
Fakt ist, dass es kurioserweise funktioniert. Wenn man das ForEach() weg 
lässt ist die Sache ja geritzt. Aber ich überleg mir das morgen nochmal, 
ob ich wirklich ein "IoPort ist ein IoPort ist ein IoPort ..." haben 
will, oder das anders lösen werde. Danke euch soweit!

Autor: Alexander I. (daedalus)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
static void InitAll(void) { ForEach( &(((IoPort*)0)->Init) ); }
error:  #300: a pointer to a bound function may only be used to call the 
function

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Klaus Wachtler schrieb:

> Was wiederum die Frage aufwirft, warum es List heißt,
> wenn es nur Zeiger auf anderweitig gespeicherte Objekte
> verwaltet (falls wir hier die ganze Liste sehen).

Nicht darüber nachdenken.
Die virtuelle abstrakte Funktion Init weist mich darauf hin, das da 
höchst wahrscheinlich auch noch von IoPort abgelitten wird.

Kein virtueller Destruktor, kein Copy Konstruktor, kein Assignment 
Operator. Da ist jemand brandheiss unterwegs :-)

Aber: Abgesehen von dem logischen Designfehler - man kann das so machen.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Alexander I. schrieb:
>
> static void InitAll(void) { ForEach( &(((IoPort*)0)->Init) ); }
> 
> error:  #300: a pointer to a bound function may only be used to call the
> function

OK
Keinen blassen Dunst.
Meiner Ansicht nach ist die &IoPort::Init Syntax korrekt und kein Grund 
für den Compiler zum meckern.

Autor: klaus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wollte man das Template testen. Verrücktes Stück Code. Ich bekomme es im 
GCC 3.4.2 (ja schon älter ich weiß) nur zum Laufen wenn ich die static 
Variablen explizit anlege:
template<class T> T* List<T>::First = 0;
template<class T> T* List<T>::Last = 0;

Ansonsten meckert der Linker über "undefined references"...

Autor: Klaus Wachtler (mfgkw)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Klar, die müssen definiert sein.
Ist immer so bei static-Elementen einer Klasse.

Autor: Klaus Wachtler (mfgkw)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
g++ 4.3.2 kompiliert dieses übrigens auch anstandslos mit -Wall
und -Wextra:
template<class T>
class List
{
public:
  static T* First;
  static T* Last;
  T* Next;
public:
  List()
  {
    T* node = (T*)this;
    if(!First) First = node;          // Dies ist das erste Element
    if(Last) {Last->List::Next = node;}     // Verkette das letzte Element mit Diesem
    Last = node;                      // Dieses ist nun das letzte Element
    Next = 0;                         // Es gibt noch kein nächstes Element
  }
  static void ForEach(void (T::*func)())
  {
    for(T* node = First; node; node = node->Next)
    {
      (node->*func)();                // Rufe die zu iterierende Funktion auf
    }
  }
};


class IoPort : public List<IoPort>
{
public:
  static void InitAll(void) { ForEach(&IoPort::Init); } // Fehler!
  virtual void Init()
  {
  }
};

template<class T> T* List<T>::First = 0;
template<class T> T* List<T>::Last = 0;


int main()
{
  IoPort  io;
}

Autor: Klaus Wachtler (mfgkw)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@khb: ohne das & vor der Funktion geht es übrigens nicht.
(Ich hätte gewettet, daß es keinen Unterschied macht, aber
jetzt natürlich nicht mehr :-)

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Klaus Wachtler schrieb:
> @khb: ohne das & vor der Funktion geht es übrigens nicht.
> (Ich hätte gewettet, daß es keinen Unterschied macht, aber
> jetzt natürlich nicht mehr :-)

Die Wette hätten wir beide verloren :-)

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.