Forum: Mikrocontroller und Digitale Elektronik AVR Studio / GCC C - OOP - Anfänger Frage(n)


von Tim S. (Firma: tsx89) (freak_ts) Benutzerseite


Angehängte Dateien:

Lesenswert?

Hallo,
ich beschäftige mich nun schon eine ganze Weile mit der großen Welt der 
µCs insbesondere Pic und Atmel, - darunter habe ich verschiedene 
Compiler und IDEs angeschaud, natürlich hunderte "hello worlds" mit den 
einzelnen Funktionen der µCs  durchgetestet, etc... Bissher leider eher 
nur Basic / Bascom ähnliche Sachen, um einfache mini-Projecte / oder 
kleine Tools und Gadgets zu basteln.

Nun bleib ich entschlossen beim AVR / AtmelStudio und bei GCC C, hab 
auch dazu einige HelloWorld´s durch, und viele Tutorials gelesen. Was 
C++ allgemein angeht, steh ich noch seeehr weit am Anfang, komm aber 
recht gut vorwärts...

Hab nun ein paar allgemeine Fragen, zum OOP. Hab gelesen es gibt große 
Einschränkungen, inbesondere beim "Instanziieren" von Objekten zur 
Laufzeit. Also ich weiß, dass man aufpassen muss, wegen Ressourcen - man 
darf nicht zu viel verlangen... Zum teil wurde geschrieben, dass es 
überhaupt nicht möglich sei, z.B. eine Klasse innerhalb eines Namespaces 
zu schreiben, und dann darin (ganz normal) seine Funktionen 
reinzupacken, und diese Class bzw. das "Object" über die "Main" (ggf. 
auch mehrmals) zu instanziieren mit Constructer & Co für die Laufzeit, 
um anschließend deren Funktionen aufrufen zu können.

Ich hoffe es ist klar was ich meine, also ich wollte mich von diesen 
"statischen Code-zusammen-klatschen" fernhalten, und eben nun auch auf 
dem µC etwas "weiter gehen"... Zu dem Bild, der Dateiname sagt es schon, 
also Des_Is_Des_Was_Ich_Meine_Aber_In_VB -  wie wird das in GCC gelöst ?

Habe dazu leider noch kein vernünfiges Tutorial gefunden, was
OOP  Klassen  Objekte  Abstrakte Datentypen  Instance / Constructor 
/ Destructor, etc.. enthält, und deren Handhabung und Syntax etc.. 
beschreibt. Durch die vielen verschiedenen Aussagen der vielen 
verschiedenen Artikel die ich gelesen habe, bin ich nun etwas ratlos. 
Ich hätte mir gerne diverse "Standard-Sachen" in paar Klassen gepackt.
Habe schon einige dieser "Standard-Sachen" (uart, lcd, keys, etc) 
angeschaut, aber keine Klasse die ich bisher gesehen hatte wurde auf die 
beschriebene Art und Weise "instanziiert" sondern einfach nur 
"include´t", wenn ihr wisst was ich mein..

Desshalb wollte ich "ma eben" nachfragen, ob mir jemand einen 
Beispiel-Code geben kann, oder jemand ein (verständliches, kurzes und 
knappes) Tutorial kennt oder hat, was den OOP-Part mit AVR-GCC etwas 
erläutert? Im wesentlichen geht´s mir einfach um das im Bild 
beschriebene Beispiel.

Gruß

von Tim S. (Firma: tsx89) (freak_ts) Benutzerseite


Lesenswert?

Tim S. schrieb:
> Zum teil wurde geschrieben, dass es überhaupt nicht möglich sei,
 ...
> (ganz normal)
 ...
> OOP  Klassen  Objekte  Abstrakte Datentypen  Instance / Constructor
> / Destructor, etc..
 ...
> aufrufen zu können.


Tim S. schrieb:
> Ich hoffe es ist klar was ich meine
 ...
> kein vernünfiges Tutorial gefunden
 ...
> "Standard-Sachen"
 ...
> Desshalb wollte ich "ma eben" nachfragen, ob

es überhaupt Sinn macht, weiterhin danach zu suchen, bzw. ob es 
überhaupt möglich und machbar ist, eigene "Objekte" zu instanziieren. 
Das muss doch in paar Zeilen Code erklärbar sein, bevor ich unzählige 
Tut´s nach einem Punkt absuche, der eventuell gar nicht zu finden ist...

Gruß

von asdf (Gast)


Lesenswert?

Sie sollten auf jeden fall zuerst C und erst danach ++ lernen, ansonsten 
ist die chance zeugs zu verwechseln zu gross (malloc/free vs new/delete, 
references vs address-of operator etc).

Wegen dem OO
Da ist ein paper in dem das jemand komplett durchgebruetet hat:
http://www.planetpdf.com/codecuts/pdfs/ooc.pdf

Offensichtlich ist das meiste davon overkill aufm Mikrocontroller. Meist 
reicht es wenn man sein Zeugs einigermaßen sauber in structures verpackt 
und funktionen schreibt die damit was machen, aber prinzipiell spricht 
nichts dagegen, wenn man die structure mit function pointers 
vollpflastert, und so die methoden aufruft. Mit dem this pointer wird's 
vielleicht problematisch. Dyn Speicher Allokierung ist meist 
problematisch, also wird auf dem stack oder statisch/global instanziert.

von Maximilian (Gast)


Lesenswert?

asdf schrieb:
> Sie sollten auf jeden fall zuerst C und erst danach ++ lernen, ansonsten
> ist die chance zeugs zu verwechseln zu gross (malloc/free vs new/delete,
> references vs address-of operator etc).

Blödsinn!

C++ und C sind verschiedene Sprachen, die man einwandfrei unabhängig 
voneinander lernen kann. Und wer malloc/free nie gelernt hat (weil er 
gleich ordentliches C++ lernt), der verwechselt da auch nix.

von Stefan F. (sfrings)


Lesenswert?

Mikrocontroller Programme sind in der Regel klein genug, um auch ohne 
C++ Objekte übersichtlich zu bleiben.

C++ bringt einen gewaltigen Overhead mit sich, der mit dem kleinen RAM 
Speicher nicht gut zusammen passt.

Es ist nicht schwer, Strukturen und Funktionen auch ohne OOP ausreichend 
eindeutige Namen zu geben. Und wenn dann doch mal ein Konflikt auftritt, 
benennst Du sie eben einfach um. SO macht man das normalerweise.

von Oliver (Gast)


Lesenswert?

Stefan Frings schrieb:
> C++ bringt einen gewaltigen Overhead mit sich, der mit dem kleinen RAM
> Speicher nicht gut zusammen passt.

Immer wieder behauptet, und trotzdem falsch. Identische Funktionalität 
per C oder C++ (objektorientiert) implementiert, ergibt am Ende so gut 
wie gleich großen Code.

avr-gcc kommt halt ohne C++-Standardbibliothek, damit fehlen da alle 
Bibliotheksfunktionen und -operatoren, und über sowas wie std::vector 
sollte man auf einem AVR eh erst gar nicht nachdenken.

