Forum: Mikrocontroller und Digitale Elektronik Welche Programmiersprache auf µC


von PeterPan (Gast)


Lesenswert?

Nop schrieb:
> X4U schrieb:
>
>> Bei C von zufrieden zu reden ist schon schwierig. Es ist halt ein
>> Kompromiss. Man ist dicht genug auf der Hardware, hat aber eine Art
>> Hochsprache besser evtl. mit "universeller Makroassembler" definiert.
>
> Exakt. Die Dosis macht das Gift.
>
> Und außerdem ist einer der Vorteile von C, daß es einen Haufen
> Abstraktionen nicht bietet und man, wie Torvalds schon anmerkte, damit
> auch Leute draußen hält, die sich mehr daran hochziehen als an der
> eigentlichen Aufgabe. Ich finde, dieser Thread bestätigt Torvalds' Rant
> recht beeindruckend.

Was willst du eigentlich ständig mit Torvalds? Gibt es überhaupt 
irgendwas auf der Welt worüber Linus keinen Rant starten kann? Das er 
Linux initiiert hat und ein führender Entwickler ist, schließt nicht 
aus, dass er Schwachsinn über C++ schreiben kann.

Nop schrieb:
> PeterPan schrieb:
>> auf nem ARM Cortex muss man
>> sich sowas nicht antun, da ist der ROM im gleichen Adressraum.
>
> Es kann aber verschiedene Sorten disjunktes RAM geben. SRAM, CCM,
> Backup-RAM. Da muß man schon entscheiden, welche Variablen wohin gehen
> sollen.

Das regle ich über mein Linkerscript und den dazugehörigen Sections.

Diese ganze Dogmatik erinnert mich an einen Labview Anhänger, der mir 
weiß machen wollte, dass Grafische Programmierung generell den 
Textsprachen überlegen ist. Dass C Programmieren das schlimmste auf der 
sei, dass man C ja sowas von gar nicht debuggen kann und allerhand 
anderen Schwachsinn. Wobei er nie mit C programmiert hat oder mit 
irgendeiner anderen textbasierte Programmiersprache...

Beitrag #5040893 wurde von einem Moderator gelöscht.
von Vincent H. (vinci)


Lesenswert?

Nachdem wir nun endlich geklärt haben, dass C in allen Belangen besser 
ist würde mich interessieren, wie man hier im Forum eigentlich zu Rust 
und Konsorten steht?

Rust bietet ja quasi die Möglichkeit zur Abstraktion ohne eigentliche 
Klassen? Ich würde jemanden bitten mir hier meine Meinung zu justieren, 
ich weiß nämlich nicht ob ich Rust jetzt schlecht finden muss?

von Carl D. (jcw2)


Lesenswert?

Vincent H. schrieb:
> Nachdem wir nun endlich geklärt haben, dass C in allen Belangen besser
> ist würde mich interessieren, wie man hier im Forum eigentlich zu Rust
> und Konsorten steht?
>
> Rust bietet ja quasi die Möglichkeit zur Abstraktion ohne eigentliche
> Klassen? Ich würde jemanden bitten mir hier meine Meinung zu justieren,
> ich weiß nämlich nicht ob ich Rust jetzt schlecht finden muss?

Du mußt. Es ist !C.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Jörg W. schrieb:
> Wilhelm M. schrieb:
>> Nur __flash, __flash1, ... als spezieller qualifier für
>> named-address-spaces eben nicht, weil das eben non-standard
>> Erweiterungen sind
>
> … die man dort offenbar aber nicht haben will.  Ansonsten hätte ja
> nichts dagegen gesprochen, dass Johann sie auch für C++ aktiviert.

Da ist nix mit "aktivieren".  Qualifier sind eine Erweiterung des 
C-Fronts und des GCC Middle-Ends, und das AVR-Backend implementiert 
einige extra Qualifier.

Aber das C++-Front anzupassen (geschweige denn die Spez dahingehend zu 
erweitern), ist noch mal ne gan zandere Geschichte.  Wenn man sich alle 
Abartigkeiten von C++ zusammen vorstellt, bekommt man ne Idee, das sowas 
in einem Compiler FrontEnd nicht so ganz trivial umzusetzen (und zu 
testen) ist.

Was die Spez angeht:

> "WG21 has been burnt by volatile"

hieß es mal in einem ML-Thread zu Thema:  WG21 ist, was Qualifier 
angeht, ein gebranntes Kind und wird den Teufel tun, Qualifier nochmals 
anzufassen.  Zumal ISO/IEC DTR 18037 auch "Embedded-C" heißt, und das 
ist deutlich unter dem Radar des momentanen Entwicklungsfokus, wo es 
z.B. darum geht, wie man ISAs mit SIMD mit 4x double effizient 
parallelisiert...

Dabei geht DTR 18037 weit über das vom GCC angebotene 
Qualifier-Interface hinaus, indem es sogar User-defined Qualifier 
einführt (nicht von GCC implementier), die ermöglichen, neue Qualifier 
auf Applikationsebene einzuführen.  Aber wie gesagt: Das Radar...

Außerdem greift das non-standard Argument auch nicht wirklich.  Oft 
waren Spracherweiterungen wie GNU-C oder GNU-C++ Vorreiter von 
Erweiterungen des Standards, die Praxistauglichkeit demonstrierten. 
Erweiterungen am grünen Tisch zu entwerfen ist nicht immer so glücklich, 
wie die Diskussion um atomic zeigt, und ein großer Teil von 
Embedded-C kann man getrost in die Tonne treten da unbrauchbar (obwohl 
sie von einem WG14 Mitglied stammen!).

Inzwischen gibt es aber eher ein Henne-Ei Problem: Die Compilerbauer 
wollen nicht mehr gerne nichtstandard Erweiterungen einbauen, weil die 
oft problematisch sind (Kompatibilität, Wartung, Stabilität), und was in 
den Standard zu bringen "Out of thin Air" ist noch aussichtsloser.

Ich verstehe aber immer noch nicht, warum __flash in Embedded-C ein 
Argument gegen C und für C++ sein soll.

von PeterPan (Gast)


Lesenswert?

Vincent H. schrieb:
> Nachdem wir nun endlich geklärt haben, dass C in allen Belangen besser
> ist würde mich interessieren, wie man hier im Forum eigentlich zu Rust
> und Konsorten steht?

Um Gottes Willen, wenn hier viele schon mit dem alten, etablierten und 
bewährten C++ überfordert sind, was glaubst du ist erst bei einer 
neuartigen und innovativen (Ownershipsystem z.B.) Sprache los ist, die 
dazu auch noch die gleiche Mächtigkeit anstrebt. Vor allem wenn sich 
hier schon so wenige mit C++ auskennen, dann wird sich mit Rust wohl 
kaum schon einer beschäftigt haben (aber eine Meinung haben sie ganz 
sicher schon ;) ).

Vincent H. schrieb:
> Rust bietet ja quasi die Möglichkeit zur Abstraktion ohne eigentliche
> Klassen? Ich würde jemanden bitten mir hier meine Meinung zu justieren,
> ich weiß nämlich nicht ob ich Rust jetzt schlecht finden muss?

Die structs sind doch quasi Klassen. Das OOP funktioniert dort ein 
bisschen anders (man könnte auch sagen, dass es 
reduzierter/vereinfachter ist), so ähnlich wie bei Go. Vererbung gibts 
an sich nicht, aber Traits (also Interfaces wie in C#/Java) und 
natürlich Komposition, womit sich sowas wie Vererbung nachbilden lässt. 
Mein erster Eindruck ist sehr gut, aber es ist keine Sprache für 
Anfänger sondern eher was für Leute, die viele Dinge an C++ zu schätzen 
wissen, aber mit ein paar Dingen nicht zu frieden sind.

von Sheeva P. (sheevaplug)


Lesenswert?

Jörg W. schrieb:
> Namend memory spaces sind Bestandteil eines Standardvorschlags für
> Embedded C.  Johann wollte darauf hinaus, dass dieser Vorschlag jedoch
> auf einem standardmäßig bei C vorhandenen Feature basiert, den type
> qualifiers, die es bei C++ nicht gibt.

Wenn ich mich recht entsinne, kennt C die Typqualifizierer "const" und 
"volatile" erst seit C89 und hat sie lustigerweise ausgerechnet von C++ 
übernommen... correct me if I'm wrong. Zudem kommt IIRC __flash vom IAR, 
wurde von Atmel für den GCC übernommen, und ist bisher nur ein 
Vorschlag, aber eben kein Standard.

von Sheeva P. (sheevaplug)


Lesenswert?

PeterPan schrieb:
> Nop schrieb:
>> [...]
> Was willst du eigentlich ständig mit Torvalds?

Der Linus ist eben dem Nop sein heiliger Gott, der immer Recht hat und 
sich noch niemals nie nicht geirrt hat. Und weil unserem Nop hier leider 
gerade die eigenen Argumente ausgegangen sind (so er denn überhaupt 
jemals welche gehabt haben sollte) und der Linux mal was gesagt hat, das 
dem Nop gefällt und ihm gerade in den "argumentativen" Kram paßt, darum 
reitet der Nop hier immer wieder auf dem Linus und seinen Aussagen 
herum, daß der arme Linus schon ganz wund dabei wird.

> Gibt es überhaupt irgendwas auf der Welt worüber Linus keinen Rant
> starten kann?

Ja, Tove. ;-)

von Oliver J. (skriptkiddy)


Lesenswert?

@nop:

Mit dem was Du da von Dir gibst sagst Du, dass meine Kollegen und ich 
unsere Arbeit nicht richtig machen, weil wir diese Unsprache C++ 
hernehmen und uns damit sicher verkünsteln. Ich glaube Du kannst, wie es 
Dir hier viele schon nahe gelegt haben, C++ schlicht weg nicht 
ordentlich beurteilen, weil Du es einfach nicht richtig kennst. Somit 
ist dein Vermögen darüber qualifizierte Kommentare abzugeben nicht 
sonderlich groß. Sorry für die Direktheit, aber es ist nun mal so und Du 
willst es einfach nicht wahr haben.

@all:
Ihr hättest das hier mal lesen sollen, bevor ichs selbst zensiert hab :D
Wär ja schade drum wenn die Kernaussage des Posts nicht stehen bleibt.

Grüße Oliver

: Bearbeitet durch User
Beitrag #5041032 wurde von einem Moderator gelöscht.
von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Sheeva P. schrieb:
> Zudem kommt IIRC __flash vom IAR,

Nicht nur, auch viele andere Compiler handhaben das so.

Daher ja auch der Standardvorschlag.  Solche Dinge werden ja nicht von
den Normungsgremien erfunden, sondern sie bemühen sich in erster Linie,
gängige Praxis zu standardisieren.

> wurde von Atmel für den GCC
> übernommen,

Atmel?  Die haben damit nichts zu tun.  Das ist Johanns Werk.

> und ist bisher nur ein Vorschlag, aber eben kein Standard.

Besser als nichts.  Insbesondere eben besser als das progmem-Attribut.

Warum man named memory spaces im Embedded-Bereich gebrauchen kann,
wurde ja schon genannt.  Selbst wenn sie alle über einen flat address
space zugegriffen werden können, kann es halt notwendig sein, dass
der Programmierer verschiedene Speichersegmente selbst beim RAM explizit
auswählen möchte.  Ohne named memory spaces geht das nur über völlig
proprietäre Lösungen, Pragmas, Attribute etc. pp.

Beitrag #5041043 wurde von einem Moderator gelöscht.
Beitrag #5041053 wurde von einem Moderator gelöscht.
von W.S. (Gast)


Lesenswert?

Sheeva P. schrieb:
>> Also mal ganz vulgär:
>> nicht "SetzePin(Port,Nummer,Zustand);"
>> sondern "SchalteMotorEin();"
>
> ...oder eben: "motor.einschalten();". Das läßt sich mit einfacher
> Vererbung ganz einfach, elegant und kostenlos umsetzen. Das bedarf nur
> einer simplen Elternklasse, die einen Pin abstrahiert -- und ist dann
> mit einer anderen Elternklasse, die dasselbe Interface auf einer anderen
> Controllerfamilie implementiert, sogar portabel.

Nein, du fährst damit in die falsche Richtung.

Für den Motor kann man kein Objekt gebrauchen, denn er läßt sich nicht 
ableiten (wovon denn auch!) - und Vererbung hat auch keinen Sinn, denn 
der Motor (oder der Schütz oder die Lampe) hat eben auch nichts zu 
vererben (an wen denn auch!).

Objektorientierte Programmierung ist eine Domäne von äußerlich gleichen, 
aber innerlich verschiedenen Dingen, also von einem Vorfahren 
abgeleitete Objekte wie z.B. grafische Elemente auf dem Bildschirm. Aber 
das gilt ausdrücklich NICHT für Hardware. Die ist immer ein Unikat.

Abgesehen davon denkst du schon wieder an etwas in der Art von vererbtem 
und abstrahierten Pin - das ist falsch, weil einschränkend. Sowas wie 
"SchalteMotorEin() kann auch das Aufsetzen eines Timermoduls sein, wenn 
es sich z.B. um einen Schrittmotor handelt. Es kann auch ein I2C Zugriff 
sein, wenn es ein intelligenter Stellmotor ist. Natürlich kann es auch 
ein simpler Schütz sein, der den Motor erstmal in Stern anlaufen läßt um 
dann nach einiger Zeit auf Dreieck umgeschaltet zu werden. Du siehst, 
daß sich sowas einfach NICHT oder nur mit sinnlos aufgesetztem Krampf in 
eine Objakthierarchie fassen läßt.

Also, denke an "KISS" und laß als sinnvollste Schnittstelle die simple 
Aktion "SchalteMotorEin()" einfach stehen. Das ist so ziemlich die 
sinnvollste Abstraktion in diesem Falle.

W.S.

von Oliver J. (skriptkiddy)


Lesenswert?

W.S. schrieb:
> Objektorientierte Programmierung ist eine Domäne von äußerlich gleichen,
> aber innerlich verschiedenen Dingen, also von einem Vorfahren
> abgeleitete Objekte wie z.B. grafische Elemente auf dem Bildschirm. Aber
> das gilt ausdrücklich NICHT für Hardware. Die ist immer ein Unikat.

Objektorientierung geht auch ohne Vererbung. Ein Objekt kann auch 
einfach nur zusammengehörige Sachen sehr schön kapseln. Man kann dann 
Abhängigkeiten bei Konstruieren mit reingeben (Composite-Pattern), was 
UnitTests erheblich erleichtert. Da kommt mir persönlich das was die 
Sprache C++ bietet sehr entgegen. Es setzt aber vorraus, dass man sich 
darauf einlässt beim Softwareentwurf in Objekten zu denken, sonst macht 
das natürlich wenig Sinn.

Grüße Oliver

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


Lesenswert?

W.S. schrieb:
> Objektorientierte Programmierung ist eine Domäne von äußerlich gleichen,
> aber innerlich verschiedenen Dingen

… also beispielsweise einem Dutzend Motoren eines Roboters? ;-)

: Bearbeitet durch Moderator
Beitrag #5041100 wurde von einem Moderator gelöscht.
Beitrag #5041101 wurde von einem Moderator gelöscht.
Beitrag #5041105 wurde von einem Moderator gelöscht.
Beitrag #5041111 wurde von einem Moderator gelöscht.
Beitrag #5041113 wurde von einem Moderator gelöscht.
Beitrag #5041123 wurde von einem Moderator gelöscht.
Beitrag #5041134 wurde von einem Moderator gelöscht.
Beitrag #5041148 wurde von einem Moderator gelöscht.
Beitrag #5041154 wurde von einem Moderator gelöscht.
von Sheeva P. (sheevaplug)


Lesenswert?

Oliver J. schrieb:
> Ihr hättest das hier mal lesen sollen, bevor ichs selbst zensiert hab :D
> Wär ja schade drum wenn die Kernaussage des Posts nicht stehen bleibt.

Bitte, reg' Dich nicht auf. Ich weiß, angesichts der Ignoranz und der 
fast schon gebetsmühlenartig immer wieder vorgebrachten Vorurteile, die 
darüber hinaus aus Unkenntnis noch nicht einmal die eigenen sind, ist 
das manchmal leichter gesagt als getan.

Aber nimm' es mit Humor: Du darfst und kannst die Vorzüge von C++ 
genießen, und er eben nicht. Selbstgewähltes Schicksal, nennt man das 
wohl. ;-)

von christopher robin (Gast)


Lesenswert?

Ich kann nur sagen keine Angst vor C++, jeder der C++ kann, kann auch C 
und teilweise auch umgekehrt.
Ein guter Programmierer hat keine Angst vor verschiedenen 
Programmiersprachen, er kann sogar viele davon anwenden. Fürs Hobby 
lohnt sich der Aufwand freilich nicht viele Sprachen zu lernen.

Wer kleine überschaubare Programme schreibt der nehme C, wer mehr will, 
nehme C++ aber verwende nicht die allzu verrückten Sachen, ich denke, 
dass man bei Klassen einen guten Schlussstrich machen kann.

Beitrag #5041164 wurde von einem Moderator gelöscht.
von Sheeva P. (sheevaplug)


Lesenswert?

Jörg W. schrieb:
> Sheeva P. schrieb:
>
>> wurde von Atmel für den GCC
>> übernommen,
>
> Atmel?  Die haben damit nichts zu tun.  Das ist Johanns Werk.

Wenn Du das sagst, wird das so sein. Ich hatte diese Seite [1] 
fälschlich so interpretiert, daß Atmel das Verdienst beansprucht.

Entschuldige, Johann: ich wollte Deine hervorragenden Leistungen und 
Verdienste um den AVR-GCC nicht schmälern.

>> und ist bisher nur ein Vorschlag, aber eben kein Standard.
>
> Besser als nichts.

Ja, natürlich, keine Frage. Aber ein nützliches, dennoch aber (noch) 
nicht einmal standardisiertes einzelnes Feature einer Sprache als 
Argument gegen eine andere Sprache zu verwenden, während andererseits 
besonders wichtige, standardisierte Features wie etwa die wesentlich 
bessere Typsicherheit der anderen Sprache komplett ignoriert werden... 
verzeih', aber das erscheint mir weder seriös noch objektiv.

[1] 
http://www.atmel.com/webdoc/avrlibcreferencemanual/porting_1iar_porting_flash.html

Beitrag #5041174 wurde von einem Moderator gelöscht.
Beitrag #5041175 wurde von einem Moderator gelöscht.
von Oliver J. (skriptkiddy)


Lesenswert?

Sheeva P. schrieb:
> Aber nimm' es mit Humor: Du darfst und kannst die Vorzüge von C++
> genießen, und er eben nicht. Selbstgewähltes Schicksal, nennt man das
> wohl. ;-)

Da hast Recht ;)

Grüße Oliver

Beitrag #5041183 wurde von einem Moderator gelöscht.
Beitrag #5041188 wurde von einem Moderator gelöscht.
Beitrag #5041189 wurde von einem Moderator gelöscht.
Beitrag #5041191 wurde von einem Moderator gelöscht.
von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Sheeva P. schrieb:
> Ich hatte diese Seite [1] fälschlich so interpretiert, daß Atmel das
> Verdienst beansprucht.

Das ist weiter nichts als die avr-libc-Dokumentation, was sie da
wiedergeben.

Dass sie da ein “© 2016 Atmel Corporation. All rights reserved”
drüberknallen, ist eine mittelmäßige Frechheit.  Da sie sich außerdem
nicht an die Copyright-Bedingungen der avr-libc halten, werde ich
(als einer der Autoren dieser Dokumentation) ihnen mal ein paar Zeilen
schreiben müssen …

Das von dir zitierte Kapitel könnte in der Tat mal einen Update
gebrauchen, um auf das mittlerweile vorhandene __flash im AVR-GCC
hinzuweisen.

von Sheeva P. (sheevaplug)


Lesenswert?

