Forum: Mikrocontroller und Digitale Elektronik Abstrakte Klassen ohne Overhead nutzen (C++)


von FelixW (Gast)


Lesenswert?

Hallo,

Ich programmiere gerade mit eine LPCXPresso Board in C++.
Sobald ich eine abstrakte Klasse definiere bindet der Compiler einen 
ganzen Rattenschwanz von Stringverarbeitung ein, bis der Linker bei 
_write, _close, etc. scheitern muss.
1
class Led{
2
public:
3
    virtual ColorxyY const & getColor(void) const =0;
4
}


Hier mein aktueller Workaround, den ich gerne vermeiden möchte.
1
class Led{
2
private:
3
   Led(Led const &) {}
4
protected:  
5
   Led() {}
6
   ~Led() {}
7
public:
8
  virtual ColorxyY const & getColor(void) const  { while(1);} 
9
}

Bin für alle Hinweise dankbar.
Ich verwende NewLib, da C++ Projekt Redlib nicht unterstüzt.

Gruß
Felix

von Karl H. (kbuchegg)


Lesenswert?

FelixW schrieb:

> Sobald ich eine abstrakte Klasse definiere bindet der Compiler einen
> ganzen Rattenschwanz von Stringverarbeitung ein, bis der Linker bei
> _write, _close, etc. scheitern muss.

Ist nur geraten, aber einen Versuch wert.

Ich schätze mal das Problem liegt darin, dass der Compiler 
Funktionalität einbindet, die darin besteht für diese pure/virtual 
Funktion eine Default Implementierung zu generieren, die dich davon in 
Kenntnis setzt, dass eine pure/virtual Funktion unzulässigerweise 
aufgerufen wurde.

Verpass der Funktion mal eine Implementierung und sieh nach, ob dann der 
Rattenschwanz immer noch dazugezogen wird

>
1
> class Led{
2
> public:
3
>     virtual ColorxyY const & getColor(void) const =0;
4
> }

ColorxyY const & LED::getColor()
{
}

Ja, man kann durchaus auch für pure Funktionen eine Implementierung 
angeben!

von FelixW (Gast)


Lesenswert?

Mein Compiler lässt nicht eine abstrakte Definition und Implementierung 
in der selben Klasse zu.

Ich habe eine Lösung gefunden:
1
extern "C" void __cxa_pure_virtual() { while (1); }

Die Funktion __cxa_pure_virtual() ist in libstdc++ definiert und wird 
aufgerufen sobald ich eine abstrakte Methode aufrufe. Sie gibt die 
Meldung "pure virtual call" aus.
Die extra 60kB, sind vor allem malloc() geschuldet.

Mit obigen Aufruf wird jetzt gegen meine eigene Funktion gelinkt, keine 
Ahnung, ob das zu zulässig / günstig ist. Der Linker jedenfalls schluckt 
es. :)

Danke
Felix

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

FelixW schrieb:
> Die extra 60kB, sind vor allem malloc() geschuldet.

Das glaube ich nun kaum.  So umständlich und groß kann man ein
simples malloc() gar nicht implementieren.

Die von dir genannten externen Symbole, die dann referenziert werden,
deuten vielmehr darauf hin, dass standardmäßig da irgendwas
eingebaut wird, was dann per stdio (bzw. seinem C++-Pendant) bei
Bedarf eine Ausschrift auf stderr bringen kann. Finde heraus, wer
das ist und was er tun will, und du solltest in der Lage sein,
einen für dich besser passenden Ersatz zu schreiben.

(stdio seinerseits zieht natürlich noch malloc() nach für die
potenzielle Pufferung der Daten, aber das dürfte da kaum den
Löwenanteil am Overhead machen.)

von Karl H. (kbuchegg)


Lesenswert?

FelixW schrieb:
> Mein Compiler lässt nicht eine abstrakte Definition und Implementierung
> in der selben Klasse zu.

Eigenartig.
Das sollte eigentlich keinen Compiler vor große Probleme stellen.
Allerdings Achtung: Du kannst nicht (standardkonform) die 
Funktionsdefinition direkt in der Klassendeklaration angeben.

Das hier ist illegal
1
class Led{
2
 public:
3
   virtual ColorxyY const & getColor(void) const =0 {};
4
};

Du musst schon eine eigene Funktion dafür schreiben
1
class Led{
2
  public:
3
    virtual ColorxyY const & getColor(void) const =0;
4
};
5
 
6
ColorxyY const & LED::getColor() const
7
{
8
}

Falls du noch Interesse daran hast, poste mal eine Testprogramm welches 
bei dir nicht compiliert, die Fehlermeldung dazu. Vielleicht findet sich 
ja ein ganz anderes Problem, welches du missverstanden hast.
(Ich seh nämlich gerade, dass ich das Funktions-const in der 
Implementierung weiter oben vergessen habe. Aber das hätte eigentlich 
aus der Fehlermeldung ersichtlich sein sollen)

Denn wie gesagt: Wenn dein Compiler einigermassen standardkonform ist, 
dann muss das gehen. Es gibt ja auch keinen wirklich schwerwiegenden 
technischen Grund, warum das nicht möglich sein sollte.