new/delete fehlen zwar auch, lässt sich einfach selber implementieren, 
wenn man sie den unbedingt benötigt (was nicht sein muß), und dann geht 
eigentlich fast alles, was C++ erlaubt.

Oliver

von Timo R. (t_rocks)


Lesenswert?

Mein Vorschlag lern erstmal C.
Damit kommste beim Mikrocontroller programmieren deutlich weiter als mit 
C++.
Solltest dir irgendwann C nicht mehr ausreichen ist es von C nach C++ 
auch nicht mehr weit.
In C++ sind halt im groben die ganzen OOP Konzepte verpackt worden, 
strenggenommen könntest du selbst in C objektorientiert programmieren, 
aber das ist ja hier nicht die Frage.
Falls es doch unbedingt C++ sein muss, benutz google und du findest 
tausende gute Tuts.

Gruß
Timo

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Oliver schrieb:
> Stefan Frings schrieb:
>> C++ bringt einen gewaltigen Overhead mit sich, der mit dem kleinen RAM
>> Speicher nicht gut zusammen passt.
>
> Immer wieder behauptet, und trotzdem falsch. Identische Funktionalität
> per C oder C++ (objektorientiert) implementiert, ergibt am Ende so gut
> wie gleich großen Code.

Du meinst also, wenn eine Funktionalität wie "LED Blinkern" oder was 
auch immer implementiert wird, dann ergibt sich mit C und C++ immer die 
gleiche Codegüte, unabhängig davon jeweiligen Softwaredesign?

Wohl kaum.

von Marwin (Gast)


Lesenswert?

Johann L. schrieb:
> Du meinst also, wenn eine Funktionalität wie "LED Blinkern" oder was
> auch immer implementiert wird, dann ergibt sich mit C und C++ immer die
> gleiche Codegüte, unabhängig davon jeweiligen Softwaredesign?

Nein und das hat er auch nie behauptet. Der Unterschied kommt dann aber 
vom Design, nicht von der verwendeten Sprache.

Die schlechtere Sprache zu waehlen, obwohl man in der besseren Alles 
machen kann, was auch in der schlechteren geht, weil man Angst hat, man 
koennte sich zu einem Design verleiten lassen, was ein wenig mehr 
Speicher oder Rechenzeit verbraucht, ist einfach nur peinlich.

Der einzige Haken an C++ fuer Mikrocontroller ist, dass die 
Standardliteratur ein Bild von C++ vermittelt, das so auf 
Mikrocontrollern praktisch nicht anwendbar ist. Dafuer kann aber die 
Sprache auch wieder nichts.

von Marwin (Gast)


Lesenswert?

Tim S. schrieb:
> Das muss doch in paar Zeilen Code erklärbar sein, bevor ich unzählige
> Tut´s nach einem Punkt absuche, der eventuell gar nicht zu finden ist...

Wenn dir so schwer faellt, die Frage zu formulieren, bist du einfach 
nicht erfahren genug. Da bringt dir eine scheinbar einfache Antwort auch 
nichts. Sammle also erst mal etwas Erfahrung mit C++ und mit 
Mikrocontrollern, dann wirst du schnell merken, dass die Verwendung von 
C++ auf Mikrocontrollern ganz einfach ist - und du wirst auch selbst 
merken, weswegen sich viele Konzepte vom Desktop nicht auf den 
Mikrocontroller uebertragen lassen.

von Karl H. (kbuchegg)


Lesenswert?

Johann L. schrieb:
> Oliver schrieb:
>> Stefan Frings schrieb:
>>> C++ bringt einen gewaltigen Overhead mit sich, der mit dem kleinen RAM
>>> Speicher nicht gut zusammen passt.
>>
>> Immer wieder behauptet, und trotzdem falsch. Identische Funktionalität
>> per C oder C++ (objektorientiert) implementiert, ergibt am Ende so gut
>> wie gleich großen Code.
>
> Du meinst also, wenn eine Funktionalität wie "LED Blinkern" oder was
> auch immer implementiert wird, dann ergibt sich mit C und C++ immer die
> gleiche Codegüte, unabhängig davon jeweiligen Softwaredesign?

Ich denke was Oliver meint ist, dass es bei 'C versus C++' Vergleichen 
oft so ist, dass nicht identische Funktionalität verglichen wird. Ein 
C++ welches mit std::string arbeitet hat nun mal als EXE eine meistens 
größere Signatur, als ein C Programm, welches die STringverarbeitung mit 
der heißen Nadel zusammenstrickt. Ergänzt man im C Programm die 
Features, die man durch std::string automatisch bekommt, dann sind beide 
wieder gleich auf. Oft sind die C++ Programme sogar kleiner. Selbiges 
mit std::vector und selbstgeschriebenen dynamischen Array-Klassen, 
respektive den anderen Containerklassen. Benötige ich Polymorphismus, 
dann kostet mir das in C++ nun mal ein bischen was. Aber es kostet mir 
auch in C etwas, wenn man Polymorphismus nachbildet, und sei es nur 
durch Typeflags und switch-case Orgien. In Summe schenkt sich das kaum 
was, nur ist die C++ Lösung eleganter und besser erweiterbar.

Und so hält sich dann eben hartnäckig das Gerücht, dass C++ einen 
'gewaltigen Overhead' mit sich bringt. Übersehen wird dabei, dass oft 
Äpfel mit Birnen verglichen werden.


Die Frage ist eher: was bringt mir OOP auf einem µC der Güteklasse eines 
kleinen Mega. UNzweifelthaft kann man ein paar Dinge einsetzen und 
alleine dadurch, dass C++ Compiler einige Dinge strikter handhaben, 
bringt das einen Vorteil. Aber die Sache ist auch die, dass OOP in 
dieser Leistungsklasse seine Vorzüge nicht wirklich ausspielen kann. 
Dazu sind die Programme einfach zu klein und noch zu überschaubar bzw. 
finden sich auch in OOP keine Lösungen zu den 'interessanteren' 
Problemen.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Karl Heinz Buchegger schrieb:
> Johann L. schrieb:
>> Oliver schrieb:
>>> Stefan Frings schrieb:
>>>> C++ bringt einen gewaltigen Overhead mit sich, der mit dem kleinen RAM
>>>> Speicher nicht gut zusammen passt.
>>>
>>> Immer wieder behauptet, und trotzdem falsch. Identische Funktionalität
>>> per C oder C++ (objektorientiert) implementiert, ergibt am Ende so gut
>>> wie gleich großen Code.
>>
>> Du meinst also, wenn eine Funktionalität wie "LED Blinkern" oder was
>> auch immer implementiert wird, dann ergibt sich mit C und C++ immer die
>> gleiche Codegüte, unabhängig davon jeweiligen Softwaredesign?
>
> Ich denke was Oliver meint ist, dass es bei 'C versus C++' Vergleichen
> oft so ist, dass nicht identische Funktionalität verglichen wird. Ein
> C++ welches mit std::string arbeitet hat nun mal als EXE eine meistens
> größere Signatur, als ein C Programm, welches die STringverarbeitung mit
> der heißen Nadel zusammenstrickt. Ergänzt man im C Programm die
> Features, die man durch std::string automatisch bekommt, dann sind beide
> wieder gleich auf.