W.S. schrieb:
> Sheeva P. schrieb:
>>> Also mal ganz vulgär:
>>> nicht "SetzePin(Port,Nummer,Zustand);"
>>> sondern "SchalteMotorEin();"
>>
>> ...oder eben: "motor.einschalten();". Das läßt sich mit einfacher
>> Vererbung ganz einfach, elegant und kostenlos umsetzen. Das bedarf nur
>> einer simplen Elternklasse, die einen Pin abstrahiert -- und ist dann
>> mit einer anderen Elternklasse, die dasselbe Interface auf einer anderen
>> Controllerfamilie implementiert, sogar portabel.
>
> Nein, du fährst damit in die falsche Richtung.

Ich fürchte, daß Du hier gerade der Geisterfahrer bist.

> Für den Motor kann man kein Objekt gebrauchen, denn er läßt sich nicht
> ableiten (wovon denn auch!) - und Vererbung hat auch keinen Sinn, denn
> der Motor (oder der Schütz oder die Lampe) hat eben auch nichts zu
> vererben (an wen denn auch!).

Zunächst einmal wird der Motor (zumindest Dein vereinfachtes Modell, das 
nur die Zustände "ein" und "aus" kennt) aus Sicht des Mikrocontrollers 
über einen GPO-Pin angesteuert. Für den Mikrocontroller ist Dein Motor 
darum nur ein GPO-Pin, der die Zustände "an" und "aus" hat und dessen 
aktueller Zustand nur über die Operationen (OO-Slang: Methoden) 
"einschalten" und "ausschalten" verändert werden kann.

An dieser Stelle kommen bereits zwei wichtige Eigenschaften der OOP zum 
Tragen. Datenkapselung heißt an dieser Stelle, daß der Zustand des 
Motors nur über die Operationen (im OO-Slang manchmal auch Nachrichten 
genannt) "einschalten" und "ausschalten" verändert werden kann. Und die 
Vererbung heißt hier, daß der Motor in Wirklichkeit nur ein GPO-Pin ist, 
in der OO wird ein solcher Beziehungstyp als "is-a" bezeichnet. Genau 
dasselbe gilt auch für das Fahrlicht: dieselben Zustände, dieselben 
Operationen, aus Sicht des Mikrocontrollers im Prinzip dasselbe Ding: 
ein GPO-Pin.

Dazu existieren in meinem objektorientierten Fahrzeug auch 
Bedienelemente, etwa die Zündung und der Schalter für das Fahrlicht mit 
den Zuständen "ein" und "aus", letzten Endes wieder mit "is-a" 
Beziehungen zu je einem GPI-Pin. Deswegen kann ich in meiner 
Steuerungssoftware also etwas schreiben wie:
1
int main(void) {
2
  while(true) {
3
    if( zuendung.ist_an() ) {
4
      motor.einschalten();
5
    } else {
6
      motor.ausschalten();
7
    }
8
  }  
9
}

Was dieser Code tut, das verstehen Du, ich, meine Ehefrau und sogar 
meine zwölfjährige Zicke von Nichte auf den ersten Blick, weil der Code 
so schön deskriptiv ist und die bekannten Objekte der realen Welt 
nachbildet -- und Du wirst zurecht einwenden, daß das sehr ähnlich ja 
auch ohne OO geht.

Aber bis hierher ist das ja nur Objektorientierung für Arme, um den Code 
etwas kompakter und lesbarer zu machen. Modellieren wir die Sache doch 
mal richtig: jetzt empfängt unser Motor, der immer noch nur ein GPO-Pin 
mit den Zuständen "hi" und "lo" ist, eine Nachricht vom Typ Zündung, und 
bietet dafür die Methode schalten(Zuendung z):
1
int main(void) {
2
  while(true) {
3
    motor.schalten(zuendung);
4
  }
5
}

Ok, die Methoden sind blöd benannt, aber darum geht es gar nicht. Es 
geht vielmehr darum, daß unser Motor nur und ausschließlich mit der 
Zündung ein- und ausgeschaltet werden und sich darauf verlassen kann, 
daß die Zündung die passende Methode "ist_an" zur Ermittlung ihres 
Zustandes bietet. Das freut Dich spätestens dann, wenn Du mit Deinem 
Fahrzeug an einer Wettfahrt teilnehmen willst und dann noch einen 
Notaus-Knopf anbringen und in die Steuerung einbauen mußt: dann mußt Du 
nämlich nur die Klasse Zündung um Deinen Notaus-Knopf erweitern, aber 
der Motor und der gesamte Rest des Fahrzeuges bleibt komplett unberührt: 
"ist_an()" gibt eben nur dann true zurück, wenn a) die Zündung 
eingeschaltet und b) der Notausschalter nicht gedrückt worden ist.

Nun, machen wir uns nichts vor: diese winzigen Beispiele sind zu klein 
und zu schnell aus der Hand geschüttelt, um die Stärken der OO zu 
zeigen. Aber sie sollen Dir ja auch nur eine kleine Idee davon geben.

> Objektorientierte Programmierung ist eine Domäne von äußerlich gleichen,
> aber innerlich verschiedenen Dingen, also von einem Vorfahren
> abgeleitete Objekte wie z.B. grafische Elemente auf dem Bildschirm.

Das ist zwar richtig, aber auch falsch. Im vorherigen zweiten Beispiel 
"weiß" unser Motor plötzlich, wie bzw. womit er ein- und ausgeschaltet 
wird, und kann den Zustand der Zündung daher selbständig abfragen. Er 
ist nicht mehr darauf angewiesen, daß wir ihm explizit sagen, was er tun 
soll, sondern er "weiß" es selbst.

> Aber
> das gilt ausdrücklich NICHT für Hardware. Die ist immer ein Unikat.

Ich weiß nicht, was Du entwickelst, aber schon auf kleineren AVRs gibt 
es häufig mehrere U(S)ARTs, die also keineswegs Unikate sind, sondern 
Objekte mit (mehr oder weniger) denselben Eigenschaften. Ganz verwegene 
Entwickler lassen sogar unterschiedliche Protokolle über die 
verschiedenen U(S)ARTs ein- und desselben Mikrocontrollers laufen -- 
aber trotzdem sind es immer verschiedene U(S)ARTs.

> Abgesehen davon denkst du schon wieder an etwas in der Art von vererbtem
> und abstrahierten Pin - das ist falsch, weil einschränkend. Sowas wie
> "SchalteMotorEin() kann auch das Aufsetzen eines Timermoduls sein, wenn
> es sich z.B. um einen Schrittmotor handelt. Es kann auch ein I2C Zugriff
> sein, wenn es ein intelligenter Stellmotor ist. Natürlich kann es auch
> ein simpler Schütz sein, der den Motor erstmal in Stern anlaufen läßt um
> dann nach einiger Zeit auf Dreieck umgeschaltet zu werden.

Ja, natürlich kann das alles sein. Aber wo steht, daß eine Klasse Motor 
zwingend von einem GPO-Pin erben muß? "motor.einschalten()" kann genau 
dasselbe, was Deine Funktion auch kann: einen Timer aufsetzen, einen 
I2C-Zugriff auslösen oder ein Schütz schalten. Letztlich ist die Methode 
"einschalten()" ja auch nur eine Funktion und die Klasse "Motor" eine 
zustandsbehaftete Datenstruktur mit Methoden zur Änderung des Zustands.

> Du siehst,
> daß sich sowas einfach NICHT oder nur mit sinnlos aufgesetztem Krampf in
> eine Objakthierarchie fassen läßt.

Nein, das sehe ich nicht. Was ich sehe, ist klar verständlicher Code, 
der hohe Typsicherheit mit guter Les- und Erweiterbarkeit verbindet. Wo 
da ein "Krampf" sein soll, obendrein ein "sinnloser", erschließt sich 
mir nicht.

> Also, denke an "KISS" und laß als sinnvollste Schnittstelle die simple
> Aktion "SchalteMotorEin()" einfach stehen. Das ist so ziemlich die
> sinnvollste Abstraktion in diesem Falle.

Das sehe ich immer noch nicht so, tut mir leid.

Beitrag #5041241 wurde von einem Moderator gelöscht.
Beitrag #5041243 wurde von einem Moderator gelöscht.
von X4Ubbb (Gast)


Lesenswert?

Sheeva P. schrieb:
> int main(void) {
>   while(true) {
>     motor.schalten(zuendung);
>   }
> }
>
> Ok, die Methoden sind blöd benannt, aber darum geht es gar nicht. Es
> geht vielmehr darum, daß unser Motor nur und ausschließlich mit der
> Zündung ein- und ausgeschaltet werden und sich darauf verlassen kann,
> daß die Zündung die passende Methode "ist_an" zur Ermittlung ihres
> Zustandes bietet.

Das verstehe ich als lernwilliger aber C++ ahnungsloser wie folgt.

Für einen C++ Progger ist hier ein Objekt mit ner Eigenschaft und ner 
Anweisung. Vermute ich mal. Das ganze noch gekapselt, wie auch immer das 
jetzt funzen mag.

In C würde das ähnlich aussehen. Die Funktion hält die Statis local 
(static), die ganze Steuerung läuft über function calls. Wer dann 
unbedingt noch motor.schalten haben will macht das per struct.

Nicht ganz so hübsch zu schreiben, dafür spart man sich eine Menge 
undurchsichtiger Zusammenhänge. In der Regel hat man noch den 
Schaltplan, die Mechanik, Thermales, dynamic currents, EMI uswusf, zu 
beachten. Da ist die schöne OOP Toolchain eher akdemisch.

Auch steigt man  bei C (um doch nochmal die Abstraktionsebenen von 
weiter oben zu bemühen) da wesentlich besser durch als mit Assembler.

Ein Problem lösen aber weder C noch C++.

Wenn irgendein depp (üblicherweise der Autor) die Ports über andere 
Routinen toggelt und dabei statis ignoriert bzw falsch setzt schießt er 
die schöne Steuerung nebst Motor. Ohne das ich was machen kann. Wie 
auch? Weder µP-Hardware noch der Compiler können das verhindern.

Das geht nur in Hardware, Thema Eigensicherheit (die zu der Liste der 
Aufgaben noch dazu kommt).

Fazit bisher: OOP auf'm uC ist ein Randproblem.

von Vincent H. (vinci)


Lesenswert?

X4Ubbb schrieb:
> Wenn irgendein depp (üblicherweise der Autor) die Ports über andere
> Routinen toggelt und dabei statis ignoriert bzw falsch setzt schießt er
> die schöne Steuerung nebst Motor. Ohne das ich was machen kann. Wie
> auch? Weder µP-Hardware noch der Compiler können das verhindern.
>
> Das geht nur in Hardware, Thema Eigensicherheit (die zu der Liste der
> Aufgaben noch dazu kommt).
>
> Fazit bisher: OOP auf'm uC ist ein Randproblem.

Genau aus dem Grund haben Klassen private und public Member. Ein Motor 
Pin ist ein internes Detail des Motors und damit von außen nicht 
zugreifbar.

von Wilhelm M. (wimalopaan)


Lesenswert?

X4Ubbb schrieb:

> Wenn irgendein depp (üblicherweise der Autor) die Ports über andere
> Routinen toggelt und dabei statis ignoriert bzw falsch setzt schießt er
> die schöne Steuerung nebst Motor. Ohne das ich was machen kann. Wie
> auch? Weder µP-Hardware noch der Compiler können das verhindern.

Der Compiler zwar nicht allein, aber mit (etwas mehr) Template-Mechanik 
bekommt man auch das in den Griff.

> Das geht nur in Hardware, Thema Eigensicherheit (die zu der Liste der
> Aufgaben noch dazu kommt).
>
> Fazit bisher: OOP auf'm uC ist ein Randproblem.

Entwickelt sich das jetzt hier zu einem Grundkurs in OOD?

von Wilhelm M. (wimalopaan)


Lesenswert?

X4Ubbb schrieb:

>
> Nicht ganz so hübsch zu schreiben, dafür spart man sich eine Menge
> undurchsichtiger Zusammenhänge. In der Regel hat man noch den
> Schaltplan, die Mechanik, Thermales, dynamic currents, EMI uswusf, zu
> beachten. Da ist die schöne OOP Toolchain eher akdemisch.
>
> Auch steigt man  bei C (um doch nochmal die Abstraktionsebenen von
> weiter oben zu bemühen) da wesentlich besser durch als mit Assembler.

Ich weiß nicht, was dieses ganze Gerede um zu starke Abstraktion soll. 
Wenn ich schreibe
1
std::iota(std::begin(c), std::end(c), 42);

weiß ich, dass danach der Container c mit einer Folge von ganzen Zahlen 
beginnend mit 42 gefüllt ist.

Ich muss weder wissen, WAS es für ein Container ist, etwa eine rohes 
Array, ein std::array, ein std::vector, ein selbst-geschriebener ... Das 
ist ein generischer Algorithmus, und der tut, was er soll. Da WILL ich 
doch gar nicht hinein sehen, wie std::iota<>() realisiert ist.

: Bearbeitet durch User
von Johannes S. (Gast)


Lesenswert?

X4Ubbb schrieb:
> Fazit bisher: OOP auf'm uC ist ein Randproblem.

Ja, aus Sicht des E-Technikers für den Software ein nötiges Übel ist. 
Und so scheinen viele Geräte programmiert zu sein. Gruseliger C Code den 
nur der Autor versteht und wenn das Projekt vererbt wird muss der 
nächste erstmal alles neu machen.

X4Ubbb schrieb:
> In C würde das ähnlich aussehen. Die Funktion hält die Statis local
> (static), die ganze Steuerung läuft über function calls. Wer dann
> unbedingt noch motor.schalten haben will macht das per struct.

Mit solchen Lösungen hatte ich früher reichlich zu tun und das waren die 
häufigsten Fehlerquellen weil der 'Progger' (kindische Bezeichnung) zu 
faul war Indizes oder Pointer zu überprüfen was bei dieser Lösung nötig 
ist. Bei C++ geht man vom Design her anders ran weil die Sprache es 
hergibt und man nicht mit Compiler Klimmzügen Sicherheit reinbringen 
muss.

Beim Beispiel mit dem Motor brauche ich keine Abstraktion, der motor ist 
ein DigitalOut (im einfachen Fall) und ein 'motor = 1' schaltet den ein. 
So ein Motor ist in der realen embedded Welt aber nicht so einsam und 
gehört zu einer Baugruppe die einen Namen und Funktionen hat und 
wunderbar als Objekt abgebildet werden kann, zB eine Antriebsachse mit 
Endschaltern. Das packt man mit den nötigen Funktionen und internen 
Zuständen in eine Klasse. Und wenn man mehrere Achsen hat sind es ganz 
einfach mehrere Instanzen.
Die Ersatzlösung in C die das nachbilden möchte muss jetzt ein Argument 
für die Achsenauswahl in jede Funktion bekommen. Ein einfacher 
numerischer Index ist kein sicherer Typ und man kann nur den 
Wertebereich auf Gültigkeit prüfen. Pointer oder Handles sind 
aufwändiger und evtl. noch gefährlicher bei falscher Benutzung.

Alleine diese Kapselung ist für mich Grund genug lieber mit C++ zu 
arbeiten, auch und gerade auf µCs weil sich die Komponenten gut in 
Objekten abbilden lassen. Und solange ich eine konrete Anwendung habe 
brauche ich keine wilden Abstraktionen.

von Oliver J. (skriptkiddy)


Lesenswert?

Vincent H. schrieb:
> X4Ubbb schrieb:
>> Wenn irgendein depp (üblicherweise der Autor) die Ports über andere
>> Routinen toggelt und dabei statis ignoriert bzw falsch setzt schießt er
>> die schöne Steuerung nebst Motor. Ohne das ich was machen kann. Wie
>> auch? Weder µP-Hardware noch der Compiler können das verhindern.
>>
>> Das geht nur in Hardware, Thema Eigensicherheit (die zu der Liste der
>> Aufgaben noch dazu kommt).
>>
>> Fazit bisher: OOP auf'm uC ist ein Randproblem.
>
> Genau aus dem Grund haben Klassen private und public Member. Ein Motor
> Pin ist ein internes Detail des Motors und damit von außen nicht
> zugreifbar.
Leider ist das der Peripherie des µC egal. Der µC führt ja bloß den 
Binärcode aus, der auf Register zugreift. Wenn also ein Timer von 
anderer Stelle so konfiguriert wird, dass er ne PWM auf dem selben Pin 
aktiviert oder ein UART dahinter angeschaltet wird, dann hat man 
konkurrierenden zugriff auf den Pin. Davor schützen weder Klassen noch 
Compiler, da die nichts von der "Verdrahtung" der Register mit der 
Hardware wissen. Es könnten auch zwei Klassen den selben Pin als GPIO 
verwenden. Das macht im Normalfall natürlich keiner, aber wenn sich der 
Fehlerteufel einschleicht, kann sowas schon mal passieren. Icdh denke in 
diese Richtung hat das abgezielt.


X4Ubbb schrieb:
> Nicht ganz so hübsch zu schreiben, dafür spart man sich eine Menge
> undurchsichtiger Zusammenhänge.
Für wen undurchsichtig? Wahrscheinlich für jemanden ohne C++ Erfahrung.


X4Ubbb schrieb:
> Fazit bisher: OOP auf'm uC ist ein Randproblem.
Als Problem würde ich das eher nicht sehen. Es ist eher bei weitem noch 
nicht so oft vertreten wie klassische Ansatz, aber das kann sich ja noch 
ändern.


Wilhelm M. schrieb:
> std::iota(std::begin(c), std::end(c), 42);
Geht das wirklich bei avr mit Standardmitteln?


Grüße Oliver

: Bearbeitet durch User
von Wilhelm M. (wimalopaan)


Lesenswert?

Oliver J. schrieb:

>
> Wilhelm M. schrieb:
>> std::iota(std::begin(c), std::end(c), 42);
> Geht das wirklich bei avr mit Standardmitteln?

Selbstverständlich.
Was sollte das auch mit C++ als Sprache zu tun haben?

Allerdings ist beim avr-g++ die Libstdc++ nicht dabei (aus gutem Grund 
;-).
Aber man kann natürlich die benötigten templates wie std::iota und 
std::begin oder std::end oder auch std::array sich gerade selbst 
schreiben (oder aus der Libstdc++ abkupfern, wenn man das nicht 
will/kann). std::vector vllt eher nicht, da man hier dann dyn. 
Allokation braucht.

von Oliver S. (oliverso)


Lesenswert?

Wilhelm M. schrieb:
> Selbstverständlich.
> ..
> Aber man kann natürlich die benötigten templates wie std::iota und
> std::begin oder std::end oder auch std::array sich gerade selbst
> schreiben ...

Man kann nicht, man muß. Was die Definition von "selbstverständlich" 
dann doch etwa relativiert.

Aber die Welt besteht zum Glück nicht nur aus AVRs.

Oliver

von Wilhelm M. (wimalopaan)


Lesenswert?

Oliver S. schrieb:
> Wilhelm M. schrieb:
>> Selbstverständlich.
>> ..
>> Aber man kann natürlich die benötigten templates wie std::iota und
>> std::begin oder std::end oder auch std::array sich gerade selbst
>> schreiben ...
>
> Man kann nicht, man muß. Was die Definition von "selbstverständlich"
> dann doch etwa relativiert.

... das wäre dann eine gute Fingerübung für die, die hier mitreden 
wollen ;-)

>
> Aber die Welt besteht zum Glück nicht nur aus AVRs.

Genau!

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Wilhelm M. schrieb:
> Allerdings ist beim avr-g++ die Libstdc++ nicht dabei (aus gutem Grund
> ;-)

Und was ist dieser Grund?

Du als C++ Hero wärst doch prädestiniert, den GCC so zu pimpen, dass er 
auch C++ unterstützt.

von Wilhelm M. (wimalopaan)


Lesenswert?

Johann L. schrieb:
> Wilhelm M. schrieb:
>> Allerdings ist beim avr-g++ die Libstdc++ nicht dabei (aus gutem Grund
>> ;-)
>
> Und was ist dieser Grund?
>
> Du als C++ Hero wärst doch prädestiniert, den GCC so zu pimpen, dass er
> auch C++ unterstützt.

