Hallo,
ich muss gerade ein Stückchen Code in KEIL für ARM7 importieren... in
einem PowerPC-Compiler funktioniert das nachfolgende Konstrukt
einwandfrei:
1
classIoPort:publicList<IoPort>
2
{
3
public:
4
staticvoidInitAll(void){ForEach(Init);}// Fehler!
5
virtualvoidInit(void)=0;
6
};
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
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
1
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?
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?
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.
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...
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.
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.
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.
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.
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.
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?
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?"
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 :-)
Klaus Wachtler schrieb:
> Karl heinz Buchegger schrieb:>> Doch, das wird compilieren.>> Im List Template werden keine Objekte gespeichert, sondern nur Pointer.>> Nein:>
1
>classIoPort:publicList<IoPort>
2
>...
3
>
> 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.
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.
1
staticvoidInitAll(void){ForEach(IoPort::Init);}
2
staticvoidInitAll(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.
> 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.
1
staticvoidInitAll()
2
{
3
for(T*node=First;node;node=node->Next)
4
{
5
node->Init();
6
}
7
}
(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.
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:>>
1
>>classIoPort:publicList<IoPort>
2
>>...
3
>>
>> 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).
> ...
>> 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)
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!
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.
> 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.
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:
1
template<class T> T* List<T>::First = 0;
2
template<class T> T* List<T>::Last = 0;
Ansonsten meckert der Linker über "undefined references"...
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 :-)