Wenn man in der einen Sprache nur ein Feature/Konstrukt verwenden darf, 
das in der anderen zu vergleichbarem Binärcode führt, ist die Behauptung 
natürlich trivialerweise erfüllt.

Unterschiedliche Sprachen legen unterschiedliche Designpattern nahe und 
kommen mit unterschiedlichen Standard-Funktionalitäten daher.  Schreibt 
man in der Sprache, wird ein erfahrener Programmierer darauf 
zurückgreifen (wollen) und nicht "C in C++" programmieren wollen.

Callback-Implementieung, Stringverarbeitung, Konstruktoren und 
Destruktoren, Referenzen vs. Zeiger, etc.  Wenn man jetzt sagt

   "Konstruktor/Destruktor per malloc/free in C nachzubilden
   ist auch teuer"

dann stimmt das natürlich, aber der geneigte µC-C-Programmierer wird 
gerne einen Bogen drum machen.

Gleiches Thema: Referenzen.  Als ich mal wegen Effizienz-Problemen wegen 
einer Fixed-Point Implementierung in einem Forum nachfragte, kam 
reflexartig der Kommentar:  "Nimm Referenzen statt Pointer, das ist 
effizienter!" Nur leider waren die Datentypen nur 1-2 Bytes groß, und 
wenn nicht sichergestellt ist, daß alles geinlinet wird, dann wird das 
sehr teuer.

Die Probleme mit der Fixed-Point Implementierung hab ich übrigens nicht 
lösen können; Operator-Overloading wäre ganz nett gewesen, aber nach den 
ersten versuchen bin ich wieder nach C.

> Und so hält sich dann eben hartnäckig das Gerücht, dass C++ einen
> 'gewaltigen Overhead' mit sich bringt. Übersehen wird dabei, dass oft
> Äpfel mit Birnen verglichen werden.

C++ auf einem µC wie AVR mit den "erheblichen Resource-Einschränkungen" 
ist m.E. ohne Performance-Einbußen nur machbar, wenn man

   "Casual inspection of the generated code is highly recommended"

beherzigt — was freilich auch in entschärfter Form für C gilt. Auch mit 
C kann man schnell den Code aufblasen, etwa mit lokale Arrays, printf, 
float-Operationen oder Adressen von Auto/Parametern zu nehmen.

von Karl H. (kbuchegg)


Lesenswert?

Johann L. schrieb:

>> Ich denke was Oliver meint ist, dass es bei 'C versus C++' Vergleichen
>> oft so ist, dass nicht identische Funktionalität verglichen wird. Ein
>> C++ welches mit std::string arbeitet hat nun mal als EXE eine meistens
>> größere Signatur, als ein C Programm, welches die STringverarbeitung mit
>> der heißen Nadel zusammenstrickt. Ergänzt man im C Programm die
>> Features, die man durch std::string automatisch bekommt, dann sind beide
>> wieder gleich auf.
>
> Wenn man in der einen Sprache nur ein Feature/Konstrukt verwenden darf,
> das in der anderen zu vergleichbarem Binärcode führt, ist die Behauptung
> natürlich trivialerweise erfüllt.

Das hat nichts mit 'verwenden dürfen' zu tun.
Will man in C eine Stringverarbeitung bauen, die einen ähnlichen 
Sicherheitslevel bietet wie std::string, dann braucht man dazu Aufwand. 
Man kann aber nicht diesen Aufwand einfach weglassen und so tun, als ob 
Buffer Overflows einfach mit dazugehören und dann mit dem Finger auf C++ 
zeigen, bei dem diese Overflows von den darunterliegenden Klassen schon 
aufgefangen und behandelt werden. Das derartige Vergleiche immer hinken 
werden, darüber brauchen wir doch nicht wirklich diskutieren. Genau das 
wird aber oft (auf Desktop-Programmier-Ebene) getan, wenn die Aussage 
mit dem 'gewaltigen Overhead' kommt.
Der Overhead ist auch nicht größer, als wie wenn man das C Programm erst 
mal auf den gleichen Stand bringt, den man mit C++ und seinen Klassen 
von Haus aus bekommt.
Ein
  std::vector<int> a(5);
  a.push_back( 8 );
ist nun mal was anderes als ein
  int a[5];
  int i = 0;
  a[i++] = 8;
Brauch ich das dynamische Wachstum, dann muss ich auch in C 
entsprechenden Aufwand treiben. Brauch ich es nicht, dann erhebt sich 
die Frage, warum ein std::vector benutzt wird. Bringt man beide 
Schnipsel auf gleiche Funktionalität, dann verschwindet der 
vermeintliche Overhead, der nur deswegen sichtbar war, weil Äpfel mit 
Birnen verglichen wurden.

von Roland H. (batchman)


Lesenswert?

Oliver schrieb:
> Stefan Frings schrieb:
>> C++ bringt einen gewaltigen Overhead mit sich, der mit dem kleinen RAM
>> Speicher nicht gut zusammen passt.
>
> Immer wieder behauptet, und trotzdem falsch. Identische Funktionalität
> per C oder C++ (objektorientiert) implementiert, ergibt am Ende so gut
> wie gleich großen Code.

Das kann ich nur unterstreichen. Ich habe mir mal die Mühe gemacht, ein 
C-Programm mit structs und Funktionen nach C++ mit class und Methoden 
umzuschreiben: Es entsteht exakt der gleiche Code, im Falle von C++ 
aber bedeutend weniger Tipparbeit, public/private und polymorphe 
Funktionen.

Es gibt kein Argument gegen C++ auf µC - verwende ich sogar auf bei 
den µCs mit 2kb Flash (attiny2313/msp430g22x1).

> Mikrocontroller Programme sind in der Regel klein genug, um auch ohne
> C++ Objekte übersichtlich zu bleiben.

Und wenn es dann doch - so ab 32kb - plötzlich groß wird? Wechselt man 
zu C++? Das macht dann keiner, also besser gleich.

> Es ist nicht schwer, Strukturen und Funktionen auch ohne OOP ausreichend
> eindeutige Namen zu geben.

Das Problem hatte ich ständig. Die guten Namen gehen irgendwann aus. 
Alleine der "kleine" name space durch die Klasse ist vorteilhaft.

asdf schrieb:
> Wegen dem OO
> Da ist ein paper in dem das jemand komplett durchgebruetet hat:
> http://www.planetpdf.com/codecuts/pdfs/ooc.pdf

Das ist von 1993, und das Wort µC taucht darin nicht auf.