Was meinst Du? Tut er doch, der avr-g++. Sogar z.B. __underlying_type 
funktioniert ...

Nur - wie gesagt - die dynamischen Container der Libstdc++ machen auf 
diesen kleinen µC wenig bis gar keinen Sinn. Auf anderen dagegen schon.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Wilhelm M. schrieb:
> Johann L. schrieb:
>> Wilhelm M. schrieb:
>>> Allerdings ist beim avr-g++ die Libstdc++ nicht dabei (aus gutem Grund
>>> ;-)
>>
>> Und was ist dieser Grund?
>>
>> Du als C++ Hero wärst doch prädestiniert, den GCC so zu pimpen, dass er
>> auch C++ unterstützt.
>
> Was meinst Du? Tut er doch, der avr-g++.

Nein, tut er nicht, denn zum C++ Standard gehören eben auch die 
Standard-Bibliotheken, nicht nur der Sprachkern.

Und den "guten Grund", warum dieser Teil von C++ nicht unterstützt wird, 
kenn ich auch nicht.

Beitrag #5041437 wurde von einem Moderator gelöscht.
Beitrag #5041444 wurde von einem Moderator gelöscht.
von Wilhelm M. (wimalopaan)


Lesenswert?

Johann L. schrieb:
> Wilhelm M. schrieb:
>> Johann L. schrieb:
>>> Wilhelm M. schrieb:
>>>> Allerdings ist beim avr-g++ die Libstdc++ nicht dabei (aus gutem Grund
>>>> ;-)
>>>
>>> Und was ist dieser Grund?
>>>
>>> Du als C++ Hero wärst doch prädestiniert, den GCC so zu pimpen, dass er
>>> auch C++ unterstützt.
>>
>> Was meinst Du? Tut er doch, der avr-g++.
>
> Nein, tut er nicht, denn zum C++ Standard gehören eben auch die
> Standard-Bibliotheken, nicht nur der Sprachkern.

Stimmt! Ich sprach aber oben von avr-g++ und nicht vom gesamten 
C++-Standard.

> Und den "guten Grund", warum dieser Teil von C++ nicht unterstützt wird,
> kenn ich auch nicht.

Der Grund ist m.E. zweiteilig:

1) Allgemeine, unbeschränkte dyn. Container machen auf so kleinen µCs 
keinen Sinn.

2) Mit 1) hätten wir dann nur eine "halbe" Libstdc++, was auch keinen 
Sinn macht.

Aber: mit Bibliotheken wie z.B. ETL ist diese halbe Libstdc++ ja 
verfügbar.

Beitrag #5041448 wurde von einem Moderator gelöscht.
Beitrag #5041460 wurde von einem Moderator gelöscht.
Beitrag #5041464 wurde von einem Moderator gelöscht.
Beitrag #5041511 wurde von einem Moderator gelöscht.
von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Wilhelm M. schrieb:
> Allerdings ist beim avr-g++ die Libstdc++ nicht dabei (aus gutem Grund
> ;-).

Es gibt keinen „guten Grund“, höchstens einen schlechten: es hat
sich lange Zeit kein Schwein drum gekümmert, das mal zum Laufen zu
bekommen.  Offensichtlich war der Leidensdruck für die C++-Nutzer nicht
sehr hoch, vollständige Sprachunterstützung auch aus Bibliothekssicht
zu haben.  Vor einiger Zeit gab es mal wieder eine Initiative, es
wurde auch bisschen was dafür in der regulären avr-libc angepasst,
aber seither ist es wieder ruhig geworden darum.  Keine Ahnung, wie
der aktuelle Stand da wirklich ist.

Ob nun dynamische Objekte Sinn haben oder nicht, braucht man nicht
aus Sicht der Sprache zu argumentieren: dynamische Probleme lassen
sich nur dynamisch lösen, für statische Probleme wiederum zieht man
sich das sowieso nicht rein.  Die C-Bibliothek hat ja auch ein malloc(),
das beißt keinen in die Wade, solange man es nicht benutzt …

von christopher robin (Gast)


Lesenswert?

Wieso wird hier über Abstraktion geschimpft bzw. solche Codebeispiele 
gegenüber gestellt. Jeder von euch nutzt doch C oder C++ oder sonst was 
zum Programmieren, das ist schon eine enorme Abstraktion im Vergleich 
zum Assembler. Von fertigen Bibliotheken will ich gar nicht anfangen zu 
reden und dennoch nutzt jeder sie gerne.

Sinn und Zweck einer Sprache muss es sein Dinge einfacher und 
effizienter zu machen und das tut C++ nun mal besser als C usw. Beide 
Sprachen sind aber nah genug an der Hardware weil es eben kompilierte 
Sprachen sind.

Die Tragweite der Abstraktion im Programm selbst hat man doch selbst in 
der
Hand, in C++ muss ich nicht zwingend Klassen verwenden, C++ bietet mir 
lediglich die Möglichkeit, ich kann aber 100% prozedural wie in C auch 
coden wenn ich will. Aber Klassen mit Vererbung, Nutzung gleicher 
Funktionsnamen und Variablen in verschiedenen Klassen ist schon eine 
tolle Sache um ein paar Dinge zu nennen.

Beitrag #5041552 wurde von einem Moderator gelöscht.
von Chris D. (myfairtux) (Moderator) Benutzerseite


Lesenswert?

christopher robin schrieb:
> Wieso wird hier über Abstraktion geschimpft bzw. solche Codebeispiele
> gegenüber gestellt. Jeder von euch nutzt doch C oder C++ oder sonst was
> zum Programmieren, das ist schon eine enorme Abstraktion im Vergleich
> zum Assembler. Von fertigen Bibliotheken will ich gar nicht anfangen zu
> reden und dennoch nutzt jeder sie gerne.

Ja. Man möchte nicht jedes Mal wieder eine neue Tastaturabfrage oder ein 
Protokoll neu aufbauen. Heute muss ich sagen: Gottseidank haben 
ich/wir damals die Hardware in unseren Bibliotheken abstrahiert. Nur so 
war der Umzug von AVR auf deutlich leistungsfähigere STM32 ohne größere 
Schmerzen möglich.

Fortschritt in der IT lebt davon, dass wir mehr und mehr abstrahieren - 
schon weil der Mensch gar nicht in der Lage ist, beliebige viele Dinge 
gleichzeitig zu überblicken.

> Die Tragweite der Abstraktion im Programm selbst hat man doch selbst in
> der
> Hand, in C++ muss ich nicht zwingend Klassen verwenden, C++ bietet mir
> lediglich die Möglichkeit, ich kann aber 100% prozedural wie in C auch
> coden wenn ich will. Aber Klassen mit Vererbung, Nutzung gleicher
> Funktionsnamen und Variablen in verschiedenen Klassen ist schon eine
> tolle Sache um ein paar Dinge zu nennen.

Das habe ich als Argument lange vermisst: C++ ist doch eine Obermenge 
von C.

C++ enthält ja nicht nur OOP-Komponenten, sondern eben auch andere 
schöne Dinge (Namespaces usw.), die man auch bei rein prozeduraler 
Programmierung nutzen kann.

von Wilhelm M. (wimalopaan)


Lesenswert?

Jörg W. schrieb:
> Wilhelm M. schrieb:
>> Allerdings ist beim avr-g++ die Libstdc++ nicht dabei (aus gutem Grund
>> ;-).
>
> Es gibt keinen „guten Grund“, höchstens einen schlechten: es hat
> sich lange Zeit kein Schwein drum gekümmert, das mal zum Laufen zu
> bekommen.  Offensichtlich war der Leidensdruck für die C++-Nutzer nicht
> sehr hoch, vollständige Sprachunterstützung auch aus Bibliothekssicht
> zu haben.

M.E. ist eine 100%-Kompatibilität zur STL auch nicht erstrebenswert für 
die Anwendung auf so kleine µC wie die AVRs. Das fängt mit dynamischen 
Containern an, die nicht unkontrolliert wachsen dürfen. In der STL gibts 
eine Exception (auch die wollen wir auf µC nicht), wenn der Allokator 
kein Element mehr allokieren kann. Verändert man die Semantik des 
Containers zu einem mit begrenzter Kapazität und will man keine 
Exception, muss man an der Schnittstelle bspw. von std::vector was 
verändern, etwa void push_back() zu bool push_back(), oder auch void 
pop_front() zu bool pop_front(), da man auch kein UB akzeptieren kann.

Und wenn man nicht 100% kompatibel sein kann, sollte man es lassen. Das 
ist m.E. ein guter Grund. Und deswegen wird sich da auch keiner drum 
kümmern. Und wie gesagt: es gibt ja bereits Ersatz ...

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


Lesenswert?

Wilhelm M. schrieb:
> M.E. ist eine 100%-Kompatibilität zur STL auch nicht erstrebenswert

Dem Fuchs waren die Trauben damals auch zu sauer …

von Wilhelm M. (wimalopaan)


Lesenswert?

Chris D. schrieb:
>
> Das habe ich als Argument lange vermisst: C++ ist doch eine Obermenge
> von C.

Leider nicht. Es gibt filigrane Unterschiede z.B. wann UB entsteht. Der 
Klassiker sind unions und der Zugriff auf das "non-active-member".

> C++ enthält ja nicht nur OOP-Komponenten, sondern eben auch andere
> schöne Dinge (Namespaces usw.), die man auch bei rein prozeduraler
> Programmierung nutzen kann.

Das ist doch gerade das, was ich hier schon zum x-ten Mal sage: C++ ist 
eine Multiparadigmen-Sprache (vielleicht weiß ja keiner was damit 
gemeint ist ...): also mindestsmal kann ich auch nur rein prozedural und 
imperativ programmieren. Man muss ja nicht gleich eine DSL (nein: nicht 
Telekom ...) implementieren oder funktional werden.

Und es gibt eben so viele kleine Nettigkeiten, die wirklich Freude 
machen: zuallererst domänenspezifische Datentypen etwa Länge[m], 
Strom[A], Ladung[C], ...

Beitrag #5041585 wurde von einem Moderator gelöscht.
Beitrag #5041616 wurde von einem Moderator gelöscht.
Beitrag #5041662 wurde von einem Moderator gelöscht.
von W.S. (Gast)


Lesenswert?

Jörg W. schrieb:
>> Objektorientierte Programmierung ist eine Domäne von äußerlich gleichen,
>> aber innerlich verschiedenen Dingen
>
> … also beispielsweise einem Dutzend Motoren eines Roboters? ;-)

Genau. Und bei jedem Motor ist die Ansteuerung ganz anders, weswegen 
jeder seine eigenen Methoden haben muß und sich keinerlei Gemeinsamkeit 
findet, wenn man mal vom Wort "Motor" absieht. Was hast du denn von 
einer Klasse "Motor", wenn jeder Motor seinen eigenen Satz Methoden 
haben muß? Oder jedem Motor seine eigene Klasse, die mit der des 
Nachbarmotors nix zu schaffen hat?

Die Alternative sähe so aus, daß man alles bei allen Motoren 
parametrisieren müßte, um auf gleiche Methoden kommen zu können. Aber 
das bläht die Treiber derart auf, daß man es lieber bleiben lassen 
sollte.

W.S.

Beitrag #5041804 wurde von einem Moderator gelöscht.
Beitrag #5041806 wurde von einem Moderator gelöscht.
Beitrag #5041818 wurde von einem Moderator gelöscht.
von Vincent H. (vinci)


Lesenswert?

W.S. schrieb:
> Jörg W. schrieb:
>>> Objektorientierte Programmierung ist eine Domäne von äußerlich gleichen,
>>> aber innerlich verschiedenen Dingen
>>
>> … also beispielsweise einem Dutzend Motoren eines Roboters? ;-)
>
> Genau. Und bei jedem Motor ist die Ansteuerung ganz anders, weswegen
> jeder seine eigenen Methoden haben muß und sich keinerlei Gemeinsamkeit
> findet, wenn man mal vom Wort "Motor" absieht. Was hast du denn von
> einer Klasse "Motor", wenn jeder Motor seinen eigenen Satz Methoden
> haben muß? Oder jedem Motor seine eigene Klasse, die mit der des
> Nachbarmotors nix zu schaffen hat?
>
> Die Alternative sähe so aus, daß man alles bei allen Motoren
> parametrisieren müßte, um auf gleiche Methoden kommen zu können. Aber
> das bläht die Treiber derart auf, daß man es lieber bleiben lassen
> sollte.
>
> W.S.


Nur weil ein Schritt- und ein Encoder-gestützer DC Motor andere 
Ansteuerungen haben, heißt das doch noch lange nicht, dass ich nicht 
beide mit "Motor::move(x, y, z)" ansprechen kann... Nona brauch ich für 
jeden Motor eine eigene Implementierung. Hab ich die aber einmal fertig, 
so ist mir später einmal egal ob ich mit einer move Funktion jetzt den 
Schritt- oder den DC Motor steuere.

Bei diesem Komfort hört die objektorientierte Abstraktion aber nicht 
auf. Wird das Projekt später einmal erweitert und eine Asynchronmaschine 
eingebaut, so zwinge ich mit dem bestehenden Code dem neuen Motor exakt 
das selbe Interface auf. Damit unterbinde ich von vornherin, dass ein 
Kollege aus Jux und Tollerei eine "goto" Funktion mit Polar-Koordinaten 
schreibt...

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

PeterPan schrieb:
> Ich frage mich wie du dann überhaupt C in Betracht ziehen kannst, eine
> Sprache die nicht mal sowas wie echte Konstanten kennt (const wohl eher
> nicht...).

Na, in C++ ist "const" auch nicht das, was man unter "konstant" oder 
"read-only" versteht.

Instruktives Beispiel:
1
extern const int c;
2
3
__attribute__((__used__))
4
const int i = c;

mach avr-g++ zu
1
_GLOBAL__sub_I_const.c:
2
  lds r24,c
3
  lds r25,c+1
4
  sts _ZL1i+1,r25
5
  sts _ZL1i,r24
6
  ret
7
  .size  _GLOBAL__sub_I_const.c, .-_GLOBAL__sub_I_const.c
8
  .global __do_global_ctors
9
.section .ctors,"a",@progbits
10
  .p2align  1
11
  .word  gs(_GLOBAL__sub_I_const.c)
12
  .local  _ZL1i
13
  .comm  _ZL1i,2,1

D.h. es wird ein statischer Konstruktor aus dem Hut gezaubert (geht halt 
nicht anders) um i zu initialisieren.

Wenn du nun PROGMEM verwendest weil du glaubst, const i sei read-only: 
BUMMER.

Natürlich ist i read-only auf C++ Ebene, aber eben nicht read-only auf 
Binärebene, und C++ kann offenbar auch keine solche Zusicherung 
machen.

Dafür brauchte es dann eine Spracherweiterung wie constexpr.  Welche 
Zusicherungen die auf Binärebene macht weiß ich nicht.  Da der 
Standard darüber (z.B. elf oder aout) keine Annahmen macht, stell ich 
mir die Spezifikation ganz interessant vor...

Wenn es hingegen in C übersetzt, dann ist es read-only auf Binäreben 
-- und wenn es nicht übersetzt, bedeutet das nicht, dass es im 
landläufigen Sinne nicht konstant ist.  Ist zwar eine Einschränkung in 
C, dafür weiß ich aber was rauskommt wenn es compiliert.

von Wilhelm M. (wimalopaan)


Lesenswert?

Johann L. schrieb:
> PeterPan schrieb:
>> Ich frage mich wie du dann überhaupt C in Betracht ziehen kannst, eine
>> Sprache die nicht mal sowas wie echte Konstanten kennt (const wohl eher
>> nicht...).
>
> Na, in C++ ist "const" auch nicht das, was man unter "konstant" oder
> "read-only" versteht.

Oft ein Mißverständis, was const bedeutet!

Hier ist ein gutes Beispiel, das auch noch aus der µC Welt stammt, und 
deswegen hier verständlich sein sollte.

https://stackoverflow.com/questions/4486326/does-const-just-mean-read-only-or-something-more

Zum Big Picture hinter const:

https://isocpp.org/wiki/faq/const-correctness

Und die Bedeutung von constexpr ist nochmal anders ...

: Bearbeitet durch User
von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Wilhelm M. schrieb:
> Johann L. schrieb:
>> PeterPan schrieb:
>>> Ich frage mich wie du dann überhaupt C in Betracht ziehen kannst, eine
>>> Sprache die nicht mal sowas wie echte Konstanten kennt (const wohl eher
>>> nicht...).
>>
>> Na, in C++ ist "const" auch nicht das, was man unter "konstant" oder
>> "read-only" versteht.
>
> Oft ein Mißverständis, was const bedeutet!

Ich wollte nur darauf hingewiesen haben, dass const in C++ eben nicht 
konstant ist von der binär-Perspektive.

Auf einem Host ist mir das relativ Wurscht, aber je limitierter die 
Zielplattform ist, desto exakter muss die Code-Erzeugung sein.

Und an C++ für AVR interessiert mich zumindest immer auch welchen Code 
der Compiler generiert, einfach weil ich immer auch die Tool-Perspektive 
habe.

Beitrag #5041940 wurde von einem Moderator gelöscht.
Beitrag #5041976 wurde von einem Moderator gelöscht.
von Yalu X. (yalu) (Moderator)


Lesenswert?

Johann L. schrieb:
>> Oft ein Mißverständis, was const bedeutet!
>
> Ich wollte nur darauf hingewiesen haben, dass const in C++ eben nicht
> konstant ist von der binär-Perspektive.

Die C++-Konstanten sind i.Allg. schon etwas konstanter als diejenigen in
C, aber noch lange nicht konstant genug, um bspw. folgendes zu machen:

1
extern const int c;
2
3
const int i = c;
4
int array[i];

von Programmiersprachentheaterintendant (Gast)


Lesenswert?

Ich will unbedingt noch in Errinnerung rufen, dass der erste C++ 
Compiler CFront (und folgende andere frühe C++ Compilers) nicht direkt 
Maschinencode sondern C-Code generierten.
Für die Zankdiskussion hier: man schrieb in C Quellcode, mit dem man 
nichts zu tun haben wollte.

Man wendet in der neuen Sprache C++ "ach so fürchterliche" OO-Konzepte 
an, welche sich in der alten Sprache C zwar auch formulieren lassen, 
aber in einer (wegen C) gezwungenermassen dermassen schrecklichen Form 
ausfallen, auf die man keine Lust hat sie von Hand weder direkt so 
hinzuschreiben noch zu warten, sodass eben einen Automaten (der 
Umsetzer) ran muss damit man nur in der "weniger schrecklichen" neuen 
Formulierung bleiben kann.

Das ist übrigens eine sehr verbreitete Vorgehensweise, welche bewährt 
und akzeptiert ist.
Nicht nur dass "damals" aus dem Verdruss in Assembler/Maschinensprache 
immer wieder den selben boilerplate Code durchkauen zu müssen 
BASIC/FORTRAN/C/usw. entstanden -alle diese Programmiersprachen haben 
ziemlich überschaubare "schlimme" aber eben doch sinnvollerweise nötige 
Abstraktionen resp. Äquivalenzen zu was man davor machte- sondern auch 
neuzeitigere, junge, "ach so fürchterlich Abstrakte" Programmiersprachen 
lassen sich so umsetzen: p2c, ptoc, Ofront, m2c, GNAT, u.v.A.mehr. Auch 
für die JVM gibt es mehr als bloss Java. LLVM lässt ebenso grüssen.
Letztendlich werden alle Programme, egal in welcher Sprache sie 
geschrieben sind, von den CPUs in Maschinencode ausgeführt.

C-isten, welche C++-isten den nächsten "fürchterlichen" Schritt richtung 
Hochsprache wegzetern wollen sollen bedenken dass sie selbst auf dem 
gleichen Gefälle und in gleicher Position gegenüber ASM-isten sind.
Für alle 3 genannten Gruppen gilt: jeder darf bei seinem 
Lieblingswerkzeug bleiben, solange es ihm gut von der Hand geht und egal 
wie umständlich es tatsächlich ist (mit Computern werden Probleme 
gelöst, welche man ohne Computer gar nicht hat). Das ist in beide 
Blickrichtungen des o.g. Gefälles gültig was auch impliziert das es noch 
handlicheres über C++ gibt.