von Rene H. (Gast)


Lesenswert?

Versuch mal mit einem Destruktor. Meine abstrakten Klassen mache ich 
immer mit:
1
class Led{
2
public:
3
    virtual ~Led() {} = 0;
4
    virtual ColorxyY const & getColor(void) const =0;
5
};

Grüsse,
R.

von Dr. Sommer (Gast)


Lesenswert?

FelixW schrieb:
> Ich habe eine Lösung gefunden:extern "C" void __cxa_pure_virtual() {
> while (1); }

Jörg Wunsch schrieb:
> Finde heraus, wer
> das ist und was er tun will, und du solltest in der Lage sein,
> einen für dich besser passenden Ersatz zu schreiben.

Der Overhead kommt vom Exception-Handling (Stack-Undwinding, 
Unwinding-Tables, Generierung der Fehlermeldung), welches wiederum 
malloc(), String-Verarbeitungen, und Konsolen-Output (_write) benötigt. 
Falls du tatsächlich die gesamte Fehlermeldung der Exception haben und 
ausgeben möchtest (über UART oder so) dann musst du _write und die 
anderen implementieren, ja. Falls du aber die Exception gar nicht haben 
möchtest, ist die Implementierung von __cxa_pure_virtual der richtige 
Ansatz, dann kannst du deine eigene Behandlung verwenden (wie eben 
while(1) aka Controller-Freeze; kannst ja noch ein __disable_irq () 
dazuschreiben).
In der Embedded-Entwicklung können 60kB mehr oder weniger den Ausschlag 
geben, weswegen es sinnvoll sein kann den riesigen Overhead durch das 
Exception-Handling zu vermeiden, anstelle von sie "richtig" zu verwenden 
und die Meldung irgendwo auszugeben.

von FelixW (Gast)


Lesenswert?

1
class Led{
2
  public:
3
    virtual ColorxyY const & getColor(void) const =0;
4
};
5
 
6
ColorxyY const & Led::getColor() const
7
{
8
}
compiliert :).
Man sollte
1
ColorxyY const & Led::getColor() const
2
{
3
}
nur nicht in den Header schreiben :/

@Jörg Wunsch:
Asche auf mein Haupt. Man sollte mit den Zeilen im map-File nicht 
durcheinander kommen.
"malloc() ist klein, sobald ich "new" im Code verwende bläht sich der 
Code auf.
Ich mache dafür aber ein neues Thema auf.

von Karl H. (kbuchegg)


Lesenswert?

FelixW schrieb:

> compiliert :).

Super.
Dann ist mein Weltbild wieder hergestellt.
Wobei ich persönlich ebenfalls die Variante mit dem 'überschreiben' der
1
extern "C" void __cxa_pure_virtual() { while (1); }
wählen würde.
Die Endlosschleife ist diskussionswürdig, da würde ich für mich 
irgendwas eindeutigeres wünschen um auch an der Schaltung erkennen zu 
können, das dieser Fall eingetreten ist. Aber grundsätzlich hätte ich 
keine Probleme mit so einer Lösung.


> Asche auf mein Haupt. Man sollte mit den Zeilen im map-File nicht
> durcheinander kommen.
> "malloc() ist klein, sobald ich "new" im Code verwende bläht sich der
> Code auf.

Einigen wir uns darauf: Wird 1 Systemfunktion inkludiert, dann zieht das 
einen Rattenschwanz an anderen Funktionen nach sich, die allesamt 
inkludiert werden müssen. Denn eines darf man nie vergessen: Oftmals ist 
die eigentliche Funktionalität eigentlich recht trivial, aber 
Fehlerbehandlung und vor allen Dingen Fehlerkommunikation mit dem 
Benutzer bläht Funktionen (und deren Folgefunktionen ... und deren 
Folgefunktionen) oftmals enorm auf.

von Oliver (Gast)


Lesenswert?

Dr. Sommer schrieb:
> FelixW schrieb:
> Falls du aber die Exception gar nicht haben
> möchtest, ist die Implementierung von __cxa_pure_virtual der richtige
> Ansatz,

In dem Fall ist die Compileroption -fno-exceptions das geeignete Mittel 
der Wahl.

Oliver

von Dr. Sommer (Gast)


Lesenswert?

Oliver schrieb:
> In dem Fall ist die Compileroption -fno-exceptions das geeignete Mittel
> der Wahl.
Ja, aber nur wenn die Exceptions im eigenen Code geworfen werden; auf 
den bereits vorkompilierten Code der libstdc++ , welcher normalerweise 
eben die __cxa_pure_virtual enthält, hat das keinen Einfluss, somit 
würde bei verwendung virtueller Funktionen der ganze Rattenschwanz 
trotz -fno-exceptions eingebunden. Was eber nicht heißt dass man 
-fno-exceptions nicht benutzen soll/muss...

von Dr. Sommer (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Die Endlosschleife ist diskussionswürdig
Die ist wunderbar, da kann man im Debugger unterbrechen und sieht sofort 
woran es hapert.

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.