> Offensichtlich ist das meiste davon overkill aufm Mikrocontroller. Meist
> reicht es wenn man sein Zeugs einigermaßen sauber in structures verpackt

Mit der Einschränkung "meist" und "einigermaßen". Es spricht überhaupt 
gar nichts dagegen, dies "sauber" in eine class zu verpacken, und 
private/public nebst getter und setter einzusetzen: Es entsteht der 
exakt gleiche Code.

Johann L. schrieb:
> Du meinst also, wenn eine Funktionalität wie "LED Blinkern" oder was
> auch immer implementiert wird, dann ergibt sich mit C und C++ immer die
> gleiche Codegüte, unabhängig davon jeweiligen Softwaredesign?

Die gleiche Codegüte beim gleichen SW-Design wie oben beschrieben. Was 
soll sonst der Vergleich? Den Vergleich mit Funktionszeigern vs. 
virtuelle Funktionen habe ich noch nicht durchgeführt.

Karl Heinz Buchegger schrieb:
> Die Frage ist eher: was bringt mir OOP auf einem µC der Güteklasse eines
> kleinen Mega.

Bis auf den letzten Abschnitt stimme ich Dir zu :-)

M. A. ist die Frage eher "Warum nicht gleich auf einen Ansatz setzen, 
der keine Nachteile mit sich bringt, damit man auch für den großen Mega 
gerüstet ist: Im Sinne der Erfahrung, der Tools, der erstellten 
Bibliotheken usw.

von Tim S. (Firma: tsx89) (freak_ts) Benutzerseite


Lesenswert?

asdf schrieb:
> Wegen dem OO
> Da ist ein paper in dem das jemand komplett durchgebruetet hat:
> http://www.planetpdf.com/codecuts/pdfs/ooc.pdf

Erstmal danke für die vielen umfangreichen Antworten...

Timo Reuters schrieb:
> Mein Vorschlag lern erstmal C.
> Damit kommste beim Mikrocontroller programmieren deutlich weiter als mit
> C++.
> Solltest dir irgendwann C nicht mehr ausreichen ist es von C nach C++
> auch nicht mehr weit.

Karl Heinz Buchegger schrieb:
> In Summe schenkt sich das kaum was, nur ist die C++ Lösung eleganter und besser 
erweiterbar.

Was würdet ihr mir nun raten?? (Bitte nicht wieder einen Kampf starten 
wegen dem einen Byte was C evtl sparsamer [oder whatever] gegenüber C++ 
ist...) Bin für´s erste mit "unoptimierten" - also direkt erzeugten Code 
zufrieden.

Johann L. schrieb:
> C++ auf einem µC wie AVR mit den "erheblichen Resource-Einschränkungen"
> ist m.E. ohne Performance-Einbußen nur machbar, wenn man
>
>    "Casual inspection of the generated code is highly recommended"
>
> beherzigt — was freilich auch in entschärfter Form für C gilt.

Das gilt für mich / bei mir noch gar nicht ^^...

So derart Aufwändig werden meine Sachen wahrscheinlich auch wieder 
nicht.
Ich versuch mal dem Compiler zu vertrauen.

Marwin schrieb:
> Tim S. schrieb:
>> Das muss doch in paar Zeilen Code erklärbar sein, bevor ich unzählige
>> Tut´s nach einem Punkt absuche, der eventuell gar nicht zu finden ist...
>
> Wenn dir so schwer faellt, die Frage zu formulieren, bist du einfach
> nicht erfahren genug. Da bringt dir eine scheinbar einfache Antwort auch
> nichts. Sammle also erst mal etwas Erfahrung mit C++ und mit
> Mikrocontrollern, dann wirst du schnell merken, dass die Verwendung von
> C++ auf Mikrocontrollern ganz einfach ist -

- wird langsam...

> und du wirst auch selbst merken, weswegen sich viele Konzepte vom Desktop > 
nicht auf den Mikrocontroller uebertragen lassen.

Ja schon klar, begrenzter Speicher, ganz andere Architektur usw. Ist ja 
auch kein ARM.

Vorläufig werde ich bei C++ bleiben, außer jemand kann mich wirklich 
überzeugen, dass es längerfristig gesehen mehr sinn macht, auf C zu 
setzen. Insbesondere auch wegen (irgendwann mal ganz) anderen µCs, oder 
eben auch Desktop-Sachen. Dass ich eben bei einer einzigen relativ gut 
portierbaren Sprache bleiben könnte.

Werde mir jedenfalls constructor/destructor selber bauen müssen, und mir 
die benötigten (erwähnten) Funktionen und Eigenschaften von C++, mit 
Pointern, Callback, Referenzen und Zeiger etc. genauer auf der Zunge 
zergehen lassen.


Gruß.

von Marwin (Gast)


Lesenswert?

Tim S. schrieb:

> Werde mir jedenfalls constructor/destructor selber bauen müssen, und mir

Mit Konstruktoren und Destruktoren gibt es kein Problem. Nur mit Allem, 
was dynamischen Speicher braucht. Also erst Mal Finger weg von new und 
delete (genauso wie man mit C die Finger von malloc und free laesst). 
Vor der Verwendung von virtuellen Methoden dreimal nachdenken, das macht 
die Klassen fett. Und die std-C++-Library vergessen wir gleich mal, 
halte dich an das nackige C++.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Ok, dann in medias res.

Aus den unterschiedlich Gründen hab ich bisherimmer einen großen Bogen 
um C++ gemacht.  Dementsprechend wenig Ahnung hab ich von dem Moloch, 
lerne aber gerne dazu.

Für den Anfang hab ich ein kleines Beispiel getippst, das den Ansatz 
einer accum-Implementierung analog zu accum aus stdfix.h von ISO/IEC TR 
18037 darstellen soll:
1
class accum
2
{
3
    // s7.8 wie accum aus stdfix.h
4
5
public:
6
    int x;
7
8
    accum operator + (accum b) const
9
    {
10
        accum sum;
11
        sum.x = x + b.x;
12
        return sum;
13
    };
14
    
15
    inline accum (void) : x (0) {};
16
    inline accum (double val)  : x (val * 0x100) {};
17
};
18
19
20
#include <math.h>
21
#include <stdlib.h>
22
#include <avr/pgmspace.h>
23
24
const PROGMEM accum lookup[] = 
25
{
26
    accum (M_PI),
27
    accum (M_E)
28
};
29
30
const accum v PROGMEM = 0;
31
32
unsigned volatile index;
33
34
int main (void)
35
{
36
    if (pgm_read_word (&v))
37
        abort();
38
    
39
    return (lookup[index] + accum (0.5) + v).x;
40
}

Vermutlich hab ich da schon x Klopper drinne.

Als erstes: Wie schafft man es, daß das Programm nicht auf abort läuft? 
Ich hab's zwar nicht laufen lassen, aber daß es auf abort kommt ist 
offensichtlich.