Alle möglichen "Zwischenformen" eines Computerprogrammes verlieren 
sowieso beim Relevanzvergleich (pun intended): es zählen letztlich nur 
die "niedrigste" Form für den ausführen Automaten und die dem die 
ursprüngliche Aufgabe lösenden Mensch behaglichste Form. Erstere Akteure 
haben nichts zu melden weil leblose Maschinen - letztere sind aus 
biologischer Evolution hervorgegangene komplexe Organismen wovon leider 
nicht alle Exemplare nur die besten Vorteile mit auf den Lebensweg 
abbekommen haben. In dieser Erbsenklauberei um den blossen Unterschied 
von "++" im Namen kann man schon merken wer sich mit Besitzstandwahrung 
begnügt und wer weder tief noch weit genug über seinen Tellerrand zu 
Blicken bereit/fähig ist.

von Einer K. (Gast)


Lesenswert?

Programmiersprachentheaterintendant schrieb:
> Für alle 3 genannten Gruppen gilt: jeder darf bei seinem
> Lieblingswerkzeug bleiben, ...
! unterschreib !

von Programmiersprachentheaterintendant (Gast)


Lesenswert?

oh hallo Yalu,

das Mikroskop für EBNF wieder beiseite gestellt?  :-)

von Wilhelm M. (wimalopaan)


Lesenswert?

Programmiersprachentheaterintendant schrieb:

> C-isten, welche C++-isten den nächsten "fürchterlichen" Schritt richtung
> Hochsprache wegzetern wollen sollen bedenken dass sie selbst auf dem
> gleichen Gefälle und in gleicher Position gegenüber ASM-isten sind.
> Für alle 3 genannten Gruppen gilt: jeder darf bei seinem
> Lieblingswerkzeug bleiben, solange es ihm gut von der Hand geht und egal
> wie umständlich es tatsächlich ist (mit Computern werden Probleme
> gelöst, welche man ohne Computer gar nicht hat). Das ist in beide
> Blickrichtungen des o.g. Gefälles gültig was auch impliziert das es noch
> handlicheres über C++ gibt.

Ja, da hast Du sicherlich recht. Und bei den C++-isten führt das dann 
dazu, dass man sich eine eDSL in C++ selbst für seine Anwendungsdomäne 
erzeugt, die natürlich noch abstrakter als C++ selbst. Generische 
Algorithmen sind die Vorstufe, eine eDSL die nächste Stufe.

Aber das machen ja auch die C-isten, z.B. mit Regulären-Ausdrücken, was 
einer DSL in etwa nahe kommt.

Letztendlich geht es doch darum, wie schnell, sicher, effizient und 
dauerhaft ich als Mensch mit meinem Werkzeug ein Problem lösen kann. 
Natürlich muss das Ziel dabei sein, dass am Ende ein optimaler Code für 
die Maschine dabei heraus kommt.

von Teo D. (teoderix)


Lesenswert?

Was mich bisher von C++ abgehalten hat, wurde durch den Beitrag von
Beitrag "Re: Welche Programmiersprache auf µC"
mehr als deutlich dargelegt.

Allerdings für die kleinen µCs muss ich doch nicht den ganzen Wald 
kennen.
Nur wie meine Perlen finden, ohne den gesamten Wald durchforsten zu 
müssen?

von Carl D. (jcw2)


Lesenswert?

Johann L. schrieb:
> Wilhelm M. schrieb:
>> Johann L. schrieb:
>>> Wilhelm M. schrieb:
>>>> Allerdings ist beim avr-g++ die Libstdc++ nicht dabei (aus gutem Grund
>>>> ;-)
>>>
>>> Und was ist dieser Grund?
>>>
>>> Du als C++ Hero wärst doch prädestiniert, den GCC so zu pimpen, dass er
>>> auch C++ unterstützt.
>>
>> Was meinst Du? Tut er doch, der avr-g++.
>
> Nein, tut er nicht, denn zum C++ Standard gehören eben auch die
> Standard-Bibliotheken, nicht nur der Sprachkern.
>
> Und den "guten Grund", warum dieser Teil von C++ nicht unterstützt wird,
> kenn ich auch nicht.

So weit ich das verstehe, beruht ein großer Teil der STL auf dem 
Vorhandensein von Exceptions. Ohne wäre es eine NonSTL.

Allerdings gibt es es einige "statische" Dinge, z.B. std::array, die man 
kostenlos benutzen kann um Arrays konstanter Größe an <allgorithm> 
anzudocken. Man muß z.B. die Suche eines Maximalwerts in einem Int-Array 
nicht jedesmal neu erfinden. Stattdessen std::foreach über das Array und 
Lambda zu Verarbeitung der Werte.

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


Lesenswert?

Teo D. schrieb:
> Nur wie meine Perlen finden, ohne den gesamten Wald durchforsten zu
> müssen?

Eigentlich nur, indem du überhaupt erstmal anfängst, dich damit zu
befassen.

Ich stand letztens auch vor rätselhaftem C++-Code (in qucs), bei dem
ich erstmal Tante Gugel angeworfen habe.  Offenbar versucht man in
den letzten C++-Standards, FORTRAN nun mal die Domäne streitig zu
machen, sie hätten für parallele Vektordatenverarbeitung die einzige
nutzbare Programmiersprache, und hat solche Dinge jetzt in C++ drin.

Allerdings genügte es mir für diesen Zweck vollauf, nur die Grundidee
hinter den paar Zeilen zu verstehen (irgendwas musste da geändert
werden, weiß nicht mehr genau, was es war).  Solche Dinge muss ich
gewiss nicht selbst schreiben können, erst recht nicht auf einem
kleinen Controller.

von Abel H. (abel)


Lesenswert?

Teo D. schrieb:
> Nur wie meine Perlen finden, ohne den gesamten Wald durchforsten zu
> müssen?

Suche Steinpilze. Das ist im Wald aussichtsreicher.

Abel

Beitrag #5042137 wurde von einem Moderator gelöscht.
Beitrag #5042153 wurde von einem Moderator gelöscht.
Beitrag #5042155 wurde von einem Moderator gelöscht.
Beitrag #5042158 wurde von einem Moderator gelöscht.
Beitrag #5042159 wurde von einem Moderator gelöscht.
Beitrag #5042160 wurde von einem Moderator gelöscht.
von christopher robin (Gast)


Lesenswert?

Es gibt allerdings ein ziemlich gewichtiges Argument für C:
Es kam bei Star Wars zum Einsatz und zwar bei der Programmierung des 
Todessterns. Ich kann auch gerne die Quelle benennen :-)

von W.S. (Gast)


Lesenswert?

Yalu X. schrieb:
> Die C++-Konstanten sind i.Allg. schon etwas konstanter als diejenigen in
> C, aber..

Muß ich jetzt mit dir schimpfen?

Also, was man auf nem µC mit einem ordentlichen Compiler in C const 
macht, IST konstant. Und zwar derart konstant, daß es nicht konstanter 
geht.

Hab grad mal das Menüsystem der steinalten Lernbetty (per Keil 
übersetzt) angeschaut: da ist schlichtweg ALLES im Flash. Sowohl die 
structs als auch die Texte als auch die Members in den structs und deren 
eben grad vorwärts deklarierten Zielen. Eben alles.

konstanter geht es nicht mehr.

W.S.

von W.S. (Gast)


Lesenswert?

Programmiersprachentheaterintendant schrieb:
> In dieser Erbsenklauberei um den blossen Unterschied
> von "++" im Namen kann man schon merken wer sich mit Besitzstandwahrung
> begnügt und wer weder tief noch weit genug über seinen Tellerrand zu
> Blicken bereit/fähig ist.

Ach, der Tellerrand.

Ja, da hast du wohl Recht. So ziemlich alle hier in diesem Thread 
beteiligten scheinen einen recht kleinen Teller zu haben, der mal gerade 
von Assembler (schon außerhalb des Tellerrandes) über C und dann C++ bis 
Java und C# reicht. Mir ist dieser Teller zu klein, ich benutze dort wo 
ich kann, lieber Pascal und schreibe meine PC-Programme mit Delphi oder 
Lazarus. Aber bei den C-lastigen Leuten hier erwarte ich nicht, daß sie 
das aktuelle D10.2 Tokyo auch nur vom Hörensagen her kennen.

Nur der Umstand, daß es keinen guten Pascal-Compiler für aktuelle µC 
gibt, führt dazu, für µC eben C zu verwenden. Aber auf die Idee, hier 
ein C++ verwenden zu wollen, halte ich für reinen Mutwillen.

ach was.. gut Nacht zusammen!

W.S.

von Yalu X. (yalu) (Moderator)


Lesenswert?

W.S. schrieb:
> Yalu X. schrieb:
>> Die C++-Konstanten sind i.Allg. schon etwas konstanter als diejenigen in
>> C, aber..
>
> Muß ich jetzt mit dir schimpfen?

Nein, musst du nicht. Es sei denn, du kannst nicht anders ;-)

> Also, was man auf nem µC mit einem ordentlichen Compiler in C const
> macht, IST konstant. Und zwar derart konstant, daß es nicht konstanter
> geht.

Mit konstant meine ich konstant im Sinn einer "constant expression"
gemäß der C- und C++-Norm. Hätte ich konstant im mathematischen Sinn
gemeint, hätte ich das Adjektiv nicht im Komparativ gebraucht.

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


Lesenswert?

W.S. schrieb:
> Mir ist dieser Teller zu klein, ich benutze dort wo ich kann, lieber
> Pascal und schreibe meine PC-Programme mit Delphi oder Lazarus.

Also einfach einen anderen Teller.  Auf die Argumente, dass dieser
Teller auch nur golden aussieht und nicht aus Gold ist, gehst du
sowieso nie ein, ist also auch egal. ;-)

Viel mehr schade finde ich eigentlich, dass Ada in der Controllerwelt
nie recht zum Fliegen gekommen ist.  Der Zug ist aber halt einfach
abgefahren, wie's scheint.

Wenn die Controller größer werden, kann man sich dann wohl besser
Gedanken um Lua oder Python machen, statt Ada oder Pascal nachzutrauern.

von Guido B. (guido-b)


Lesenswert?

W.S. schrieb:
> Also, was man auf nem µC mit einem ordentlichen Compiler in C const
> macht, IST konstant. Und zwar derart konstant, daß es nicht konstanter
> geht.
>
> Hab grad mal das Menüsystem der steinalten Lernbetty (per Keil
> übersetzt) angeschaut: da ist schlichtweg ALLES im Flash.

Wieso das denn? Was konstant ist, kennt der Compiler, das muss
doch nicht ins Flash?

Beitrag #5042376 wurde von einem Moderator gelöscht.
Beitrag #5042378 wurde von einem Moderator gelöscht.
von Christopher J. (christopher_j23)


Lesenswert?

Jörg W. schrieb:
> Wenn die Controller größer werden, kann man sich dann wohl besser
> Gedanken um Lua oder Python machen, statt Ada oder Pascal nachzutrauern.

Gedanken kann man sich machen und ein paar nette Spielereien sind damit 
sicher auch möglich aber dann hört es da meiner Meinung nach auch schon 
auf. Das einzige was noch einigermaßen erträglich vom 
Ressourcenverbrauch ist, ist wenn man vorhandene Funktionen, die in C 
implementiert sind per Scriptsprache mal eben schnell zu einem Programm 
zusammenhackt.

Von der Performance abgesehen mag ich ganz prinzipiell kompilierte 
Sprachen, weil der Compiler mir gegebenenfalls schon auf die Finger haut 
bevor das Programm läuft. Aus dem Grund ziehe ich mittlerweile auf dem 
PC auch ein Go-Programm einem Python-Script vor. Die Programmierung ist 
ähnlich dynamisch wie bei Python und Go kompiliert dermaßen schnell das 
ein "go run some_program.go" gefühlt genauso schnell startet wie ein 
"./python_script.py". Das es zusätzlich noch um ein bis zwei 
Größenordnungen schneller läuft interessiert mich dabei meistens nicht 
mal, weil der Rechner für ein billiges Script bzw. Progrämmchen meist 
ohnehin mehr als genug Dampf hat.

Da aber die aber die Mikrocontroller auch übermorgen nicht dutzende MB 
SRAM für lau mit an Board haben werden, wage ich mal zu behaupten, dass 
auch in zehn Jahren noch ASM, C und C++ vorherrschen werden (im Bereich 
uC). Eventuell wird sich noch Rust einen Teil vom Kuchen schnappen. 
Potential hat Rust meiner Meinung nach mehr als genug. Sehr starke 
Typisierung, quasi kein Runtime-Overhead und auch kein GC, trotzdem 
keine Dangling-Pointer und sonstige Zombies. Der Einstieg ist aber 
wirklich hart. Der Compiler haut einem anfangs so oft auf die Finger das 
es schnell weh tut. Wer da aus dem C(++)-Lager kommt verliert da schnell 
die Lust daran, weil nicht mehr alles erlaubt ist. Dafür hat man eine 
gewisse Sicherheit, dass das Programm sofern es denn kompiliert, frei 
von irgendwelchen Memorybugs ist.

Beitrag #5042385 wurde von einem Moderator gelöscht.
Beitrag #5042386 wurde von einem Moderator gelöscht.
Beitrag #5042388 wurde von einem Moderator gelöscht.
Beitrag #5042422 wurde von einem Moderator gelöscht.
Beitrag #5042423 wurde von einem Moderator gelöscht.
Beitrag #5042424 wurde von einem Moderator gelöscht.
Beitrag #5042447 wurde von einem Moderator gelöscht.
von Vincent H. (vinci)


Lesenswert?

Christopher J. schrieb:
> Jörg W. schrieb:
>> Wenn die Controller größer werden, kann man sich dann wohl besser
>> Gedanken um Lua oder Python machen, statt Ada oder Pascal nachzutrauern.
>
> Gedanken kann man sich machen und ein paar nette Spielereien sind damit
> sicher auch möglich aber dann hört es da meiner Meinung nach auch schon
> auf. Das einzige was noch einigermaßen erträglich vom
> Ressourcenverbrauch ist, ist wenn man vorhandene Funktionen, die in C
> implementiert sind per Scriptsprache mal eben schnell zu einem Programm
> zusammenhackt.


Ich hab leider mit Python und JS auf µC überhaupt keine Erfahrung, kenne 
aber zumindest Lua. Wenn ich ein Projekt vorgesetzt bekäme, dass weder 
Echtzeitfähigkeit benötigt, noch der Preis auf die letzte Kommastelle 
berücksichtigt werden muss, dann würde ich keine Sekunde zögern und 
irgendeine Skriptsprache verwenden.

Gerade letzteres, sprich Lua, ist sogar begrenzt Echtzeitfähig, da die 
GC bei Bedarf deaktiviert werden kann...

von Teo D. (teoderix)


Lesenswert?

Jörg W. schrieb:
> Eigentlich nur, indem du überhaupt erstmal anfängst, dich damit zu
> befassen.

Danke, sehr nett von dir mir das zu unterstellen..... :´(

Komm mal wieder runter! (und schneide dir mal wieder die Nasenhaare)

von Chris D. (myfairtux) (Moderator) Benutzerseite


Lesenswert?

Vincent H. schrieb:
> Ich hab leider mit Python und JS auf µC überhaupt keine Erfahrung, kenne
> aber zumindest Lua. Wenn ich ein Projekt vorgesetzt bekäme, dass weder
> Echtzeitfähigkeit benötigt, noch der Preis auf die letzte Kommastelle
> berücksichtigt werden muss, dann würde ich keine Sekunde zögern und
> irgendeine Skriptsprache verwenden.
> Gerade letzteres, sprich Lua, ist sogar begrenzt Echtzeitfähig, da die
> GC bei Bedarf deaktiviert werden kann...

Ja, so waren damals auch meine Gedanken, als ich mir einen passenden 
Tcl-Interpreter für AVRs baute: für über 90% der Dinge, die ein µC 
macht, reicht eine Skriptsprache (sogar ohne Bytecompiler) vollkommen 
aus. Und da Speicher damals knapp war, konnte man so sehr große 
Programme z.B. in EEPROMs auslagern.

Lua ähnelt Tcl von der Intention her stark (Einbau in C-Programme, 
Verlagerung der laufzeitunkritischen Dinge in die Skriptsprache, sehr 
einfache Implementierbarkeit weiterer C-Funktionen). Echtzeitfähigkeit 
kann man dort auch sehr einfach nachrüsten.

Heutzutage passt der komplette Interpreter auf einen STM32 und wir sind 
dabei, Tk soweit zu portieren, dass man den Großteil der Tk-Befehle dann 
für eigene Oberflächen verwenden kann.

Debugging gestaltet sich bei Skriptsprachen auch einfacher, weil man das 
Programm in einer Shell direkt auf dem Zielsystem ändern kann.

Bei der Frage "Welche Programmiersprache auf µC" sollte man also 
durchaus auch einen interessierten Blick auf Skriptsprachen werfen.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Chris D. schrieb:
> Bei der Frage "Welche Programmiersprache auf µC" sollte man also
> durchaus auch einen interessierten Blick auf Skriptsprachen werfen.

Wenn man diese Skriptspache erst selbst implementieren muss, dann dürfte 
das deren Attraktivitat etwas dämpfen...

von Chris D. (myfairtux) (Moderator) Benutzerseite


Lesenswert?

Nur weil es damals so war, muss das nicht heute gelten :-)

Es gibt durchaus einige sehr kleine Tcl-Interpreter:

http://wiki.tcl.tk/1363

Bei Lua kenne ich mich nicht aus, aber eLua scheint in die Richtung zu 
gehen:

http://www.eluaproject.net/

: Bearbeitet durch Moderator
Beitrag #5042683 wurde von einem Moderator gelöscht.
Beitrag #5042687 wurde von einem Moderator gelöscht.
Beitrag #5042694 wurde von einem Moderator gelöscht.
Beitrag #5042695 wurde von einem Moderator gelöscht.
von Teo D. (teoderix)


Lesenswert?

Jörg W. schrieb:
> Teo D. schrieb:
>> Nur wie meine Perlen finden, ohne den gesamten Wald durchforsten zu
>> müssen?
>
> Eigentlich nur, indem du überhaupt erstmal anfängst, dich damit zu
> befassen.

OK, hab nun auch wieder Bodenkontakt ;)

Hab mich vor ~25J mal damit befasst, daher auch der Bammel :)
C++ Bücher gibt's ja zur genüge.... Leider alles nur Pfade durch den 
Dschungel.
Ich würde mir gerne einen Überblick verschaffen, ohne ein Dutzend Bücher 
durchforsten zu müssen!
Für die Grundlagen hab ich schon ein halbes Dutzend geordert. Wird schon 
was brauchbares dabei sein. :)

Beitrag #5042739 wurde von einem Moderator gelöscht.
von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Teo D. schrieb:
> Hab mich vor ~25J mal damit befasst, daher auch der Bammel :)

Irgendwann damals hatte ich auch mal einen C++-Lehrgang.  Zum Glück
nicht alles vergessen. ;-)

Ich würde an deiner Stelle nicht zu viele Bücher wälzen, sondern mal
kleine Stückchen programmieren.  Wirklich erstmal die LED als Objekt.
Vielleicht nach dem Ein- und Ausschalten dann auch mit einem
PWM-Timer für die Helligkeitssteuerung erweitern.

AVRs sind aufgrund ihrer IO-Registerstruktur nicht ganz so nett für
eine Abstraktion in C++.  Ich schrieb's oben schon mal, ein GPIO wird
dort ja durch das Tupel {PORTx, PINx, DDRx} beschrieben, formal (wenn
man nicht irgendwelche Mystik mit hin- und her-casten und händischen
Offsets zwischen diesen Registern verzapfen möchte) muss man daher
beim Konstruktor alle drei Namen übergeben.  Wenn man nur „A“ oder
„B“ (für das Metazeichen „x“ oben) schreiben will, dann hilft da leider
wieder nur der leidige Präprozessor, denn er ist in der Lage, ganze
Bezeichner durch Verkettung zu synthetisieren.

