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.
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?
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.
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.
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.
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.
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. ;-)
@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.
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.
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.
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
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.
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. ;-)
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.
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.
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.
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.
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.
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.
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.
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?
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
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.
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
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.
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
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!
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.
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.
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.
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.
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 …
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.
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.
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 ...
Wilhelm M. schrieb: > M.E. ist eine 100%-Kompatibilität zur STL auch nicht erstrebenswert Dem Fuchs waren die Trauben damals auch zu sauer …
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.
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.
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...
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.
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
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.
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]; |
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.
Programmiersprachentheaterintendant schrieb: > Für alle 3 genannten Gruppen gilt: jeder darf bei seinem > Lieblingswerkzeug bleiben, ... ! unterschreib !
oh hallo Yalu, das Mikroskop für EBNF wieder beiseite gestellt? :-)
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.
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?
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.
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.
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.
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 :-)
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.
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.
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.
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.
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.
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.
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...
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)
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.
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...
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.
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.
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.
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. :)
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
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.
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?
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.
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.
> 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.
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.
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...
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
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
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
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.
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.
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).
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.
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.
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");")?
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 ...
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...
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!
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
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....
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 ...
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
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ß ;-))
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.
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.
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.
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)
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.
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. ;-)
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. ;-)
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.
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
Wilhelm M. schrieb: > hier fehlt dann auch noch Assembler. Assembler habe ich absichtlich weggelassen, weil das kein sinnvoller Vergleich wird, sondern nur sinnbefreites Trollfutter.
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.
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.
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
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...
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.
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
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
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
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 ... ;-)
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 ... ;-)
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.
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
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.
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.
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
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.
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
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.
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, ...).
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?
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.
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.
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).
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.