Zweitens: C++ ist keine Obermenge von C, schon garnicht vom o.g. ISO/IEC 
TR 18037, was etwa die "Named Address Space" (AS) spezifiziert, die 
avr-gcc 4.7 implementiert (__flash, __flash1, ..., __memx).  Wie macht 
man das sauber in C++, ohne das PROGMEM und pgm_read-Geraffel?

Drittens: Ist es möglich, C-Erweiterungen in C++ zu verwenden? Ein 
Beispiel ist der o.g. accum-Typ, ein s7.8 Q-Format.

Gerade auf kleiner Hardware ist Fixed-Point ganz nett.  Es ist 
wesentlich effizienter als float und schöner, als ständig von Hand Werte 
hin- und her und wieder hin zu skalieren und zu shiften.  Ausserdem 
bekommt ein Compiler, der diese Typen kennt, z.T. besseren Code hin als 
(inline-)Assembler

Das kann in C++ zwar nett in Klassen verpackt werden, aber die libgcc 
bring schon mehr als 3000 Support-Funktionen für Fixed-Point mit: 
Konvertierung, +, -, *, /, <<, >>, ABS, NEG, Saturierung (!), 
Vergleiche, unterschiedliche Formate wie 0.8, s.7, 8.8, s7.8, 0.16, 
s.15, 16.16, etc.  Weiterer Vorteil sind neue Typ-Suffixe, die man in 
C++ auch nicht hat.

von Karl H. (kbuchegg)


Lesenswert?

Johann L. schrieb:

>     accum operator + (accum b) const

Das erste was du dir (tatsächlich) angewöhnen musst:
Wenn geht, dann benutze const Referenzen.
Es gibt hier keinen Grund b als Wert zu übergeben, in der Funktion wird 
b nicht verändert. Also const Referenz

Das muss nicht bedeuten, dass es damit automatisch effizienter ist. Aber 
du ermöglichst dem Compiler sein Bestes zu tun.

      accum operator + (const accum& b) const