Bei den Cortex-Ms ist das besser, da hat man sowas wie PORTx->DIR
oder PORTx->DIN oder dergleichen; hier kann man das komplette „PORTx“
übergeben und damit den passenden Port wählen.

Achso, und natürlich immer den generierten Assemblercode mit ansehen.

Was sich übrigens gut macht ist, wenn man so viel wie möglich in der
Abstraktion als „const“ bzw. „constexpr“ deklariert.  Damit ist dann
verbunden, dass man bei der Instanziierung keine Zuweisung machen
darf, sondern den tatsächlichen Wert über den Konstruktor übergibt.
Das wiederum gibt dem Compiler die Gewissheit, dass die Sache bereits
zur Compilezeit bekannt ist, sodass er maximal die konstanten Dinge
ersetzen kann.  Auf dem AVR landet man so am Ende auch wieder bei
CBI und SBI im generierten Code.

: Bearbeitet durch Moderator
Beitrag #5042756 wurde von einem Moderator gelöscht.
Beitrag #5042770 wurde von einem Moderator gelöscht.
Beitrag #5042783 wurde vom Autor gelöscht.
von Teo D. (teoderix)


Lesenswert?

Jörg W. schrieb:
> Ich würde an deiner Stelle nicht zu viele Bücher wälzen, sondern mal
> kleine Stückchen programmieren.  Wirklich erstmal die LED als Objekt.
> Vielleicht nach dem Ein- und Ausschalten dann auch mit einem
> PWM-Timer für die Helligkeitssteuerung erweitern.

Würdest du damit bitte aufhören! :)

Jörg W. schrieb:
> AVRs sind aufgrund ihrer IO-Registerstruktur nicht ganz so nett für
> eine Abstraktion in C++........

Genau solch Informationen suche ich, kompakt und nicht auf Tausende 
Seiten verteilt.....

Jörg W. schrieb:
> Ich würde an deiner Stelle nicht zu viele Bücher wälzen

Hab sie ja nur gekauft!
Bei Bücherbillig für 20€. Gelesen wird nur was brauchbar ist. :)

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Chris D. schrieb:
> Bei der Frage "Welche Programmiersprache auf µC" sollte man also
> durchaus auch einen interessierten Blick auf Skriptsprachen werfen.

Ich entwickle gerade eine Interpretersprache NIC für 32-Bit-µCs. Die 
Entwicklung ist zur Zeit noch in der Alpha-Phase, jedoch zeigen erste 
Benchmarks, dass der Interpreter etwa doppelt so schnell wie PHP und 
5-10 mal so schnell wie Python ist. Sobald ich eine Beta-Version fertig 
habe, werde ich sie unter "Projekte und Code" vorstellen.

Die hohe Ausführungsgeschwindigkeit wird dadurch erreicht, dass der Host 
das NIC-Script vorcompiliert und dann auf den µC einen 
maschinenunabhängigen Objekt-Code hochlädt. Dabei werden dann auch 
direkt einige Optimierungen vorgenommen: Zum Beispiel werden konstante 
Ausdrücke in Expressions direkt zur Compilezeit ausgerechnet.

Da die Übersetzungszeiten unter einer Sekunde bleiben, merkt man das 
überhaupt nicht. Sobald der Objekt-Code hochgeladen wurde, wird er auf 
dem µC (im Moment werden STM32F10x und STM32F4xx unterstützt) direkt 
ausgeführt. Dadurch verkürzt sich die Entwicklung eigener Programme, da 
das zeitaufwendige Flashen komplett entfällt.

NIC-Programme laufen nicht nur auf den STM32, sondern auch unter Windows 
und Linux - auch wenn das nur ein Nebeneffekt ist. So kann ich den 
NIC-Compiler und NIC-Interpreter direkt auf dem Host testen.

Fortgeschrittene Programmierer können die Sprache durch eigene 
C-Funktionen  erweitern und diese dann direkt aus dem NIC-Programm 
aufrufen. So kann man zeitkritische Routinen in den C-Code verlagern.

Im Moment ist die Sprache noch rein prozedural, Objekt-Orientierung wird 
aber noch kommen. Außerdem wird es noch einen weiteren Compiler geben, 
der den maschinenunabhängigen Objekt-Code in ein C-Programm umwandelt, 
um diesen dann zur NIC-Laufzeitbibliothek dazulinken zu können. Damit 
kann man dann nach der Entwicklungsphase das Programm mit annähernd der 
Geschwindigkeit laufen lassen, wie auch ein adäquates C-Programm 
brauchen würde.

Ich habe auch schon mal begonnen, NIC zu dokumentieren. Diese Doku wird 
während der Entwicklung von mir parallel fortgeschrieben, siehe Artikel 
NIC.

: Bearbeitet durch Moderator
von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Teo D. schrieb:
> Genau solch Informationen suche ich, kompakt und nicht auf Tausende
> Seiten verteilt.....

Da es hier ja insgesamt um Sprachauswahl auf Mikrocontrollern geht,
vielleicht dafür einen neuen Thread öffnen?  Dieser hier ufert sowieso
gerade aus.

von Vincent H. (vinci)


Lesenswert?

Frank M. schrieb:
> Chris D. schrieb:
>> Bei der Frage "Welche Programmiersprache auf µC" sollte man also
>> durchaus auch einen interessierten Blick auf Skriptsprachen werfen.
>
> Ich entwickle gerade eine Interpretersprache NIC für 32-Bit-µCs. Die
> Entwicklung ist zur Zeit noch in der Alpha-Phase, jedoch zeigen erste
> Benchmarks, dass der Interpreter etwa doppelt so schnell wie PHP und
> 5-10 mal so schnell wie Python ist. Sobald ich eine Beta-Version fertig
> habe, werde ich sie unter "Projekte und Code" vorstellen.

Das klingt interessant, liest sich aber sehr wie Lua. Inwiefern wird 
sich NIC von Lua unterscheiden?

von Vn N. (wefwef_s)


Lesenswert?

Wilhelm M. schrieb:
> nicht angebrachten ... Exceptions

Gerade Exceptions sind in kritischen Umgebungen toll, weil damit keine 
"vergessenen Returnwerte" passieren können, und man bei nicht abgefangen 
Exceptions einfach das ganze System in einen Fehlerzustand schicken 
kann.

Ansonsten kann ich nur Bruce Douglass wiedergeben: meist nimmt man C, 
nicht weil es toll ist, sondern weil es funktioniert, überall verfügbar 
ist und wesentlich einfacher ist als C++. Bei größeren Projekten kann 
eine Teilmenge von C++ Sinn machen, wird aber tlw. von 
Zertifizierungsstellen nicht gern gesehen wegen mehr möglicher Dynamik 
(je nach verwendeten Features). Bzw. die Zertifizierer sehens nicht 
gern, ihre Chefs schon, weil die Anzahl der einzuwerfenden großen 
Scheine mit dem Zertifizierungsaufwand steigt.

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


Lesenswert?

vn n. schrieb:
> Gerade Exceptions sind in kritischen Umgebungen toll, weil damit keine
> "vergessenen Returnwerte" passieren können, und man bei nicht abgefangen
> Exceptions einfach das ganze System in einen Fehlerzustand schicken
> kann.

Leider ist der Overhead nicht ganz zu verachten, den sie erzeugen.

Daher mag man sie im Embedded-Bereich eher nicht.  Ansonsten fänden
sie wohl viele Programmierer gut.

von Stefan F. (Gast)


Lesenswert?

> AVRs sind aufgrund ihrer IO-Registerstruktur nicht ganz so nett
> für eine Abstraktion in C++.

Offensichtlich hat auch Atmel das inzwischen erkannt und es bei der 
Xmega Serie besser gemacht. Für's Hobby sind die allerdings aus anderen 
Gründen weniger attraktiv.

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


Lesenswert?

Stefan U. schrieb:
> Offensichtlich hat auch Atmel das inzwischen erkannt und es bei der
> Xmega Serie besser gemacht.

Ich denke nicht, das C++ dort das primäre Argument war :), aber ja,
deren IO-Register sind diesbezüglich besser strukturiert.

> Für's Hobby sind die allerdings aus anderen Gründen weniger attraktiv.

Nicht nur da, auch anderweitig scheinen sie mir nicht so gut „ans
Fliegen gekommen“ zu sein wie die klassischen AVRs.  Verglichen mit
der Konkurrenz aus dem Cortex-M-Lager waren sie dafür wohl einfach
zu spät auf dem Markt.

von Wilhelm M. (wimalopaan)


Lesenswert?

Stefan U. schrieb:
>> AVRs sind aufgrund ihrer IO-Registerstruktur nicht ganz so nett
>> für eine Abstraktion in C++.
>
> Offensichtlich hat auch Atmel das inzwischen erkannt und es bei der
> Xmega Serie besser gemacht. Für's Hobby sind die allerdings aus anderen
> Gründen weniger attraktiv.

Also wie die phys. I/O-Registeranornung aussieht, ist ziemlich egal. Man 
braucht ein bißchen template-Mechanik und gut ist.

Das einzige, was sie bei den XMegas besser gemacht habe, dass sie schon 
die C structs definiert haben. Die muss man bei den non-XMega eben 
selbst erzeugen...

von Carl D. (jcw2)


Lesenswert?

Bei den AVRs hat man vielleicht die IO-Register auf den Befehlssatz hin 
optimiert. Häufig bitweise zu Änderndes ganz nach vorn.

Die XMegas haben aber so viele Register, daß der AVR-Ansatz eh nichts 
wird. Deshalb ist identischer Aufbau und instanzspezifische Basisadresse 
für die einzelnen Geräte sicher auch von Hardwareseite besser zu 
handhaben.

: Bearbeitet durch User
von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Vincent H. schrieb:
> Das klingt interessant, liest sich aber sehr wie Lua.

Ich kannte Lua bisher nicht, habe es mir eben mal näher angeschaut. Ja, 
das sieht auf den ersten Blick tatsächlich sehr ähnlich aus :-)

> Inwiefern wird sich NIC von Lua unterscheiden?

Das kann ich jetzt noch nicht sagen, da ich Lua überhaupt nicht kenne. 
Ich werde mir Lua auf jeden Fall mal näher anschauen. Danke für den 
Hinweis.

Ich habe mir eben mal Lua 5.2 unter Linux installiert und das im 
NIC-Artikel angeführte Benchmark-Programm "Sieb des Eratosthenes" 
von NIC auf Lua umgeschrieben, was gar nicht so viel Umstände bereitete.

Ergebnis: Für die Ermittlung sämtlicher Primzahlen unter 100000 braucht 
NIC 5,3 Sekunden und Lua 6,1 Sekunden auf einem i7-2600@3.40GHz. Mit der 
um 20% höheren Geschwindigkeit bin ich ganz zufrieden, gerade weil NIC 
noch im Alpha-Stadium und als Interpreter trotzdem von der 
Ausführungszeit bisher noch ungeschlagen ist. Sobald der 
maschinenunabhägige Objekt-Code eines NIC-Programms in C übersetzt und 
damit echter Binärcode erzeugt werden kann, sollte noch ein weiterer 
Faktor von mindestens 10 drin sein.

Die Lua-Variante vom Sieb-Programm werde ich dann im NIC-Artikel 
noch einfügen zum Vergleich.

Wie ich schon sagte: Sobald eine Beta-Version von NIC verfügbar ist, 
werde ich das NIC-Projekt incl. Sources veröffentlichen.

: Bearbeitet durch Moderator
von Christopher J. (christopher_j23)


Lesenswert?

Vincent H. schrieb:
> Ich hab leider mit Python und JS auf µC überhaupt keine Erfahrung, kenne
> aber zumindest Lua. Wenn ich ein Projekt vorgesetzt bekäme, dass weder
> Echtzeitfähigkeit benötigt, noch der Preis auf die letzte Kommastelle
> berücksichtigt werden muss, dann würde ich keine Sekunde zögern und
> irgendeine Skriptsprache verwenden.
>
> Gerade letzteres, sprich Lua, ist sogar begrenzt Echtzeitfähig, da die
> GC bei Bedarf deaktiviert werden kann...

Ja, Lua ist meiner Meinung nach tatsächlich noch die brauchbarste 
Skriptsprache für Systeme mit begrenzten Ressourcen, weil vor allem auch 
die Anbindung an C sehr einfach ist. Schaut man da allerdings mal bei 
den üblichen Verdächtigen, wie z.B. http://nodemcu.com/ in den 
Source-Code, stellt man eben auch schnell fest, das im Prinzip alle 
"Low-Level" geschichten in C implementiert sind und man mit Lua 
lediglich "mal eben schnell" die einzelnen Bausteine zusammensetzt. 
Natürlich kann man Skripte vorab zu Bytecode kompilieren und das geht 
auch bei MicroPython aber wenn ich mein Programm ohnehin immer neu laden 
muss, dann kann ich das doch auch gleich in C++ schreiben (meinetwegen 
mit Arduino) und statt einer VM packe ich einfach einen Bootloader auf 
den uC der gegebenenfalls auch noch netzwerkfähig ist und wo ich über 
ein HTML-Interface eine neue Firmware laden kann, so wie das bei jedem 
Router typischerweise ist.


Frank M. schrieb:
> Ich entwickle gerade eine Interpretersprache NIC für 32-Bit-µCs.

Sieht, wie ich finde, ebenenfalls sehr interessant aus.



Jörg W. schrieb:
> vn n. schrieb:
>> Gerade Exceptions sind in kritischen Umgebungen toll, weil damit keine
>> "vergessenen Returnwerte" passieren können, und man bei nicht abgefangen
>> Exceptions einfach das ganze System in einen Fehlerzustand schicken
>> kann.
>
> Leider ist der Overhead nicht ganz zu verachten, den sie erzeugen.
>
> Daher mag man sie im Embedded-Bereich eher nicht.  Ansonsten fänden
> sie wohl viele Programmierer gut.

Auch wenn die Idee hinter den C++-Exceptions löblich sein mag, finde ich 
die Umsetzung eher suboptimal. Man hat seine Programmlogik und parallel 
dazu eine Exceptionlogik. Tritt ein Fehler auf, muss man aber erstmal 
wieder beides synchronisieren. Ich persönlich finde den Ansatz von Go 
oder Rust, die Fehlerbehandlung grundsätzlich über die Rückgabewerte der 
Funktionen (mittels "multiple return type") durchzuführen, wesentlich 
eleganter. Der Programmierer sollte sich eben schon in seiner 
Programmlogik Gedanken machen, an welcher Stelle etwas schief gehen kann 
und wie er das dann abfängt. In C gibt es ja die Hilfskrücke errno.h 
bzw. das überladen von Rückgabewerten (z.B. -1 für einen Fehler bei der 
Initialisierung eines Sockets). In der Praxis werden aber häufig nicht 
einmal diese Hilfskrücken genutzt und nach dem Motto "Et is noch immer 
jot jejange" einfach "void" als Rückgabewert gewählt. Demnach denke ich 
ist das grundsätzliche Problem eher die Grundeinstellung des 
Programmierers und erst nachrangig das es keine (vom Ressourcenverbrauch 
her) brauchbare Möglichkeit gibt Exceptions zu nutzen. Die "multiple 
return types" sind im Übrigen quasi ohne Overhead, weshalb man sie mit 
Rust auch z.B. auf einem Cortex-M problemlos nutzen kann. Hätte ich für 
C einen Wunsch für ein neues Feature frei und zur Wahl stünden 
C++-Exceptions und 'multiple return types', so würde ich eindeutig 
letzteres bevorzugen.

: Bearbeitet durch User
von Wilhelm M. (wimalopaan)


Lesenswert?

Christopher J. schrieb:
...
> Jörg W. schrieb:
>> vn n. schrieb:
>>> Gerade Exceptions sind in kritischen Umgebungen toll, weil damit keine
>>> "vergessenen Returnwerte" passieren können, und man bei nicht abgefangen
>>> Exceptions einfach das ganze System in einen Fehlerzustand schicken
>>> kann.
>>
>> Leider ist der Overhead nicht ganz zu verachten, den sie erzeugen.
>>
>> Daher mag man sie im Embedded-Bereich eher nicht.  Ansonsten fänden
>> sie wohl viele Programmierer gut.

...

> Hätte ich für
> C einen Wunsch für ein neues Feature frei und zur Wahl stünden
> C++-Exceptions und 'multiple return types', so würde ich eindeutig
> letzteres bevorzugen.

In "modernem" C++ werden Exceptions eigentlich kaum noch verwendet. 
Besser geeignet sind (v.a. dann auch im µC Bereich) std::optional oder 
ähnliche, konkrete Typen, die in ihrem Wertebereich einen ungültigen 
Wert haben.

Beitrag #5043573 wurde von einem Moderator gelöscht.
Beitrag #5043576 wurde von einem Moderator gelöscht.
Beitrag #5043577 wurde von einem Moderator gelöscht.
Beitrag #5043706 wurde von einem Moderator gelöscht.
Beitrag #5043707 wurde von einem Moderator gelöscht.
Beitrag #5043708 wurde von einem Moderator gelöscht.
von Chris D. (myfairtux) (Moderator) Benutzerseite


Lesenswert?

Frank M. schrieb:
> Ich habe auch schon mal begonnen, NIC zu dokumentieren. Diese Doku wird
> während der Entwicklung von mir parallel fortgeschrieben, siehe Artikel
> NIC.

Nochmal vielen Dank für den Hinweis. Das sieht doch schon richtig gut 
aus.

Ich werde allerdings bei Tcl bleiben, weil ich gerade die Typlosigkeit 
und Flexibilität (vor allem Listen, Arrays, neue Befehle/Umbenennung zur 
Laufzeit, Safe-Interpreter usw.) sehr schätze. Die GPIO-Funktionen habe 
ich dort schon implementiert ;-)
Und dazu kommt natürlich (hoffentlich in Bälde) die Gui/Tk-Anbindung.

Ich werde das aber auf jeden Fall im anderen Thread 
(Beitrag "NIC (aus dem langen C vs C++ Thread)") weiter beobachten.

von Vn N. (wefwef_s)


Lesenswert?

Jörg W. schrieb:
> vn n. schrieb:
>> Gerade Exceptions sind in kritischen Umgebungen toll, weil damit keine
>> "vergessenen Returnwerte" passieren können, und man bei nicht abgefangen
>> Exceptions einfach das ganze System in einen Fehlerzustand schicken
>> kann.
>
> Leider ist der Overhead nicht ganz zu verachten, den sie erzeugen.
>
> Daher mag man sie im Embedded-Bereich eher nicht.  Ansonsten fänden
> sie wohl viele Programmierer gut.

Ok, vielleicht muss man der Vollständigkeit halber unterscheiden, ob man 
mit 8MHz oder 150MHz unterwegs ist. ;)

Wilhelm M. schrieb:
> In "modernem" C++ werden Exceptions eigentlich kaum noch verwendet.
> Besser geeignet sind (v.a. dann auch im µC Bereich) std::optional oder
> ähnliche, konkrete Typen, die in ihrem Wertebereich einen ungültigen
> Wert haben.

In kritischen Umgebungen können Exceptions halt den Vorteil haben, dass 
man sie nicht ignorieren kann (im Gegensatz zu return values).

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


Lesenswert?

vn n. schrieb:
>> Leider ist der Overhead nicht ganz zu verachten, den sie erzeugen.
>> Daher mag man sie im Embedded-Bereich eher nicht.  Ansonsten fänden
>> sie wohl viele Programmierer gut.
>
> Ok, vielleicht muss man der Vollständigkeit halber unterscheiden, ob man
> mit 8MHz oder 150MHz unterwegs ist. ;)

Ich denke, dass das gar nicht so sehr das Problem ist.  Das letzte Mal,
als ich mir das mal angesehen habe, war der Code Bloat, der von der
Einbindung der Exceptions verursacht worden ist, aber immens.  Damit
ist es zumindest auf kleineren Controllern außen vor.

Wenn man 1 MB Flash hat, mag die Sache anders aussehen.

von Wilhelm M. (wimalopaan)


