mikrocontroller.net

Forum: Compiler & IDEs mspgcc C++ rein virtuelle Methoden


Autor: Maddin (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo.
Kann ich den Kompiler irgendwie überzeugen rein virtuelle Methoden zu 
zulassen, die erst in einer Unterklasse deklariert werden?

also: virtual void test() = 0;
Der Linker meckert nämlich: " undefined reference to 
`__cxa_pure_virtual' "

Gruß.

Autor: Oliver (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wieso willst du den Compiler überzeugen, wenn doch der Linker meckert?

Wenn der so meckert, fehlt irgendwo die nicht-virtuelle Methode in einer 
Unterklasse. Virtuell linken kann der Linker nicht.

Oliver

Autor: MBtechler (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Das liegt daran, daß es für avr-g++ keine libsupc++ gibt. Versuche mal, 
diese Funktion als leere Funktion mit extern "C" zu definieren.

Autor: Maddin (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@MBtechler: Danke für die Antwort, ich habe mir schon so etwas gedacht.
Als extern "C" wär eine Möglichkeit, löst aber mein Problem 
wahrscheinlich nicht, da ich die virtuell deklarierte Methode in der 
Oberklasse benutze, und zwei verschiedene Klassen mit unterschiedlichen 
Implementierungnen der virtuellen Methoden existieren. Eventuell kann 
ich das Problem aber mit einem Funktionszeiger umgehen.

Autor: mork (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>löst aber mein Problem
>wahrscheinlich nicht, da ich die virtuell deklarierte Methode in der
>Oberklasse benutze, und zwei verschiedene Klassen mit unterschiedlichen
>Implementierungnen der virtuellen Methoden existieren. Eventuell kann
>ich das Problem aber mit einem Funktionszeiger umgehen.

Wer hat Dir denn gesagt, dass Du keine virtuellen Methoden nutzen 
kannst? '__cxa_pure_virtual' ist die Funktion, die angesprungen wird, 
wenn duch einen Programmfehler eine rein virtulle ausgeführt wird. Da es 
aber normaleweise nciht der Fall ist, sollte es keine Probleme geben, 
wenn man diese einfach als extern "C" void __cxa_pure_virtual(){} 
definiert

MfG Mark

Autor: Maddin (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Achso, jetzt wird es mir etwas klarer. Ich schau mir die Sache morgen 
nochmal in Ruhe an, hab jetzt keinen Zugriff auf den Code. (vielleicht 
hab ich wirklich einfach nur vergessen die virtuelle Methode in der 
Unterklasse zu implementieren; bin heute den ganzen Tag schon etwas 
verpeilt :)

Danke für die Hinweise!

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

Bewertung
0 lesenswert
nicht lesenswert
Und wenn das nicht geht, dann kannst du der pure-virtuell
Funktion immer noch eine Implementierung verpassen.

pure heist nicht, dass die Funktion keine Implementierung
haben kann!

Autor: Maddin (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hm, also irgendwie klappt das nicht. Ich habe folgende Oberklasse:
class Stringbuffer
{
public:  
  char buffer[BUFSIZE];
  int act_position;
  char * bufp;
  virtual void flush() = 0;
  Stringbuffer ( );
  void put (char c );
};
mit einer Implementierung von put():
void Stringbuffer::put (char c ) {
  if(bufp - buffer == (BUFSIZE - 1)) flush();
  *bufp++ = c;
}

und hier kommt eben das flush() vor, das erst in Unterklassen den Puffer 
unterschiedlich an unterschiedliche Ausgabegeräte leeren soll.

eine z.B. die RS232 Schnittstelle:
class RS232 : public Stringbuffer
{
public:
  RS232 ( );
  void flush ( );
  void print(char * text, int length);
};
void RS232::flush ( ) {
  print(buffer, bufp - buffer);
  bufp = buffer;
}

Mit extern "C" void __cxa_pure_virtual(){} kann ich zwar kompilieren, 
beim Aufruf von flush springt er aber ins Leere (call @r14 ; wobei r14 = 
0)

Mit einer leeren Implementierung von flush (virtual void flush(){};)
passiert dasselbe.

Vielleicht sollte ich doch lieber in C weitermachen...

Autor: Oliver (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
[c]if(bufp - buffer == (BUFSIZE - 1)) this->flush();8]

Oliver

Autor: Oliver (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
arrgh

Nochmal:
if(bufp - buffer == (BUFSIZE - 1)) this->flush();
Oliver

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

Bewertung
0 lesenswert
nicht lesenswert
Maddin wrote:

> Mit extern "C" void __cxa_pure_virtual(){} kann ich zwar kompilieren,
> beim Aufruf von flush springt er aber ins Leere (call @r14 ; wobei r14 =
> 0)
>

Schwer zu sagen, mit nur diesem Ausschnitt.
Du hast nicht zufällig irgendwo einen Fall von Slicing.

Slicing wäre, wenn du dein RS232 Objekt irgendwo irrtümlich
auf ein Stringbuffer Objekt zurück-castest. Also zb so was

void foo( Stringbuffer Output )
{
  ...
  Output.flush();
}

Der Fehler ist hier die vergessene Referenz in der Argumentliste

void foo( Stringbuffer& Output )
{
  ...
  Output.flush();
}

> Mit einer leeren Implementierung von flush (virtual void flush(){};)
> passiert dasselbe.

leere Implementierung in welcher Klasse? Stringbuffer oder RS232

>
> Vielleicht sollte ich doch lieber in C weitermachen...

Stell mal ein Minimalbeispiel nur mit diesen beiden Klassen
zusammen, damit man mal handfesten Code hat. Ich kann mir
nicht vorstellen, dass der Compiler/Linker virtuelle Funktionen
nicht richtig hinkriegst.

Was ist, wenn du die Funktion nicht pure machst?

Autor: Oliver (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Und nochmal:

Polymorphismus funktioniert nur beim Zugriff über Pointer.
void Stringbuffer::put (char c ) {
  if(bufp - buffer == (BUFSIZE - 1)) flush();
  *bufp++ = c;
}
geht nicht. Da wird die Methode flush von Stringbuffer aufgerufen, und 
die gibt es eben nicht, da virtuell.
void Stringbuffer::put (char c ) {
  if(bufp - buffer == (BUFSIZE - 1)) this->flush();
  *bufp++ = c;
}
hat den benötigten Pointer, und sollte funktionieren.

Oliver

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

Bewertung
0 lesenswert
nicht lesenswert
Oliver wrote:
> Und nochmal:
>
> Polymorphismus funktioniert nur beim Zugriff über Pointer.

oder Referenzen

>
>
void Stringbuffer::put (char c ) {
>   if(bufp - buffer == (BUFSIZE - 1)) flush();
>   *bufp++ = c;
> }
> geht nicht. Da wird die Methode flush von Stringbuffer aufgerufen, und
> die gibt es eben nicht, da virtuell.

Wo hast du denn den Unsinn her?

>
>
void Stringbuffer::put (char c ) {
>   if(bufp - buffer == (BUFSIZE - 1)) this->flush();
>   *bufp++ = c;
> }
> hat den benötigten Pointer, und sollte funktionieren.

Auch in der Originalversion ist der Pointer vorhanden.
    flush();
ist implizit für
    this->flush();

macht also keinen Unterschied.

Autor: Maddin (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Ich habe den Quellcode mal angehängt. Es sollen ein paar 
Ausgabefunktionen der IO Stream Bibliothek nachgebaut werden...
Die Vererbungshierarchie ist Stringbuffer <- O_Stream <- RS232

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

Bewertung
0 lesenswert
nicht lesenswert
OK.
Das Programm ist soweit in Ordnung.
Das der Compiler mit virtuellen Funktionen grundsätzlich
nicht klar kommt halte ich für eher unwahrscheinlich. Vielleicht
irgendein Compilerswitch?

Nochmal die Frage: Hast du schon mal versucht was passiert,
wenn du flush() nicht pure machst?

Oder so:
class Stringbuffer
{
public:  
  char buffer[BUFSIZE];
  int act_position;
  char * bufp;

  virtual void flush() = 0   { // Ooops, should never be called }
  Stringbuffer ( );
  void put (char c );
};
Du die Funktion zwar pure machst, aber trotzdem eine
Implementierung dafür angibst.

Autor: Maddin (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Meinst du so:
virtual void flush(){};
?
Das funktioniert auch nicht.

Autor: Maddin (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Achso Moment, im Header = 0 lassen, und zusätzlich eine leere 
Implementation?

Autor: Maddin (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Fehler beim linken:
undefined reference to `__cxa_pure_virtual'

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

Bewertung
0 lesenswert
nicht lesenswert
Maddin wrote:
> Meinst du so:
> virtual void flush(){};
> ?
> Das funktioniert auch nicht.

Schlecht.

OK. Zeit für ein minimalistisches Testprogramm.
Kannst du mal folgendes durch den Compiler jagen.
class a
{
  public:
    virtual void foo();
    void bar();
};

void a::foo()
{
   // mach was, zb eine LED ein
}

void a::bar()
{
  foo();
}

class b : public a
{
  public:
    virtual void foo();
};

void b::foo()
{
  // mach was anderes, zb eine andere LED
}

int main()
{
  b myB;

  myB.bar();
}

Im Endeffekt muss b::foo() aufgerufen werden.

Autor: Maddin (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hm, wenn ich das RS232 Objekt nicht global, sondern lokal in main() 
erzeuge, dann kann er auch das flush() aufrufen...

Autor: Maddin (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ja, mit lokal angelegtem Objekt funktioniert der Code wunderbar.

Wie bekomm ich nun das Objekt global zugänglich?
Lokal in main() anlegen, und globaler Zeiger darauf??

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

Bewertung
0 lesenswert
nicht lesenswert
Maddin wrote:
> Ja, mit lokal angelegtem Objekt funktioniert der Code wunderbar.

Mehr als seltsam.

>
> Wie bekomm ich nun das Objekt global zugänglich?
> Lokal in main() anlegen, und globaler Zeiger darauf??

Ist ne Möglichkeit.
Zwar nicht schön, aber der Zweck heiligt ... und so.

Autor: Oliver (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ok, das mit dem this war Blödsinn.

Aber dann noch eine dumme Frage:
Hat der msp-430-gcc eine vollständige libg++?

Etwas googlen findet:

>g++ v3.x generates references to a function __cxa_pure_virtual()
>whenever the code contains pure virtual functions. This is a
>placeholder used while constructing an object, and the function
>should never actually get called. There is a default
>implementation in libsupc++ ...


Egal, gibt man ihm im main.cpp mit
extern "C" void __cxa_pure_virtual(){}

das, was er anmeckert, linkt er zumindest ohne Fehler. Ob es dann auch 
läuft, kann ich nicht probieren.

Oliver

Autor: Rolf Magnus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Womit linkst du? Mit gcc oder mit g++? Es muß g++ sein.
Kann auch ein Compiler-Problem sein. Ich kenne mich mit dem mspgcc nicht 
so aus. Evtl wird der Startup-Code für den Aufruf der Konstruktoren bei 
globalen Variablen nicht generiert. So klingt es jetzt zumindest für 
mich.

Autor: Maddin (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich glaube das Problem liegt daran, dass die Konstruktoren von globalen 
Objekten im Startup Code nicht aufgerufen werden.

Autor: Maddin (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ups sorry den letzten Post bitte vergessen, hab den vorhin getippt, 
vergessen abzuschicken, und gerade auf Absenden geklickt...

Ja richtig ich linke mit dem msp430-gcc, da ich mit msp430-g++ das 
Problem habe, dass er die libstdc++ einbinden will, die es nicht gibt.
(msp430-ld: cannot find -lstdc++)
Gibt es da evtl ein g++ Flag, mit dem er nicht probiert diese Lib 
einzubinden.

Autor: Rolf Magnus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Ja richtig ich linke mit dem msp430-gcc, da ich mit msp430-g++ das
> Problem habe, dass er die libstdc++ einbinden will, die es nicht gibt.

Scheinbar ist der msp430-g++ noch schlechter gewartet als der für den 
avr. Dort wird die nicht existente libstdc++ und libsupc++ per default 
wenigstens nicht gelinkt.

> (msp430-ld: cannot find -lstdc++)
> Gibt es da evtl ein g++ Flag, mit dem er nicht probiert diese Lib
> einzubinden.

Es gibt -nostdlib, aber ich glaube, das beinhaltet auch die 
C-Standardlib. Du könntest versuchen, eine leere Datei mit dem Namen 
libstdc++.a im Verzeichnis deines Programms anzulegen und - falls noch 
nicht vorhanden - ein -L. an die Linker-Kommandozeile hängen.

Autor: Maddin (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Mit leerer libstdc++.a klappt das linken mit dem msp430-g++, aber 
offensichtlich wird der Konstruktor dennoch nicht ausgeführt.

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.