>     {
>         accum sum;
>         sum.x = x + b.x;

Bevorzuge Initialisierung, wo es geht. Es hat keinen Sinn, wenn der 
Konstruktor erst mal das sum Objekt in einen gültigen Zustand bringt, 
nur damit du es nachher gleich wieder änderst. Schreibe dir 
Konstruktoren, mit denen du gleich das Objekt so initialisieren kannst, 
dass das Ergebnis der Operation als Argument für einen Konstruktor 
gebrauchen kannst (wenn du sie noch nicht hast).


    std::string result;
    result = "hello world";

ist eine Operation zu viel

    std::string result = "hello world";

ergibt danach denselben Inhalt, wird aber schneller ausgeführt.



> const PROGMEM accum lookup[] =
> {
>     accum (M_PI),
>     accum (M_E)
> };
>
> const accum v PROGMEM = 0;
>
> unsigned volatile index;
>
> int main (void)
> {
>     if (pgm_read_word (&v))
>         abort();
>
>     return (lookup[index] + accum (0.5) + v).x;
> }[/c]
>
> Vermutlich hab ich da schon x Klopper drinne.

Sieht nicht so schlecht aus. Inwiefern da jetzt PROGMEM den gcc 
beeinflusst kann ich nicht sagen, aber der Rest sieht gut aus.

>
> Als erstes: Wie schafft man es, daß das Programm nicht auf abort läuft?
> Ich hab's zwar nicht laufen lassen, aber daß es auf abort kommt ist
> offensichtlich.

Wieso soll das offensichtlich sein?
Du hast hier eine implizite Annahme: dass sizeof(accum) gleich einem 
word ist. Das würde ich mal in Frage stellen.

> Zweitens: C++ ist keine Obermenge von C, schon garnicht vom o.g. ISO/IEC
> TR 18037, was etwa die "Named Address Space" (AS) spezifiziert, die
> avr-gcc 4.7 implementiert (__flash, __flash1, ..., __memx).  Wie macht
> man das sauber in C++, ohne das PROGMEM und pgm_read-Geraffel?

'namespace' Keyword.
Aber im Regelfall reicht es aus, wenn man die Dinge einfach in Klassen 
kapselt. Dann fungiert der Klassenname wie eine Art Namespace.

> Drittens: Ist es möglich, C-Erweiterungen in C++ zu verwenden? Ein
> Beispiel ist der o.g. accum-Typ, ein s7.8 Q-Format.

Kann ich dir nicht sagen. Dazu müsste man die Compilerdoku studieren, 
oder einfach ausprobieren. Ich würde aber davon ausgehen, dass das 
meiste was ein C Compiler experimentell implementiert hat, auch in C++ 
gehen wird. Gerade der gcc ist sowas wie das "Arbeitspferd" der Leute, 
die versuchen eine Spracherweiterung durch die Normierungsgremien zu 
bekommen. Eine beispielhafte Implementierung ist zu diesem Behufe 
nützlich (um zu demonstrieren dass es prinzipiell geht und das keine 
negativen Auswirkungen auf den Rest zu erwarten sind), und oft genug 
muss dazu der gcc her halten.

von Karl H. (kbuchegg)


Lesenswert?

Karl Heinz Buchegger schrieb:

>>     accum operator + (accum b) const
>
> Das erste was du dir (tatsächlich) angewöhnen musst:
> Wenn geht, dann benutze const Referenzen.
> Es gibt hier keinen Grund b als Wert zu übergeben, in der Funktion wird
> b nicht verändert. Also const Referenz
>
> Das muss nicht bedeuten, dass es damit automatisch effizienter ist. Aber
> du ermöglichst dem Compiler sein Bestes zu tun.
>
>       accum operator + (const accum& b) const


Um das auch gleich auszuräumen.
Eine Referenz ist nicht einfach nur ein POinter in einem anderen Gewand. 
Eine Referenz ist ein anderer Name für ein ansonsten existierendes 
Objekt.

D.h. in

  accum x, y;

  x + y;

wir y an die Referenz gebunden. b ist nicht einfach nur eine neue 
Variable, sondern mit der Referenz hast du dem COmpiler gesagt: das ist 
dieselbe Variable, die sind austauschbar

Wird die inline Ersetzung gemacht

   x.operator+( y )

...

   const accum& b = y;
   accum sum;
   sum.x = x.x + b.x;

dann kann der Compiler quer durch die ganze Ersetzung b durch y 
ersetzen!

   accum sum
   sum.x = x.x + y.x;

d.h. b taucht im (soweit) endgültigen Code überhaupt nicht mehr auf.



Eines ist unbestritten: C++ lebt sehr stark davon, dass der Compiler 
optimiert. Bei abgeschaltetem Optimizer ist der erzeugte Code 
grauenhaft. Darf der Compiler aber optimieren, dann würde es mich nicht 
wundern, wenn bei deinem Code mehr oder weniger dasselbe wie bei
1
int values[2] = { M_PI * 100, M_E * 100 }
2
int v;
3
4
int main()
5
{
6
  int tmp = pgm_read_word( &v );
7
  return values[index] + 0.05 * 100 + tmp;
8
}

rauskommt.
(Auch das retournierte Objekt im op+ darf der Compiler ohne Rücksicht 
auf Verluste wegoptimieren. Das ist die Named Return Value Optimization.
http://en.wikipedia.org/wiki/Return_value_optimization

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Karl Heinz Buchegger schrieb:
> Johann L. schrieb:
>
>>     accum operator + (accum b) const
>
> Das erste was du dir (tatsächlich) angewöhnen musst:
> Wenn geht, dann benutze const Referenzen.
> Es gibt hier keinen Grund b als Wert zu übergeben, in der Funktion wird
> b nicht verändert. Also const Referenz

Für + seh ich das ein, weil die Funktion geinlint werden soll. Aber was, 
wenn nicht, etwa wenn die Operation teuer ist wie bei Division oder 
einer saturierten Addition.  In dem Falle würde doch die Adresse von b 
genommen, bzw. dem entsprechenen Parameter.  Die Adresse einer auto zu 
nehmen ist auf einem x86-Host ken großes Ding, aber mach das mal auf 
AVR.

> Das muss nicht bedeuten, dass es damit automatisch effizienter ist. Aber
> du ermöglichst dem Compiler sein Bestes zu tun.
>
>       accum operator + (const accum& b) const
>
>>     {
>>         accum sum;
>>         sum.x = x + b.x;
>
> Bevorzuge Initialisierung, wo es geht. Es hat keinen Sinn, wenn der
> Konstruktor erst mal das sum Objekt in einen gültigen Zustand bringt,
> nur damit du es nachher gleich wieder änderst. Schreibe dir
> Konstruktoren, mit denen du gleich das Objekt so initialisieren kannst,
> dass das Ergebnis der Operation als Argument für einen Konstruktor
> gebrauchen kannst (wenn du sie noch nicht hast).

Has Problem ist, daß der Constructore einen double bekommen muss. Nimmt 
man einen Contructor mit int um die Komponente .x direkt zu 
initialisieren, dann hat man später eine Mehrdeutigkeit, weil man nicht 
den Contruktoe mit 1 aufrufen kann um die Zahl mit 1 zu initialisieren 
(der Wert wäre dann 1/256).

Ich hatte schon einen Constructor mit int und den wieder rausgeworfen, 
weil zB in einem Initializer der falsche Constructor für einen double 
wie 3.14 genommen wurde -> falscher Wert.

>> const PROGMEM accum lookup[] =
>> {
>>     accum (M_PI),
>>     accum (M_E)
>> };
>>
>> const accum v PROGMEM = 0;
>>
>> unsigned volatile index;
>>
>> int main (void)
>> {
>>     if (pgm_read_word (&v))
>>         abort();
>>
>>     return (lookup[index] + accum (0.5) + v).x;
>> }[/c]
>>
>> Vermutlich hab ich da schon x Klopper drinne.
>
> Sieht nicht so schlecht aus. Inwiefern da jetzt PROGMEM den gcc
> beeinflusst kann ich nicht sagen, aber der Rest sieht gut aus.

Es wird ein impliziter Constructor für v und lookup angelegt. Zum einen 
ist das teuer wegen des zusätzlichen Codes, zum anderen können die 
Variablen nicht mehr im Flash sein. Entweder sie sind im Flash und der 
implizite Constructor schreibt willkürlich RAM-Adressen (diejenigen, die 
den Objekten im Flash entsprechen), oder das Zeug ist entgegen der 
Vereinbarung nicht im Flash.  Hier ist letzteres der Fall.

>> Als erstes: Wie schafft man es, daß das Programm nicht auf abort läuft?
>> Ich hab's zwar nicht laufen lassen, aber daß es auf abort kommt ist
>> offensichtlich.
>
> Wieso soll das offensichtlich sein?

Die Daten liegen nicht im Flash!  Daher das abort — es sei denn, an 
der entsprechenden Flash-Adresse stehz zufällig eine 0x0000.

> Du hast hier eine implizite Annahme: dass sizeof(accum) gleich einem
> word ist. Das würde ich mal in Frage stellen.

sizeof accum ist 2.

Wenn man davon nicht ausgehen kann, dann braucht's memcpy_P o.ä. um 
Objekte aus dem Flash zu lesen.  Oder man muss die Daten 
serialisieren/deserialisieren, was insbesondere Kopfzerbrechen bereitet, 
wenn die Daten schon zur Compilezeit serialisiert werden sollen, z.B in 
ein Char- oder unsigned Array.  Und selbst wenn das geht: schön ist 
anders...

Im Endeffekt heisst das: Es ist nicht möglich, Objekte aus dem Flash zu 
lesen und zB const-Objekte zu haben, die im Flash leben.

>> Zweitens: C++ ist keine Obermenge von C, schon garnicht vom o.g. ISO/IEC
>> TR 18037, was etwa die "Named Address Space" (AS) spezifiziert, die
>> avr-gcc 4.7 implementiert (__flash, __flash1, ..., __memx).  Wie macht
>> man das sauber in C++, ohne das PROGMEM und pgm_read-Geraffel?
>
> 'namespace' Keyword.
> Aber im Regelfall reicht es aus, wenn man die Dinge einfach in Klassen
> kapselt. Dann fungiert der Klassenname wie eine Art Namespace.

Ich versteh nicht, wie man durch den Namespace erreicht, daß der 
Compiler nicht mehr per LD/LDS auf die Objekte zugreift, sondern wie bei 
des Address-Spaces per LPM.  Wo und wie kommen denn die 
LPM-Instruktionen ins Spiel?

Karl Heinz Buchegger schrieb:

> Eines ist unbestritten: C++ lebt sehr stark davon, dass der Compiler
> optimiert. Bei abgeschaltetem Optimizer ist der erzeugte Code
> grauenhaft.
>
> int values[2] = { M_PI * 100, M_E * 100 }

Aber genau das will man doch vermeiden in C++! Das "* 100" ist die 
interne Darstellung des accum, keiner ausser die Innereien von accum 
hat das zu interessieren!

Das oll doch alles in accum gekapselt sein, oder nicht?

> Darf der Compiler aber optimieren, dann würde es mich nicht
> wundern, wenn bei deinem Code mehr oder weniger dasselbe wie bei
>
>   return values[index] + 0.05 * 100 + tmp;

Nicht nur das Ergebnis spielt eine Rolle, sondern auch der Weg dahin.

1) "* 100" gehört in die Klasse accum. Wenn das nicht geht hat C++
   versagt — behaupt ich jetzt mal frech ;-)

2) Es geht hier darum, float-Operationen komplett zu vermeiden und
   die entsprechenden Fixed-Operationen in eine eine Klasse zu packen.
   Wenn float auf AVR keine Effizient-Probleme hätte, dann bräuchte
   man die ganze Terz ja nicht und würde direkt float/double hernehmen.

von Karl H. (kbuchegg)


Lesenswert?

Johann L. schrieb:

> Für + seh ich das ein, weil die Funktion geinlint werden soll. Aber was,
> wenn nicht, etwa wenn die Operation teuer ist wie bei Division oder
> einer saturierten Addition.  In dem Falle würde doch die Adresse von b
> genommen, bzw. dem entsprechenen Parameter.  Die Adresse einer auto zu
> nehmen ist auf einem x86-Host ken großes Ding, aber mach das mal auf
> AVR.

OK.
Ist natürlich ein Argument. Bei Objekten dieser Größe (die im 
wesentlichen nur ein int sind) ist eine Kopie kein Thema.

Aber das ist ja nicht der Normalfall.


> Has Problem ist, daß der Constructore einen double bekommen muss. Nimmt
> man einen Contructor mit int um die Komponente .x direkt zu
> initialisieren, dann hat man später eine Mehrdeutigkeit, weil man nicht
> den Contruktoe mit 1 aufrufen kann um die Zahl mit 1 zu initialisieren
> (der Wert wäre dann 1/256).

Man kann Konstruktoren als 'explizit' markieren. Damit verbietet man 
ihm, einen Konstruktor von sich aus zu benutzen. Eine Verwendung dieses 
Konstruktors muss damit explizit angefordert werden.

> Im Endeffekt heisst das: Es ist nicht möglich, Objekte aus dem Flash zu
> lesen und zB const-Objekte zu haben, die im Flash leben.

Da hat sich ja nichts geändert.
const hat ja ursäcjlich nichts mit dem Flash zu tun (zumindest nicht 
beim gcc)

>
>>> Zweitens: C++ ist keine Obermenge von C, schon garnicht vom o.g. ISO/IEC
>>> TR 18037, was etwa die "Named Address Space" (AS) spezifiziert, die
>>> avr-gcc 4.7 implementiert (__flash, __flash1, ..., __memx).  Wie macht
>>> man das sauber in C++, ohne das PROGMEM und pgm_read-Geraffel?
>>
>> 'namespace' Keyword.
>> Aber im Regelfall reicht es aus, wenn man die Dinge einfach in Klassen
>> kapselt. Dann fungiert der Klassenname wie eine Art Namespace.
>
> Ich versteh nicht, wie man durch den Namespace erreicht,

Mein Fehler.
namespaces haben damit nichts zu tun. Ich war so sehr auf Standard-C++ 
fixiert, dass ich gar nicht mitbekommen habe, dass du von was ganz 
anderem sprichst.

> daß der
> Compiler nicht mehr per LD/LDS auf die Objekte zugreift, sondern wie bei
> des Address-Spaces per LPM.  Wo und wie kommen denn die
> LPM-Instruktionen ins Spiel?

Da würde ich mein Heil in einer 'Flash' Klasse suchen, die im Prinzip 
wie ein Pointer funktioniert und die Dereferenzierungsoperation 
überschreibt (monadischen operator *). Ein klassischer Smart-Pointer.


>
> Karl Heinz Buchegger schrieb:
>
>> Eines ist unbestritten: C++ lebt sehr stark davon, dass der Compiler
>> optimiert. Bei abgeschaltetem Optimizer ist der erzeugte Code
>> grauenhaft.
>>
>> int values[2] = { M_PI * 100, M_E * 100 }
>
> Aber genau das will man doch vermeiden in C++! Das "* 100" ist die
> interne Darstellung des accum, keiner ausser die Innereien von accum
> hat das zu interessieren!

Das was ja ein Beispiel, wie man das in C schreiben würde!
Natürlich will man das in C++ in der Klasse verstecken.


> Nicht nur das Ergebnis spielt eine Rolle, sondern auch der Weg dahin.

Auch der Weg dorthin, wird vom Compiler nicht viel anders organsisiert 
werden.

>
> 1) "* 100" gehört in die Klasse accum. Wenn das nicht geht hat C++
>    versagt — behaupt ich jetzt mal frech ;-)
>
> 2) Es geht hier darum, float-Operationen komplett zu vermeiden und
>    die entsprechenden Fixed-Operationen in eine eine Klasse zu packen.
>    Wenn float auf AVR keine Effizient-Probleme hätte, dann bräuchte
>    man die ganze Terz ja nicht und würde direkt float/double hernehmen.

Oh Mann.
Ich wollte doch nur dein C++ Beispiel in ein C Beispiel überführen, von 
dem ich erwarten würde, dass der C Compiler in etwa denselben Code 
erzeugt wie ein C++ COmpiler, der sich das aus den Klassen 
zusammenoptimiert. Das war die Intention.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Karl Heinz Buchegger schrieb:
> Johann L. schrieb:
>
>> Drittens: Ist es möglich, C-Erweiterungen in C++ zu verwenden? Ein
>> Beispiel ist der o.g. accum-Typ, ein s7.8 Q-Format.
>
> Kann ich dir nicht sagen. Dazu müsste man die Compilerdoku studieren,
> oder einfach ausprobieren.

-std=gnu++0x tut's jedenfalls nicht.  Address-Space Support funktioniert 
definitiv nicht in C++.

> Ich würde aber davon ausgehen, dass das
> meiste was ein C Compiler experimentell implementiert hat, auch in C++
> gehen wird. Gerade der gcc ist sowas wie das "Arbeitspferd" der Leute,
> die versuchen eine Spracherweiterung durch die Normierungsgremien zu
> bekommen.

Das war einmal.

Mittlerweile ist's andersrum.  Die GCC-Entwickler sind überhaupt nicht 
mehr begeistert von "Language Extentions"

Address-Space Qualifier wie __flash werden mit ziemlicher Sicherheit 
nicht in C++ aufgenommen, zumindest wenn ich Gaby [1] glauben darf; er 
ist Mitglied der WG21, die den C++ Standard hütet:

>> In general, there is an aversion towards more qualifiers.
>> I suspect this is mostly because of the committee being
>> burned by "volatile" and also because qualifiers tend to
>> lead to combinatorial explosion when it comes to implicit
>> conversions and overload resolution.
>>
>> The trend has been to use classes (sometimes with compiler
>> support) to render abstractions such as these.  See for
>> example how C++11 handles atomics (std::atomic<T>) compared
>> to C11 (_Atomic).

> Eine beispielhafte Implementierung ist zu diesem Behufe
> nützlich (um zu demonstrieren dass es prinzipiell geht und das keine
> negativen Auswirkungen auf den Rest zu erwarten sind), und oft genug
> muss dazu der gcc her halten.

Wenn ich [1] recht verstehe würde das über Templates(?) gehen wie 
std::flash<T>.  Wie würde so ein Template denn aussehen? Und auch da 
wieder die Frage: Wo kommen die LPMs her?

Und was macht man, denn man den Code zu einer anderen Architektur 
portiert? Mit den Address-Spaces geht das unfein, aber kurz und 
schmerzlos über

#define __flash

Oder sogar ganz ohne den Code anzufassen per -D__flash=

--