Lesenswert?

Jörg W. schrieb:
> vn n. schrieb:
>>> Leider ist der Overhead nicht ganz zu verachten, den sie erzeugen.
>>> Daher mag man sie im Embedded-Bereich eher nicht.  Ansonsten fänden
>>> sie wohl viele Programmierer gut.
>>
>> Ok, vielleicht muss man der Vollständigkeit halber unterscheiden, ob man
>> mit 8MHz oder 150MHz unterwegs ist. ;)
>
> Ich denke, dass das gar nicht so sehr das Problem ist.  Das letzte Mal,
> als ich mir das mal angesehen habe, war der Code Bloat, der von der
> Einbindung der Exceptions verursacht worden ist, aber immens.  Damit
> ist es zumindest auf kleineren Controllern außen vor.
>
> Wenn man 1 MB Flash hat, mag die Sache anders aussehen.

Wenn man unbedingt Exceptions verwenden muss / möchte, aber deswegen ein 
Problem mit der Wartbarkeit / Lesbarkeit bekommt, könnte man auch über 
die sog. "Exploding-Return-Types" nachdenken.

Beitrag #5045091 wurde von einem Moderator gelöscht.
Beitrag #5045606 wurde von einem Moderator gelöscht.
Beitrag #5046556 wurde von einem Moderator gelöscht.
Beitrag #5047826 wurde von einem Moderator gelöscht.
Beitrag #5047832 wurde von einem Moderator gelöscht.
Beitrag #5048050 wurde von einem Moderator gelöscht.
von Sheeva P. (sheevaplug)


Lesenswert?

Wilhelm M. schrieb:
> Wenn man unbedingt Exceptions verwenden muss / möchte, aber deswegen ein
> Problem mit der Wartbarkeit / Lesbarkeit bekommt, könnte man auch über
> die sog. "Exploding-Return-Types" nachdenken.

Wie groß ist eigentlich der Code Bloat, wenn man anstelle einer 
Exception-Instanz einen einfachen Integer-Fehlercode wirft (soetwas wie 
"throw 20;" statt "throw new RuntimeError("bla");")?

von Wilhelm M. (wimalopaan)


Lesenswert?

Sheeva P. schrieb:
> Wilhelm M. schrieb:
>> Wenn man unbedingt Exceptions verwenden muss / möchte, aber deswegen ein
>> Problem mit der Wartbarkeit / Lesbarkeit bekommt, könnte man auch über
>> die sog. "Exploding-Return-Types" nachdenken.
>
> Wie groß ist eigentlich der Code Bloat, wenn man anstelle einer
> Exception-Instanz einen einfachen Integer-Fehlercode wirft (soetwas wie
> "throw 20;" statt "throw new RuntimeError("bla");")?

Das sollte man nicht machen, da der Compiler das Objekt, das geworfen 
wird (hier ein Zeiger) zerstört, aber nicht das Heap-Objekt: das müsste 
man im catch nun explizit zerstören. Deswegen niemals heap-allocated 
Objekte der Zeiger werfen. Oder SmartPtr. Aber die dyn. Allokation 
produziert natürlich viel Laufzeit-Overhead.

Der Unterschied zwischen throw 20; und throw Bla; hängt davon ab, was 
der Ctor von Bla macht. Da ein Objekt immer (mindestens) ein Byte 
umfasst, ein int aber ggf. größer ist, könnte Bla günstiger sein.

Aber wie gesagt: erstens: exceptions sind old-school und zweitens: auf 
Minimal-Systemen möchte man sie eh nicht ...

von Sheeva P. (sheevaplug)


Lesenswert?

Wilhelm M. schrieb:
> Sheeva P. schrieb:
>> Wie groß ist eigentlich der Code Bloat, wenn man anstelle einer
>> Exception-Instanz einen einfachen Integer-Fehlercode wirft (soetwas wie
>> "throw 20;" statt "throw new RuntimeError("bla");")?
>
> Das sollte man nicht machen, da der Compiler das Objekt, das geworfen
> wird (hier ein Zeiger) zerstört, aber nicht das Heap-Objekt: das müsste
> man im catch nun explizit zerstören. Deswegen niemals heap-allocated
> Objekte der Zeiger werfen. Oder SmartPtr. Aber die dyn. Allokation
> produziert natürlich viel Laufzeit-Overhead.

Schon klar, aber hier geht es ja um den besonderen Anwendungsfalls auf 
einem Mikrocontroller -- wo viele herkömmliche Empfehlungen nur 
teilweise oder gar nicht gelten. Und genau deswegen meine Frage: wie 
groß ist der Overhead in diesem speziellen Fall?

> Aber wie gesagt: erstens: exceptions sind old-school und zweitens: auf
> Minimal-Systemen möchte man sie eh nicht ...

Exceptions sind IMHO keineswegs old-school, sondern ein ausgesprochen 
wert- und sinnvolles Instrument zur sauberen Behandlung von 
Fehlersituationen. Ob man sie auf Minimal-Systemen will oder nicht, 
hängt allerdings nicht zuletzt davon ab, wieviel Mehraufwand an 
Ressourcen sie produzieren...

von Wilhelm M. (wimalopaan)


Lesenswert?

Sheeva P. schrieb:
> Wilhelm M. schrieb:
>> Sheeva P. schrieb:
>>> Wie groß ist eigentlich der Code Bloat, wenn man anstelle einer
>>> Exception-Instanz einen einfachen Integer-Fehlercode wirft (soetwas wie
>>> "throw 20;" statt "throw new RuntimeError("bla");")?
>>
>> Das sollte man nicht machen, da der Compiler das Objekt, das geworfen
>> wird (hier ein Zeiger) zerstört, aber nicht das Heap-Objekt: das müsste
>> man im catch nun explizit zerstören. Deswegen niemals heap-allocated
>> Objekte der Zeiger werfen. Oder SmartPtr. Aber die dyn. Allokation
>> produziert natürlich viel Laufzeit-Overhead.
>
> Schon klar, aber hier geht es ja um den besonderen Anwendungsfalls auf
> einem Mikrocontroller -- wo viele herkömmliche Empfehlungen nur
> teilweise oder gar nicht gelten. Und genau deswegen meine Frage: wie
> groß ist der Overhead in diesem speziellen Fall?

Schaus Dir an!

Aber wie gesagt: per raw-Pointer ist extrem Fehlerträchtig, da expl. 
delete notwendig.

>> Aber wie gesagt: erstens: exceptions sind old-school und zweitens: auf
>> Minimal-Systemen möchte man sie eh nicht ...
>
> Exceptions sind IMHO keineswegs old-school, sondern ein ausgesprochen
> wert- und sinnvolles Instrument zur sauberen Behandlung von
> Fehlersituationen.

Wie gesagt: std::optional oder splices oder exploding-return-types 
führen i.A. zu besserer Code-Struktur.

> Ob man sie auf Minimal-Systemen will oder nicht,
> hängt allerdings nicht zuletzt davon ab, wieviel Mehraufwand an
> Ressourcen sie produzieren...

Genau! Und da ist eine Exception auf dem Stack wesentlich 
leichtgewichtiger. Am besten als integral_constant<>, dann hat man alle 
Info im Typ und nicht als Wert und es reicht damit ein Byte!

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


Lesenswert?

Wilhelm M. schrieb:
> Und da ist eine Exception auf dem Stack wesentlich leichtgewichtiger. Am
> besten als integral_constant<>, dann hat man alle Info im Typ und nicht
> als Wert und es reicht damit ein Byte!

Vielleicht würdest du ja mal einen Wiki-Artikel hier im Wiki mit
Codebeispielen für sowas anfangen?  Das fände ich deutlich sinnvoller,
als hier im Thread, denn wer diese Dinge noch nicht in den Fingern
hatte, wird sich aus den paar Stichworten kaum einen Reim machen
können.

Vorschlag: C++ auf Mikrocontrollern

von Einer K. (Gast)


Lesenswert?

Jörg W. schrieb:
> Vorschlag: C++ auf Mikrocontrollern
Ja, das würde mich auch erfreuen.

Ich habe hier gerade ein kleines Programm vor mir liegen.
C++ ist der Standard im Arduino Umfeld.
Da stellt sich die Frage "Welche Programmiersprache auf µC?" also eher 
nicht.

In dem Programm werden ein paar Arduino Komfort Funktionen benutzt.
z.B. ein 2 Sekunden delay()
Und ein paar C++11 Features.

Viel tut das Programm nicht:
6 Taster abfragen, und die zugeordnete LED leuchten lassen.
Taste gedrückt, LED geht an.
Taste losgelassen, die LED geht nach 1 Sekunde aus.

Schickt man ein 'p' über die serielle Konsole, zeigt es die aktuelle Pin 
Zuordnung
1
const unsigned long wartezeit = 1000; // leuchtdauer in ms
2
3
struct LED  : Printable
4
{
5
  byte taster;
6
  byte led;
7
  unsigned long startzeit;
8
9
  LED(byte taster, byte led):taster(taster),led(led){}
10
11
  void init()
12
  {
13
    pinMode(led,OUTPUT);
14
    digitalWrite(led,0);
15
    pinMode(taster,INPUT_PULLUP);
16
  }
17
18
  void update()
19
  {
20
    if(digitalRead(led) && millis()-startzeit>wartezeit) digitalWrite(led,0);  
21
    if(!digitalRead(taster)) // invers, wg. internem Pullup
22
    {
23
      digitalWrite(led,1); 
24
      startzeit = millis();
25
    }
26
  }
27
  
28
  size_t printTo(Print& p) const
29
  {
30
    int len = 0;
31
    len+=p.print(F("TasterPin: "));
32
    len+=p.print(taster);
33
    len+=p.print(F("  LedPin: "));
34
    len+=p.print(led);
35
    len+=p.print(F("  Startzeit: "));
36
    len+=p.println(startzeit);
37
    return len;
38
  } 
39
40
};
41
42
LED ledGruppe[]
43
{
44
// {tasterpin,ledpin},
45
  {A0,13},
46
  { 2,12},
47
  { 3,11},
48
  { 4,10},
49
  { 5, 9},
50
  { 6, 8},
51
};
52
53
void serialEvent()
54
{
55
  while(Serial.available())
56
  {
57
    if('p' == Serial.read()) 
58
    {
59
      for(auto && i :ledGruppe) Serial.print(i);
60
      Serial.println();
61
    } 
62
  }
63
}
64
65
void yield()
66
{
67
  for(auto && i :ledGruppe) i.update();
68
}
69
70
void setup() 
71
{
72
  Serial.begin(9600);
73
  for(auto && i :ledGruppe) i.init();
74
}
75
76
void loop() 
77
{
78
   // nur alle 2 Sekunden auf die serielle Schnittstelle reagieren
79
   delay(2000);
80
}
Kompiliert mit der Arduino IDE und getestet auf einem UNO

So, und jetzt möge die ArduinoHasserFraktion über mich herfallen....

von Wilhelm M. (wimalopaan)


Lesenswert?

Arduino F. schrieb:
> Jörg W. schrieb:
>> Vorschlag: C++ auf Mikrocontrollern
> Ja, das würde mich auch erfreuen.
>
> Ich habe hier gerade ein kleines Programm vor mir liegen.
> C++ ist der Standard im Arduino Umfeld.
> Da stellt sich die Frage "Welche Programmiersprache auf µC?" also eher
> nicht.

Wenn Du Vergleichbarkeit mit anderen Realisierungen herstellen möchtest, 
wäre es gut wenn Du die Ausgabe von
1
$ avr-nm -SCn -t d xxx.elf

oder
1
$ avr-size xxx.elf

hier posten würdest und auch sagen würdest, welcher µC das ist. Denn 
viele werden dieses Beispiel nicht nachvollziehen können / wollen, weil 
sie (wie ich) Arduino agnostisch sind ...

von Einer K. (Gast)


Lesenswert?

Da will ich doch gerne drauf antworten


Wilhelm M. schrieb:
> Wenn Du Vergleichbarkeit mit anderen Realisierungen herstellen möchtest,
Möchte ich eigentlich nicht.... war zumindest nicht Ziel
Das Beispiel soll kein RessourcenSparWunder sein.


Wilhelm M. schrieb:
> sagen würdest, welcher µC
Mit "Getestet auf einem Arduino UNO" ist der ATMega328P gemeint.

Wilhelm M. schrieb:
> oder
> $ avr-size xxx.elf
Du meinst sowas?:
1
Der Sketch verwendet 2.930 Bytes (9%) des Programmspeicherplatzes. Das Maximum sind 32.256 Bytes.
2
Globale Variablen verwenden 240 Bytes (11%) des dynamischen Speichers, 1.808 Bytes für lokale Variablen verbleiben.
Die IDE nutzt intern avr-size.

128 Byte Ram gehen alleine für die Serial FiFos drauf

von Wilhelm M. (wimalopaan)


Lesenswert?

Arduino F. schrieb:
> Wilhelm M. schrieb:
>> Wenn Du Vergleichbarkeit mit anderen Realisierungen herstellen möchtest,
> Möchte ich eigentlich nicht.... war zumindest nicht Ziel

Ok, allerdings war das ja eigentlich das Kernthema hier.

> Du meinst sowas?:

... ja

>
1
> Der Sketch verwendet 2.930 Bytes (9%) des Programmspeicherplatzes. Das 
2
> Maximum sind 32.256 Bytes.
3
> Globale Variablen verwenden 240 Bytes (11%) des dynamischen Speichers, 
4
> 1.808 Bytes für lokale Variablen verbleiben.
> Die IDE nutzt intern avr-size.

Das ist natürlich schon mal amtlich!

> 128 Byte Ram gehen alleine für die Serial FiFos drauf

Und wo bleiben dann die anderen 112 Bytes?
Evtl. könntest Du die Zeile
1
     for(auto && i :ledGruppe) Serial.print(i);
testweise auskommentieren und nochmal die Größenangaben nennen (nur für 
den Spaß ;-))

von Einer K. (Gast)


Lesenswert?

Aber gerne doch...

Alles, was mit Serial zu tun hat, rausgeworfen.
1
Der Sketch verwendet 1.414 Bytes (4%) des Programmspeicherplatzes. Das Maximum sind 32.256 Bytes.
2
Globale Variablen verwenden 45 Bytes (2%) des dynamischen Speichers, 2.003 Bytes für lokale Variablen verbleiben.

// -----------------


Wenn es sich um weitere Optimierung dreht, müssten die andern Arduino 
Komfort Dinger auch noch raus fliegen.
digitalRead() und seine Kumpanen, sind auch relativ fett. Nicht nur die 
Funktionen an sich, sondern auch die Aufdröseltabellen(+Funktionen), 
welche diese brauchen, um von der Pinnummer auf die IO Register zu 
schließen.



Ein leerer Rumpf:
1
int main(void )
2
{
3
  for(;;);
4
  return 0;  
5
}
Sagt:
1
Der Sketch verwendet 134 Bytes (0%) des Programmspeicherplatzes. Das Maximum sind 32.256 Bytes.
2
Globale Variablen verwenden 0 Bytes (0%) des dynamischen Speichers, 2.048 Bytes für lokale Variablen verbleiben.

Das Kompilat besteht dann quasi  nur noch aus Interruptsprungleiste, 
Stack Initialisierung und leerer main().

Es ist also möglich den Komfort komplett zu entfernen.

In der Regel zieht aber das Argument:
Für ungenutzten Speicher gibts kein Geld zurück.


// ------------- Nachtrag:

> Evtl. könntest Du die Zeile
Das bringt nicht viel, 40 Byte Flash Einsparung.

Beitrag #5051474 wurde von einem Moderator gelöscht.
von S. R. (svenska)


Lesenswert?

Arduino F. schrieb:
> Es ist also möglich den Komfort komplett zu entfernen.

Wenn du auf setup() und loop() verzichtest und stattdessen dein eigenes 
main() baust, hast du aber effektiv keinen Arduino mehr. Dann sind 
Aussagen darüber witzlos.

Beitrag #5051970 wurde von einem Moderator gelöscht.
von Einer K. (Gast)


Lesenswert?

S. R. schrieb:
> Arduino F. schrieb:
>> Es ist also möglich den Komfort komplett zu entfernen.
>
> Wenn du auf setup() und loop() verzichtest und stattdessen dein eigenes
> main() baust, hast du aber effektiv keinen Arduino mehr. Dann sind
> Aussagen darüber witzlos.

Na, na...
Es ist, und bleibt, weiterhin eine C++ Umgebung.

Dass die mitgelieferten Arduino Funktionen keine Performance Wunder 
sind, ist nicht erst seit heute klar.

Die ansonsten positiven/willkommenen Eigenschaften, wie 
Wiederverwendbarkeit und Plattformneutralität(im Arduino Zoo), müssen 
der Performance geopfert werden, wenn es denn nötig ist.
Das ist keine so ganz unübliche Strategie. Findet man auch woanders.

von S. R. (svenska)


Lesenswert?

Arduino F. schrieb:
> Es ist, und bleibt, weiterhin eine C++ Umgebung.

Ja, aber keine Arduino-Umgebung mehr.

Wenn du in einem "Auto-vs-Fahrrad"-Vergleich deinen Golf nimmst und 
Motor, Getriebe, Türen, Scheiben, ... entfernst, dann hast du am Ende 
vielleicht etwas, was mit einem Fahrrad theoretisch vergleichbar ist. 
Aber es ist definitiv kein Auto mehr, oder zumindest für den Vergleich 
vollkommen untauglich.

Leere Firmwares sollten in C und C++ identisch sein. Interessant ist, 
wie hoch der Preis für "Arduino" ist, wenn man zwei vergleichbare 
Programme benutzt. Ob es sich im Einzelfall lohnt, muss jeder mit sich 
selbst ausmachen - wie bei printf und Freunden auch.

Ein guter Vergleich - aus meiner Sicht - wäre dein Programm in vier 
Varianten:
- "Arduino"
- "Arduino mit Abkürzungen" (also ohne digitalWrite / Serial)
- "C++" (aber mit der gleichen Struktur)
- "C" (als Vergleichsbasis)

von Einer K. (Gast)


Lesenswert?

S. R. schrieb:
> - "Arduino mit Abkürzungen" (also ohne digitalWrite / Serial)
Den Punkt kannst du beruhigt streichen, denn das ist dann, laut deiner 
eigenen Aussage, kein Arduino mehr.

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


Lesenswert?

S. R. schrieb:
> Wenn du auf setup() und loop() verzichtest und stattdessen dein eigenes
> main() baust, hast du aber effektiv keinen Arduino mehr.

Ich komme auf die gleiche Größe mit setup() und loop().  Die machen
das Kraut also nicht fett. ;-)

von S. R. (svenska)


Lesenswert?

Arduino F. schrieb:
> S. R. schrieb:
>> - "Arduino mit Abkürzungen" (also ohne digitalWrite / Serial)
> Den Punkt kannst du beruhigt streichen, denn das ist dann, laut deiner
> eigenen Aussage, kein Arduino mehr.

Da wolltest du mich dann absichtlich missverstehen. Aber gut, belassen 
wir's dabei, ich muss keine Haare spalten.

Jörg W. schrieb:
> Ich komme auf die gleiche Größe mit setup() und loop().  Die machen
> das Kraut also nicht fett. ;-)

Danke für den Test. ;-)

von Wilhelm M. (wimalopaan)


Lesenswert?

S. R. schrieb:

> Ein guter Vergleich - aus meiner Sicht - wäre dein Programm in vier
> Varianten:
> - "Arduino"
> - "Arduino mit Abkürzungen" (also ohne digitalWrite / Serial)
> - "C++" (aber mit der gleichen Struktur)
> - "C" (als Vergleichsbasis)

hier fehlt dann auch noch Assembler.

von Einer K. (Gast)


Lesenswert?

Wilhelm M. schrieb:
> hier fehlt dann auch noch Assembler.

Dann wirds aber auch schon enger, mit der Arduino IDE.
Das kann sie nur als inline Assembler, oder als *.S in Libs

von S. R. (svenska)


Lesenswert?

Wilhelm M. schrieb:
> hier fehlt dann auch noch Assembler.

Assembler habe ich absichtlich weggelassen, weil das kein sinnvoller 
Vergleich wird, sondern nur sinnbefreites Trollfutter.

von Wilhelm M. (wimalopaan)


Lesenswert?

S. R. schrieb:
> Wilhelm M. schrieb:
>> hier fehlt dann auch noch Assembler.
>
> Assembler habe ich absichtlich weggelassen, weil das kein sinnvoller
> Vergleich wird, sondern nur sinnbefreites Trollfutter.

Mmh, wenn die Anforderungen an den Vergleich klar sind, sehe ich das 
eigentlich nicht als "Trollfutter": man müsste mindestens noch 
hinzufügen, dass wir einen ms-genauen Systemtick haben wollen und einen 
UART mit jeweils x-Byte send/recv-Queue.

von Johannes S. (Gast)


Lesenswert?

Und was bringt so ein Vergleich? Wenn die Aufgabe mit der gewünschten 
Hard- und Software gelöst wurde und alle Anforderungen erfüllt sind ist 
das Problem doch gelöst. Die LED Klasse ist wiederwendbar, im main wo es 
um die reine Applikation geht muss ich mich nicht durch LowLevel 
Initialisierungen kämpfen, wenn mehr LED/Taster nötig werden ist 
lediglich die Tabelle zu erweitern, alles gut finde ich.

von Oliver J. (skriptkiddy)


Lesenswert?

Arduino F. schrieb:
> In der Regel zieht aber das Argument:
> Für ungenutzten Speicher gibts kein Geld zurück.

Sehe ich mittlerweile privat genau so. Ich kenne zwar einige Schrauben 
zum justieren des  Speicherverbrauchs, aber eine schöne C++-HAL ist mir 
die "Verschwendung" durchaus wert. Ich nehme gerne Interfaces (abstrakte 
Klassen) her, was natürlich RAM verbrät für die vtables, aber diese Art 
so zu coden gefällt mir richtig gut und da nehme ich das gerne in Kauf. 
Wobei ich die Tage ehrlich gesagt schon arg mit mir kämpfen musste: Ich 
stand ganz kurz davor aus RAM-spargründen virtuelle Methode aus einem 
Interface zu verbannen, die durchaus nützlich, jedoch nicht notwendig 
waren. Aber die Bequemlichkeit in der Benutzung hat gesiegt ;). Es 
bleibt jetzt so wie es ist.

Mal ganz ehrlich - wann schafft man es schon wirklich mal einen 
Atmega238P zu füllen? Der reicht für kleinere Steuerungsaufgaben doch 
sehr oft aus und Arduino bringt den auch nicht so schnell voll. Zur Not 
müsste man dann halt ein STM32-CM3 oder ähnliches hernehmen.

Wenn man natürlich auch privat den Anspruch hat immer auf alle 
Ressourcen
zu achten (die Phase hatte ich sehr lange), dann wird man mit so etwas 
wie Arduino nicht glücklich. ;)

Grüße Oliver

von Gerhard O. (gerhard_)


Lesenswert?

Langsam kristallisiert sich prinzipiell die folgende Tatsache heraus: 
Jeder soll eben auf seine Art selig werden und wie er seine Projekte 
nach besten Wissen und Können durchzieht. Ich finde, diese ganze 
Diskussion bringt wenig. Ganz abgesehen davon, wer mit einer 
Mikroprozessor/uC umgehen kann, sollte sehr wenig Mühe haben mit anderen 
umzugehen. Wenn man mit einer Programmiersprache gut umzugehen weiß, 
findet immer Lösungen seiner Probleme. Manchmal kommt mir vor man 
bewertet zu viele Programmiersprachen nach ihren *exappeal;-)

 Wenn man ehrlich ist, reicht für die alltäglichen 
Steueraufgabenprojekte auch der etwas bescheidene 328P und wer mehr 
Leistung und Platz braucht dem stehen genug Pfade zum Leistungsanstieg 
zur Auswahl. Für viele Anwendung ist es mir sowieso ziemlich 
gleichgültig auf welchen uC ich meine Projekte durchziehe. Solange der 
notwendige Funktionsumfang gegeben ist, spielen die alle ihre Liedchen.

Das war mein Freitagsbeitrag,
Schönes Wochenende!

P.S. Zeit einen Radausflug bei dem schönen Wetter zu machen anstatt im 
Labor zu verkümmern und wunderlich zu werden...

von Torsten R. (Firma: Torrox.de) (torstenrobitzki)


Lesenswert?

Oliver J. schrieb:
> Arduino F. schrieb:
>> In der Regel zieht aber das Argument:
>> Für ungenutzten Speicher gibts kein Geld zurück.
>
> Sehe ich mittlerweile privat genau so. Ich kenne zwar einige Schrauben
> zum justieren des  Speicherverbrauchs, aber eine schöne C++-HAL ist mir
> die "Verschwendung" durchaus wert.

Das hat weniger etwas mit privat vs. professionel zu tun. Sondern eher 
etwas mit den Stückzahlen. Auch ein "Profi" wird bei kleinen Stückzahlen 
versuchen, die Entwicklungszeiten zu reduzieren.

Eine "schöne" HAL würde aber auch keine Resourcen verschwenden. Darum 
geht es doch die ganze Zeit in dieser Diskusion: In C++ kannst Du 
Abstraktionen ohne Overhead schaffen!

> Ich nehme gerne Interfaces (abstrakte
> Klassen) her, was natürlich RAM verbrät für die vtables, aber diese Art
> so zu coden gefällt mir richtig gut und da nehme ich das gerne in Kauf.

Das ist Quatsch (mit Verlaub): die vtables sollten im ROM liegen, wenn 
Deine tool chain nicht kaput ist. Im RAM liegt maximal ein Zeiger auf 
den Anfang der Tabelle. Aber was willst Du mit Laufzeitpolymorphy, wenn 
in Deiner Applikation die Konfiguration statisch ist?

> Wobei ich die Tage ehrlich gesagt schon arg mit mir kämpfen musste: Ich
> stand ganz kurz davor aus RAM-spargründen virtuelle Methode aus einem
> Interface zu verbannen, die durchaus nützlich, jedoch nicht notwendig
> waren. Aber die Bequemlichkeit in der Benutzung hat gesiegt ;). Es
> bleibt jetzt so wie es ist.

Und, hast Du Dir den das Map-File mal genauer angeguckt, bevor Du mit 
Deinen Überlegungen angefangen hattest?

mfg Torsten

Beitrag #5052732 wurde von einem Moderator gelöscht.
von Oliver J. (skriptkiddy)


Lesenswert?

Torsten R. schrieb:
> Das ist Quatsch (mit Verlaub): die vtables sollten im ROM liegen, wenn
> Deine tool chain nicht kaput ist. Im RAM liegt maximal ein Zeiger auf
> den Anfang der Tabelle.
Kein Grund unhöflich werden.
Das handhabt der avr-g++ meines Wissens so. Da kann ich nichts dafür. 
Liegt wahrscheinlich an der PROGMEM-Problematik. Aber ist mir eigentlich 
auch egal, denn die paar Bytes stören mich ehrlich gesagt nicht wirklich 
solange ich noch genug Reserven hab.
P.S. das Teil hier schaut ganz interessant aus. Sollte ich mir 
wahrscheinlich mal ansehen:
https://github.com/jcmvbkbc/avr-flash-vtbl


Torsten R. schrieb:
> Aber was willst Du mit Laufzeitpolymorphy, wenn
> in Deiner Applikation die Konfiguration statisch ist?
So erzeuge ich halt Code. Mir ist leider keine Art bekannt z.b. ein 
Proxy-Pattern oder überhaupt Interfaces ohne so etwas komfortabel 
umsetzen. Dir?


Torsten R. schrieb:
> Das hat weniger etwas mit privat vs. professionel zu tun. Sondern eher
> etwas mit den Stückzahlen. Auch ein "Profi" wird bei kleinen Stückzahlen
> versuchen, die Entwicklungszeiten zu reduzieren.
Stimmt. Dachte das wäre jedem hier klar und hab mir die Passage im Post 
deshalb geschenkt.


Torsten R. schrieb:
> Und, hast Du Dir den das Map-File mal genauer angeguckt, bevor Du mit
> Deinen Überlegungen angefangen hattest?
Ja das hab ich. Ich hab mir sogar die vtables angesehen.


Torsten R. schrieb:
> Eine "schöne" HAL würde aber auch keine Resourcen verschwenden. Darum
> geht es doch die ganze Zeit in dieser Diskusion: In C++ kannst Du
> Abstraktionen ohne Overhead schaffen!
Das geht natürlich, aber wozu sollte ich das tun, wenn ich die 
Anforderung nicht habe?

P.S. Schätze mein ironisch gewählter Nickname hat wohl ein falsches Bild 
von mir vermittelt. Dafür muss ich mich dann wohl bei Dir entschuldigen. 
:D

Grüße Oliver

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Torsten R. schrieb:
> Das ist Quatsch (mit Verlaub): die vtables sollten im ROM liegen,
> wenn Deine tool chain nicht kaput ist.

G++ legt VTables nach .rodata, und .rodata wird vei avr-gcc/++ (außer 
für avrtiny und avrxmega3) ins RAM lokatiert.

Grund ist, dass bei avr-g++ und
 
1
char lese (const char *c)
2
{
3
    return *c;
4
}
 
nichts über die Ablage bekannt ist, und Lesen aus dem Flash andere 
Instruktionen erfordert als Lesen aus dem RAM.  Weil man nicht wirklich 
zur Laufzeit entscheiden will (und noch nichteinmal kann) liegt das 
Zeug im RAM.

Und VTables brachen nicht nur Platz sondern sind bei Verwendung auch 
langsamer.

: Bearbeitet durch User
von Oliver J. (skriptkiddy)


Lesenswert?

Johann L. schrieb:
> Und VTables brachen nicht nur Platz sondern sind bei Verwendung auch
> langsamer.
Das stimmt natürlich. Es muss ja immer erst über die die Adresse der 
Vtable und dem jeweiligen Offset die Methoden-Adresse geholt werden, 
bevor man reinspringen kann. Für hochfrequente Aufrufe wäre die 
Verwendung von virtuellen Methoden somit eher kontraproduktiv. Da 
verzichte ich natürlich drauf.

Grüße Oliver

von Wilhelm M. (wimalopaan)


Lesenswert?

Arduino F. schrieb:
> Aber gerne doch...
>
> Alles, was mit Serial zu tun hat, rausgeworfen.
>
1
> Der Sketch verwendet 1.414 Bytes (4%) des Programmspeicherplatzes. Das 
2
> Maximum sind 32.256 Bytes.
3
> Globale Variablen verwenden 45 Bytes (2%) des dynamischen Speichers, 
4
> 2.003 Bytes für lokale Variablen verbleiben.
5
>
>
> // -----------------

Vorher waren es 240 Bytes Ram, nun (ohne die UART-Instanz) 45 Bytes. 
Also 195 Bytes für ein UART-Objekt mit 2x64-Byte FiFos. Also irgendwo 
bleibt da ja recht viel auf der Strecke ... auch die 6 Led-Instanzen 
sind 36 Bytes.

> Wenn es sich um weitere Optimierung dreht, müssten die andern Arduino
> Komfort Dinger auch noch raus fliegen.

Auf keinen Fall. Es geht doch nicht darum, auf Komfort zu verzichten. 
Ganz im Gegenteil: ich will allen Komfort den ich bekommen kann - 
allerdings will ich dafür "nichts bezahlen". Und m.E. muss man dafür 
auch nichts bezahlen. Allerdings braucht es dafür auch die Bereitschaft, 
von manchem Standardvorgehen abzuweichen.

In Deinem Arduino Beispiel und generell ist es z.B. vollkommen unnötigt, 
die Information über den Pin der Led oder des Tasters im RAM abzulegen. 
Das ist eine statische Information, die in den Typ codiert werden 
sollte. Typ-Information benötigen keinen zusätzlichen Speicher. Und ja: 
ich kann mit Typen auch "rechnen" : das nennt sich dann Meta-Funktion. 
Bei solchen Anwendungen erlebt TMP eine gewisse Renaissance.

> digitalRead() und seine Kumpanen, sind auch relativ fett. Nicht nur die
> Funktionen an sich, sondern auch die Aufdröseltabellen(+Funktionen),
> welche diese brauchen, um von der Pinnummer auf die IO Register zu
> schließen.

Das kann der Compiler alles zur Compilezeit ausrechnen, dafür habe ich 
ja einen Compiler! Und nicht zur Laufzeit: das kosten RAM und 
CPU-Zyklen.

> Ein leerer Rumpf:
>
1
> 
2
> int main(void )
3
> {
4
>   for(;;);
5
>   return 0;
6
> }
7
>
> Sagt:
>
1
> Der Sketch verwendet 134 Bytes (0%) des Programmspeicherplatzes. Das 
2
> Maximum sind 32.256 Bytes.
3
> Globale Variablen verwenden 0 Bytes (0%) des dynamischen Speichers, 
4
> 2.048 Bytes für lokale Variablen verbleiben.

Jo, das ist normal.

> Es ist also möglich den Komfort komplett zu entfernen.

s.o., auf keinen Fall.

> In der Regel zieht aber das Argument:
> Für ungenutzten Speicher gibts kein Geld zurück.

Stimmt, meistens kostet es dann beim nächsten Update viel ... ;-)

von Wilhelm M. (wimalopaan)


Lesenswert?

Arduino F. schrieb:
> Aber gerne doch...
>
> Alles, was mit Serial zu tun hat, rausgeworfen.
>
1
> Der Sketch verwendet 1.414 Bytes (4%) des Programmspeicherplatzes. Das 
2
> Maximum sind 32.256 Bytes.
3
> Globale Variablen verwenden 45 Bytes (2%) des dynamischen Speichers, 
4
> 2.003 Bytes für lokale Variablen verbleiben.
5
>
>
> // -----------------

Vorher waren es 240 Bytes Ram, nun (ohne die UART-Instanz) 45 Bytes. 
Also 195 Bytes für ein UART-Objekt mit 2x64-Byte FiFos. Also irgendwo 
bleibt da ja recht viel auf der Strecke ... auch die 6 Led-Instanzen 
sind 36 Bytes.

> Wenn es sich um weitere Optimierung dreht, müssten die andern Arduino
> Komfort Dinger auch noch raus fliegen.

Auf keinen Fall. Es geht doch nicht darum, auf Komfort zu verzichten. 
Ganz im Gegenteil: ich will allen Komfort den ich bekommen kann - 
allerdings will ich dafür "nichts bezahlen".

In Deinem Arduino Beispiel und generell ist es z.B. vollkommen unnötigt, 
die Information über den Pin der Led oder des Tasters im RAM abzulegen. 
Das ist eine statische Information, die in den Typ codiert werden 
sollte. Typ-Information benötigen keinen zusätzlichen Speicher. Und ja: 
ich kann mit Typen auch "rechnen" : das nennt sich dann Meta-Funktion. 
Bei solchen Anwendungen erlebt TMP eine gewisse Renaissance.

> digitalRead() und seine Kumpanen, sind auch relativ fett. Nicht nur die
> Funktionen an sich, sondern auch die Aufdröseltabellen(+Funktionen),
> welche diese brauchen, um von der Pinnummer auf die IO Register zu
> schließen.

Das kann der Compiler alles zur Compilezeit ausrechnen, dafür habe ich 
ja einen Compiler! Und nicht zur Laufzeit: das kosten RAM und 
CPU-Zyklen.

> Ein leerer Rumpf:
>
1
> 
2
> int main(void )
3
> {
4
>   for(;;);
5
>   return 0;
6
> }
7
>
> Sagt:
>
1
> Der Sketch verwendet 134 Bytes (0%) des Programmspeicherplatzes. Das 
2
> Maximum sind 32.256 Bytes.
3
> Globale Variablen verwenden 0 Bytes (0%) des dynamischen Speichers, 
4
> 2.048 Bytes für lokale Variablen verbleiben.

Jo, das ist normal.

> Es ist also möglich den Komfort komplett zu entfernen.

s.o., auf keinen Fall.

> In der Regel zieht aber das Argument:
> Für ungenutzten Speicher gibts kein Geld zurück.

Stimmt, meistens kostet es dann beim nächsten Update viel ... ;-)

von Carl D. (jcw2)


Lesenswert?

Oliver J. schrieb:
> Johann L. schrieb:
>> Und VTables brachen nicht nur Platz sondern sind bei Verwendung auch
>> langsamer.
> Das stimmt natürlich. Es muss ja immer erst über die die Adresse der
> Vtable und dem jeweiligen Offset die Methoden-Adresse geholt werden,
> bevor man reinspringen kann. Für hochfrequente Aufrufe wäre die
> Verwendung von virtuellen Methoden somit eher kontraproduktiv. Da
> verzichte ich natürlich drauf.
>
> Grüße Oliver

Ich würde erst mal nachschauen, ob der GCC nicht selber gemerkt hat, daß 
es sich um Scheinvirtualirät handelt. Der hat nämlich einen 
"Devirtualizer". Man sollte ein virtuell formuliertes, von einem 
tatsächlich virtuellen Problem unterscheiden.

So wie eine mit diversen variadic Templates gebaute GPIO Lib durchaus 
ein
1
using Led = Avr::GpioBit<Avr::PortB,3,Avr::PinMode::Inverted>;
2
Led::Set();
zu
1
  CBI 0x05,3
werden kann.

von Oliver J. (skriptkiddy)


Lesenswert?

Carl D. schrieb:
> Ich würde erst mal nachschauen, ob der GCC nicht selber gemerkt hat, daß
> es sich um Scheinvirtualirät handelt. Der hat nämlich einen
> "Devirtualizer". Man sollte ein virtuell formuliertes, von einem
> tatsächlich virtuellen Problem unterscheiden.
Danke Dir. Das ist ein sehr guter Punkt. Den Compilerschalter 
'-fdevirtualize' kannte ich bisher noch nicht. Jedoch bringt er in 
meinem Fall leider keine Besserung.

Edit:
Es wurden definitiv vtables für folgende Verebungshierachien erzrugt:
Interface <- abstrakte Basisklasse <- n verschiedene Implementierungen
Interface <- ProxyKlasse

Grüße Oliver

: Bearbeitet durch User
von Wilhelm M. (wimalopaan)


Lesenswert?

Carl D. schrieb:

> So wie eine mit diversen variadic Templates gebaute GPIO Lib durchaus
> ein
>
1
using Led = Avr::GpioBit<Avr::PortB,3,Avr::PinMode::Inverted>;
2
> Led::Set();
> zu
>
1
  CBI 0x05,3
> werden kann.

Falsch: werden muss!
Wenn nicht, dann hat man etwas beim Design der Template-Bibliothek 
massiv(!) falsch gemacht.

von Carl D. (jcw2)


Lesenswert?

Wilhelm M. schrieb:
> Carl D. schrieb:
>
>> So wie eine mit diversen variadic Templates gebaute GPIO Lib durchaus
>> ein
>>
1
using Led = Avr::GpioBit<Avr::PortB,3,Avr::PinMode::Inverted>;
2
>> Led::Set();
>> zu
>>
1
  CBI 0x05,3
>> werden kann.
>
> Falsch: werden muss!
> Wenn nicht, dann hat man etwas beim Design der Template-Bibliothek
> massiv(!) falsch gemacht.

Keine Frage, nur wird ja oft behauptet "niemals werden kann" und wenn 
man die Arduino-Variante anschaut, dann versteht man warum.

von Oliver J. (skriptkiddy)


Lesenswert?

In meinen Augen hat auch die ArduinoVariante (damit meine ich das ganze 
Framework) durchaus ihre Daseinsberechtigung, gerade für die Zeilgruppe 
der Platform. Ich kann mir irgendwie nicht vorstellen, das sich eine 
Templateumsetzung genau so einfach handhabt. Kann mich aber natürlich 
auch täuschen.

Wobei wie schon gesagt die Arduinovariante ganz klar verliert, wenn man 
nichts zu verschenken hat.