[1]
http://lists.gnu.org/archive/html/avr-gcc-list/2012-05/msg00044.html

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Karl Heinz Buchegger schrieb:
> Johann L. schrieb:
>
>> Für + seh ich das ein, weil die Funktion geinlint werden soll. Aber was,
>> wenn nicht, etwa wenn die Operation teuer ist wie bei Division oder
>> einer saturierten Addition.  In dem Falle würde doch die Adresse von b
>> genommen, bzw. dem entsprechenen Parameter.  Die Adresse einer auto zu
>> nehmen ist auf einem x86-Host ken großes Ding, aber mach das mal auf
>> AVR.
>
> OK.
> Ist natürlich ein Argument. Bei Objekten dieser Größe (die im
> wesentlichen nur ein int sind) ist eine Kopie kein Thema.

Es geht nicht nur um die Kopie, sondern auch um das Erzeugen der 
Referenz.  Wird geinlint dann kostet weder Referenz noch Kopie weil der 
gleiche Code entsteht. Wird nicht geinlint, ist abzuwägen zwischen Kopie 
und Referenzerzeugung (d.h. evtl. Anlegen eines Frames!) und den in der 
Methode entstehenden höheren Registerlast. AVRs sind nicht gerade üppig 
mit Zeiger-Registern ausgestattet.

> Aber das ist ja nicht der Normalfall.

Es war der Beweggrund für meinen letzten Umstiegsversuch nach C++:

Eine hübsche Fixed-Point Implementierung, die nicht zur C-üblichen 
Verunstaltung des Codes führt.  Allerdings hab ich den Versuch bald 
aufgegeben.  Ich dachte aber, daß er hier als naheliegendes Beispiel für 
die Fähigkeiten von C++ taugen würde.

>> Das Problem ist, daß der Constructor einen double bekommen muss. Nimmt
>> man einen Contructor mit int um die Komponente .x direkt zu
>> initialisieren, dann hat man später eine Mehrdeutigkeit, weil man nicht
>> den Contruktoe mit 1 aufrufen kann um die Zahl mit 1 zu initialisieren
>> (der Wert wäre dann 1/256).
>
> Man kann Konstruktoren als 'explizit' markieren. Damit verbietet man
> ihm, einen Konstruktor von sich aus zu benutzen. Eine Verwendung dieses
> Konstruktors muss damit explizit angefordert werden.

Ok, ok.

Wenn dann aber der Standard-Construktor einen double bekommt (was 
gebraucht wird, um ohne Verrenkung Konstanten wie 3.14 einzufüttern), 
wie wird dann die float-Arithmetik vermieden wenn der Constructor 
keine zur Compilezeit bekannte Konstante bekommt?  Bracht's dann doch 
wieder nichtportierbare GCC-Hässlichkeiten wie __builtin_choose_expr 
oder __builtin_const oder wie auch immer?

>> Im Endeffekt heisst das: Es ist nicht möglich, Objekte aus dem Flash zu
>> lesen und zB const-Objekte zu haben, die im Flash leben.
>
> Da hat sich ja nichts geändert.
> const hat ja ursäcjlich nichts mit dem Flash zu tun (zumindest nicht
> beim gcc)

Nicht bei AVR.  Bei linearem Speicher spricht absolut nicht dagegen, 
const Objekte ins Flash zu legen, d.h. .rodata liegt im Flash.  Bei 
avr-gcc muss .rodata jedoch im RAM liegen!

> Ich war so sehr auf Standard-C++ fixiert, dass ich gar nicht
> mitbekommen habe, dass du von was ganz anderem sprichst.

Ah, ok.

>> daß der Compiler nicht mehr per LD/LDS auf die Objekte zugreift,
>> sondern wie bei des Address-Spaces per LPM.  Wo und wie kommen
>> denn die LPM-Instruktionen ins Spiel?
>
> Da würde ich mein Heil in einer 'Flash' Klasse suchen, die im Prinzip
> wie ein Pointer funktioniert und die Dereferenzierungsoperation
> überschreibt (monadischen operator *). Ein klassischer Smart-Pointer.

AFAIK ist es in C++ nicht erlaubt, Zeiger zu überladen? Und Basis-Typen 
auch nicht.

Wie würde so ein Code denn aussehen?

Hier mal ein kleiner C-Code:
 
1
typedef short _Fract T;
2
3
// Default calibration values in flash.
4
// Will be adjusted by calibration as needed
5
static __flash const volatile T cal[] =
6
{
7
    0.214hr, 0.1191hr
8
};
9
10
T calc (T x)
11
{
12
    return (x + 0.5hr) * cal[1] + cal[0];
13
}

avr-gcc 4.8 erzeugt:
 
1
calc:
2
  ldi r30,lo8(cal+1)
3
  ldi r31,hi8(cal+1)
4
  lpm r18,Z
5
  ldi r30,lo8(cal)
6
  ldi r31,hi8(cal)
7
  lpm r25,Z
8
  subi r24,lo8(-(0x40))
9
  mov r19, r24
10
  fmuls r18,r19
11
  mov r24,r1
12
  clr __zero_reg__
13
  add r24,r25
14
  ret

Natürlich ist klar wie das als "normaler" C-Code aussieht, aber...

>> Karl Heinz Buchegger schrieb:

> Oh Mann.
> Ich wollte doch nur dein C++ Beispiel in ein C Beispiel überführen, von
> dem ich erwarten würde, dass der C Compiler in etwa denselben Code
> erzeugt wie ein C++ Compiler, der sich das aus den Klassen
> zusammenoptimiert. Das war die Intention.

...interessant ist doch die andere Richtung: Wie geht das in C++, ohne 
einfach "C per C++" zu programmieren sondern richtig mit Kapselung und 
Objektorientierung und so?  Und zudem ohne Performance-Einbuße — und 
natütlich auch übertragbar auf ausgewachseneren Code.

Hier würden doch (mindestens) 2 Klassen gebraucht:

1) Eine für T, die die Arithmetik implementiert

2) Eine die das __flash abbildet

Wie sieht denn so ein Smart-Zeiger aus, ich hab echt keinen Plan.  2) 
ist vielleicht auch ein Template, aber auch da hab ich keine Idee wie es 
aussehen könnte.

Gerade den obigen Code sollte doch gut geeignet sein um die 
Möglichkeiten von C++ zu erklären?  Denn hier kann man nicht einfach 
alles in "extern C" reinkloppen, sondern muss wirklich C++-Magie 
verwenden, um die fehlenden Features zu modellieren.

In Objective-C könnte man den obigen Code einfach übersetzen, denn im 
Gegensatz zu C++ ist Objective-C eine echte Obermenge von C.

Was spricht denn eigentlich gegen Objective-C?  Dazu reicht doch schon 
ein -c objective-c als Compilerschalter und ein Umschreiben des C-Codes 
erübrigt sich. Man kann dann anfangen, den Code Stück für Stück nach OOP 
zu bringen.

Der TO sprach ja nut von OOP, aber das ist hier wohl synonym mit C++?

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.