Hier haben sich ein paar Leute mal zur Problematik "C++ Interface vs. 
Template" ausgelassen. Finde ich ziemlich interessant:
https://stackoverflow.com/questions/16586489/c-interface-vs-template

Grüße Oliver

: Bearbeitet durch User
von Carl D. (jcw2)


Lesenswert?

Oliver J. schrieb:
> In meinen Augen hat auch die ArduinoVariante (damit meine ich das ganze
> Framework) durchaus ihre Daseinsberechtigung, gerade für die Zeilgruppe
> der Platform. Ich kann mir irgendwie nicht vorstellen, das sich eine
> Templateumsetzung genau so einfach handhabt. Kann mich aber natürlich
> auch täuschen.

Man könnte einiges verbessern. So ist z.B. kein dynamisches Mapping von 
Pins erforderlich und es ist zur Übersetzungszeit auch kein Geheimnis, 
welche Pins der Ziehlplattform abzuschaltende Zusatzfunktionen ausführen 
(PWM). Da die Arduine-Variant aber in einer unübersehbaren Zahl von 
Libraries verwendet wird, müßte man diese auch anpassen. Schlechtes 
Base-Library-Design hat eben was virulentes.

> Hier haben sich ein paar Leute mal zur Problematik "C++ Interface vs.
> Template" ausgelassen. Finde ich ziemlich interessant:
> https://stackoverflow.com/questions/16586489/c-interface-vs-template

Ja, da geht es bei einer Antwort bzgl. Performance um Intel-PC's, die 
mit indirekten CALL's weniger Problem wie ein 16MHz 8bitter haben.
Das Argument "Compile Time zu lang" ist auf μC Zielsystemen auch keins. 
Ob der GCC für "plain C" für eins meiner Projekte 100ms braucht, oder 
mit "Template-Abstraktions-Wahnsinn" 700ms, ist mir egal. Dabei sind das 
700% mehr!

Und alle anderen sagen klar: Templates! Wobei es möglich ist, ein 
Interface mit virtuellen Methoden und einer boardspezifischen 
Implementierung zu haben. Wenn der Compiler die Source der letzteren 
hat, kann er daraus leicht ein "non-virtuelles" Binary machen.

von Torsten R. (Firma: Torrox.de) (torstenrobitzki)


Lesenswert?

Oliver J. schrieb:
> Torsten R. schrieb:
>> Das ist Quatsch (mit Verlaub):
> Kein Grund unhöflich werden.

Das hast Du Recht: Entschuldige bitte.

> Torsten R. schrieb:
>> Aber was willst Du mit Laufzeitpolymorphy, wenn
>> in Deiner Applikation die Konfiguration statisch ist?

> Mir ist leider keine Art bekannt z.b. ein
> Proxy-Pattern oder überhaupt Interfaces ohne so etwas *komfortabel*
> umsetzen. Dir?

Wenn Du tatsächlich in Deiner Applikation sowohl mit einer lokalen, als 
auch einer remote Gegenstelle kommunizieren must, dann ist das 
Proxy-Pattern natürlich passend. Dann kannst Du ggf. Entscheidungen erst 
zu Laufzeit treffen.

Wenn Du aber zwei Applikationen hast, und in der einen kommuniziert eine 
Komponente immer mit einem lokalen Gegenstück und in der anderen 
kommuniziert diese Komponente tatsächlich immer mit einem remote 
Gegenstück, dann möchtest Du diese Komponente natürlich gerne in beiden 
Applikationen verwenden, must aber keine Laufzeitpolymorpie verwenden, 
weil bereits zur Compile-Zeit klar ist, welches Gegenstück verwendet 
wird:
1
class remote
2
{
3
public:
4
    void do_it();
5
private:
6
    /* state */
7
    ...
8
};
9
10
class local
11
{
12
public:
13
    void do_it();
14
};
15
16
template < class DoIt >
17
class algorithm : private DoIt
18
{
19
public:
20
    void do()
21
    {
22
        ...
23
        this->do_it();
24
        ...
25
    }
26
};

algorithm erbt von der Implementierung (das ist unüblich), erlaubt aber 
empty base class optimization im Fall, dass DoIt selbst keine Daten hat 
(wie im Fall local angedeutet). Wenn klar ist, dass Teile keinen State 
haben werden, kann man die Funktionen auch static definieren. Das ist 
nur ein Beispiel, wie man soetwas erreichen kann.

Stichworte: Compile Time Polymorphie, Duck Typing, Curiously Recurring 
Template Pattern, Traits, Mixins.

> Torsten R. schrieb:
>> Eine "schöne" HAL würde aber auch keine Resourcen verschwenden. Darum
>> geht es doch die ganze Zeit in dieser Diskusion: In C++ kannst Du
>> Abstraktionen ohne Overhead schaffen!
> Das geht natürlich, aber wozu sollte ich das tun, wenn ich die
> Anforderung nicht habe?

Wenn Du nicht die Anforderungen hast, dass es schön werden soll, dann 
wird es natürlich nicht schön ;-)

mfg Torsten

von Oliver J. (skriptkiddy)


Lesenswert?

Torsten R. schrieb:
> Wenn Du tatsächlich in Deiner Applikation sowohl mit einer lokalen, als
> auch einer remote Gegenstelle kommunizieren must, dann ist das
> Proxy-Pattern natürlich passend. Dann kannst Du ggf. Entscheidungen erst
> zu Laufzeit treffen.
Bei mir muss nix zur Laufzeit entschieden werden. Der Compiler bekommt 
nur halt nicht alles auf einen Schlag. Wenn ich mal in Platznot komme, 
dann werde ich sicher nen Unitybuild hernehmen. Dann sollten die vtables 
ja verschwinden. ;)

Ich erläutere einfach mal was ich umgesetzt hab.
Konkret geht es bei mir um die I2C-Driver Architektur. Jedes Device 
bekommt im Konstruktor eine Referenz auf einen I2C-Bus. Der Bus ist 
komplett abstrakt. Das Pattern Dependency Injektion hab ich deswegen 
gewählt, weil sich in meinen Augen so ganz einfach Mocks in die 
TreiberKlassen reingeben lassen und so der UnitTest zum Kinderspiel 
wird. Abstrakt ist das ganze, weil ich zwei verschiedene 
Implementierungen von I2C hab (Hardware und Software-Bitbanging) so kann 
ich je nach Lust und Laune eine der beiden Implementierungen nehmen, je 
nachdem was gerade benötigt wird und die IC-Treiber brauchen kein Wissen 
darüber, was sich dahinter befindet. Der Proxy den ich erwähnt hab dient 
dem Zugriff auf 8 verschiedene I2C-Busse über einen I2C-Multiplexer. Da 
der Proxy quasi vom Interface her ein I2C-Bus ist, kann ich ihn wieder 
bequem in die IC-Treiber hineingeben.

Torsten R. schrieb:
> Wenn Du nicht die Anforderungen hast, dass es schön werden soll, dann
> wird es natürlich nicht schön ;-)
Gut das Schönheit im Auge des Betrachters liegt. ;)

Mir ist natürlich Bewusst, dass das was ich tu nicht wirklich für so 
kleine Platformen geeignet ist, aber es funktioniert trotzdem in meinen 
Projekten gut und ich habe wie schon gesagt keine Schmerzen bei der 
Performance und Platz ist in der Egel auch genug da. Ein so umgesetzter 
I2C-EEPROM-Programmer auf einem Atmega32U4 schafft über die USB-Serielle 
von Arduino und dem HardwareI2C-Treiber einen 24C512 in 5 Sekunden 
komplett zu befüllen und zu verifizieren. Damit bin ich mehr als 
zufrieden. ;) Wahrscheinlich geht da noch etwas mehr, aber für mich 
ausreichend.

von Wilhelm M. (wimalopaan)


Lesenswert?

Meines Erachtens werden bei der ganzen (und vorherigen Diskussionen in 
diesem
Forum) zwei grundsätzliche Dinge nicht beachtet:

1) Die HW ist zu 99% in einer Applikation fest: damit meine ich, dass 
GPIO-Register feste Adressen haben, die angeschlossenen externen 
Perioheriekomponenten sind den korrespondierenden Pins fest zugeordnet, 
es gibt eine definierte Anzahl von internen / externen HW-Komponenten 
wie UART/I2C/SPI/RTC/USB/... mit definierten Kontrollregistern. Kurzum, 
an der HW gibt es wenig bis nichts Dynamisches.

2) Die Software-Komponenten aus der Anwendungsdomäne müssen sehr wohl 
dynamisch sein.

Zu 1): hier ist die Frage, wie man statische Sachverhalte modelliert. 
Ein Weg wäre (wie etwa auch in den Arduino-Bibs) zunächst alles 
dynamisch zu modellieren. Anschließend muss man sich dann an die 
statische Natur anpassen. Wie? Da hält die OO-Küche zwar auch einige 
Pattern und Idiome bereit wie etwa Singleton oder Monostate oder Facade, 
aber das ist m.E. das Pferd falsch herum aufgezäumt.
Weiß ich über die statische Natur, so modelliere ich auch hier statisch, 
d.h. es gibt dann eben ein MCU::Uart<0> und MCU::Uart<1>, aber die 
template-Instanziierung von Uart<2> führt zu einem Compilezeitfehler. 
Auch führt dieses Vorgehen nur zu ganz geringem bzw. gar keinem 
Code-Bloat, wenn im MCU::Uart-Template nur die tatsächlich von der 
konkreten Instanziierung abhängigen Code-Anteile drin sind. Oder bei der 
verstrubbelten Timer-Modularität der AVRs sind dort dann eben nur die 
spezifischen Anteile in MCU::Timer<0>. Bei den GPIOs ist das natürlich 
trivial. Das führe ich weiter bis zum einzelnen Pin, also etwa 
Pin<Port<B>, 3>.

zu 2) Hier brauchte ich statische oder begrenzt dynamische Container, 
d.h. Container, die nur bis zu einer bestimmten Kapazität gefüllt werden 
können (sofern ich keinen µC mit MMU und dyn. Seitenersetzungsverfahren 
habe oder gleich ein OS habe). Hier kommt man zu 99% mit den templates 
analog zu std::array<> bzw. so etwas wie stringbuffer<>, fifo<>, 
fixedvector<> und stringview<> aus.
Auch im klassischen Anwendungsumfeld wie Messen/Steuern/Regeln wird man 
noch ohne Laufzeitpolymorphie auskommen. Hier steht ja auch oft die 
Verarbeitungsgeschwindigkeit im Vordergrund. An der Grenze zu 
komplexeren UIs wird man dann um Laufzeitpolymorphie wohl nicht umhin 
kommen. Hier sind dann aber die Laufzeitanforderungen moderat, so daß 
man sich das dann auch leisten kann.

Im Übrigen kann man die klassischen Laufzeit-Ansätze wie etwa die 
Iteration über alle Elemente eines Containers, etwa
1
for(const auto& item : container) {...}

analog im statischen haben: das nennt sich klassisch in C++ 
template-meta-programmierung: hier hat man es mit Meta-Funktionen zu tun 
(Meta-Funktionen sind selbst wieder templates, denn man "rechnet" ja 
hier mit Typen und nicht mit Werten):

Mit
1
template<typename... T>
2
struct List {
3
       static void init() {
4
               (T::init(), ...);
5
       }
6
};

kann man
1
using led1 = Pin<Port, B>, 1>;
2
using led2 = Pin<Port, B>, 4>;
3
//...
4
using allLeds = List<led1, led2>;
5
allLeds::init();
verwenden, und damit
kann man etwa mit Faltungsausdrücken iterieren (zur Compilezeit). Was 
dabei herauskommt ist eine Folge (keine Schleife, kein Funktionsaufruf) 
von Initialisierungsanweisungen.

Hat man sich mit dieser TMP-Technik etwas angefreundet, kann man das 
obige in der Schreibweise weiter verkürzen, indem man ein paar 
allgemeine Meta-Funktionen zum Manipulieren von Typ-Listen schreibt:
1
using leds = Meta::List<led1, led2>;
2
using buttons = Meta::List<button1, button2, button3>;
3
4
using devices = Meta::concat<leds, buttons>;
5
using initializer = Meta::transform<Initialize, devices>;
6
7
initializer::init();
8
9
using devices = Meta::apply<function, leds>;

Die using-Deklarationen sind gewissermaßen Meta-Funktionsaufrufe zur 
Compilezeit.

Noch schöner wird das ganze, durch concepts und constraints (ab z.B. 
g++-6.1 enthalten). Ein constraints ist in etwa vergleichbar mit einem 
Interface zur Laufzeit: damit kann man eine implizite 
Template-Typanforderung explizit machen:
1
template<typename T>
2
concept bool isIsrCallback() {
3
  return requires(T t) {
4
         T::isr();
5
         T::interrupt_number;
6
         typename T::interrupt_type;
7
  };
8
}

Auf die Art und Weise kann man dann z.B. die Template-Typ-Parameter 
beschränken: man verwendet obiges concept als sog. contraint in einer 
template-definition:
1
template<typename T>
2
requires isIsrCallback<T>() struct Controller {};

oder kürzer
1
template<isIsrCallback T> struct Controller {};

Das alles habe ich nur angerissen, doch ich kann eigentlich jedem nur 
empfehlen, sich mit diesen Dingen, v.a. eben varidischen templates, 
fold-expressions, constexpr, IIFE, ... auseinander zu setzen. Meines 
Wissens gibt es dazu bislang kein gute und umfassendes neues(!) Buch 
(alte zu TMP schon, aber die benutzen meistens noch alte Paradigmen wie 
SFINAE, ...).

von Vincent H. (vinci)


Lesenswert?

Wilhelm M. schrieb:
> Das alles habe ich nur angerissen, doch ich kann eigentlich jedem nur
> empfehlen, sich mit diesen Dingen, v.a. eben varidischen templates,
> fold-expressions, constexpr, IIFE, ... auseinander zu setzen. Meines
> Wissens gibt es dazu bislang kein gute und umfassendes neues(!) Buch
> (alte zu TMP schon, aber die benutzen meistens noch alte Paradigmen wie
> SFINAE, ...).


Vielleicht solltest du ein Buch ala "modern embedded C++" schreiben. ;)

Schreibst du selbst deine ganzen Bibliotheken? Ich hab mir nämlich 
vieles von dir abgeschaut und teils auch selbst nach bestem Wissen eine 
"template library" ala "Uart<1>" für die STM32L4 Serie gebastelt.

Leider stößt man dabei immer wieder auf einige Steinchen.

Anstelle von "Uart<1>" verwende ich sowas wie "Uart<DeviceTree:Uart1>", 
wo etwa Register Definitionen, Interrupts, DMA Kanäle, usw. drin sind. 
Das funktioniert soweit ganz gut, was ich aber sehr mühsam finde ist, 
wenn etwa Uart1 2x Features hat, die Uart2 nicht bietet.

Aktuell verwende ich eben SFINAE um diverse (statische) Methoden im 
Template zu deaktivieren, sollte jenes Feature in der entsprechenden 
Peripherie nicht vorhanden sein. Leider artet das bei komplexen Dingen 
wie etwa den STM Timern in sehr mühsame Arbeit aus... Da gibts aktuell 
glaub ich 5-6 verschiedene Timer Implementierungen, die alle ein klein 
wenig anders sind. Wie gehst du mit sowas um?

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


Lesenswert?

Vincent H. schrieb:
> Vielleicht solltest du ein Buch ala "modern embedded C++" schreiben. ;)

Ich schrieb doch schon: ein Artikel hier im Wiki wäre wirklich sehr
sinnvoll.  Es nützt einem nicht viel, wenn jemand die ganze Zeit
nur sagt: „Ist doch ganz einfach!“  Es ist viel hilfreicher, wenn
diejenigen, die wissen, wie es ganz einfach geht, ihr Wissen auch
weitergeben - nicht nur in einem Thread, sondern an einer Stelle, wo
man es auch später wiederfindet.  Das Wiki hier parallel zum Forum
ist ein guter Platz dafür.

von Jan K. (jan_k)


Lesenswert?

So ein interessanter Thread! Bitte bitte liebe C++ cracks, teilt etwas 
von eurem Wissen in einem Wiki Artikel, es wäre zu schade, wenn solche 
Sachen hier untergingen.

von Wilhelm M. (wimalopaan)


Lesenswert?

Vincent H. schrieb:
> Vielleicht solltest du ein Buch ala "modern embedded C++" schreiben. ;)

Ja, so etwas entsteht gerade ...

> Schreibst du selbst deine ganzen Bibliotheken?

Ja ... (das hat die unterschiedlichsten Gründe)

> Leider stößt man dabei immer wieder auf einige Steinchen.

Sonst wäre es ja nicht spannend ...

> Aktuell verwende ich eben SFINAE um diverse (statische) Methoden im
> Template zu deaktivieren, sollte jenes Feature in der entsprechenden
> Peripherie nicht vorhanden sein. Leider artet das bei komplexen Dingen
> wie etwa den STM Timern in sehr mühsame Arbeit aus... Da gibts aktuell
> glaub ich 5-6 verschiedene Timer Implementierungen, die alle ein klein
> wenig anders sind. Wie gehst du mit sowas um?

Irgendeiner muss die Arbeit ja machen, wenn die HW-Struktur so ... ist. 
Und in solchen Fällen eine akzeptable Lösung zu finden, ist wirklich 
eine Kärrnerarbeit.

Also ich persönlich finde SFINAE zwar nützlich, aber eigentlich nur bei 
freien Funktionstemplates sinnvoll, um Überladungen für bestimmten Typen 
zu aktivieren. Der Klassiker ist hier wohl im µC-Bereich, dass man für 
die Bits der Kontrollregister scoped enums einführt, und dann gezielt 
mit einem Trait die Operatoren (etwa |) einschaltet (type safe 
registers), die man zulassen möchte.

Überladungen in Klassentemplates per SFINAE ein/auszuschalten finde ich 
nicht so "schön".  Die Alternative, Template-Spezialisierungen zu 
verwenden (full-specializations) ist auch unpraktisch, weil ich gerne 
header-only Bibliotheken möchte (dann muss man sich nicht um das Bauen 
der Bibliotheken kümmern), und dass dann ggf. zu ODR-Problemen führt.

Die m.E. gangbare Variante ist es, über Constraints 
Klassentemplatespezialisierungen einzuführen und ggf. gemeinsame Anteile 
als CRTP / mixin zu bauen. Der Vorteil hier ist, dass man ein Concept 
schreiben kann, dass wie eine vollst. Spezialisierung wirkt, aber zu 
keinen ODR-Problemen führt, und das es sehr kompakt formuliert werden 
kann. Andernfalls hat man ja im(!) Template x-fach dieses SFINAE 
Gewurschtel drin.
Dies bedeutet zugegebener Maßen, dass Du einen Compiler brauchst, der 
Concepts unterstützt (wie etwa gcc ab 6.1).

von Wilhelm M. (wimalopaan)


Lesenswert?

Jörg W. schrieb:
> Vincent H. schrieb:
>> Vielleicht solltest du ein Buch ala "modern embedded C++" schreiben. ;)
>
> Ich schrieb doch schon: ein Artikel hier im Wiki wäre wirklich sehr
> sinnvoll.

Auch das habe ich gelesen ... ;-)

> Es nützt einem nicht viel, wenn jemand die ganze Zeit
> nur sagt: „Ist doch ganz einfach!“  Es ist viel hilfreicher, wenn
> diejenigen, die wissen, wie es ganz einfach geht, ihr Wissen auch
> weitergeben - nicht nur in einem Thread, sondern an einer Stelle, wo
> man es auch später wiederfindet.  Das Wiki hier parallel zum Forum
> ist ein guter Platz dafür.

Da ich die Arbeit nicht mehrfach machen möchte, werde ich vermutlich ein 
eigenes Dokument dort einstellen. Denn ich erzeuge alle techn. Doku mit 
Asciidoctor inkl. ein paar Tools, Ausgabeformate sind dann html, pdf, 
ebub, ... Originär hier im Wiki zu schreiben, ist für mich zu 
umständlich.

: Bearbeitet durch User
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.