Ist C++ für eingebettete Systeme geeignet?

Eine häufig gestellte Frage, hier und anderswo. Ist C++ für eingebettete Systeme geeignet?

Mikrocontroller? Echtzeitbetriebssysteme? Toaster? Embedded-PCs?

Ist OOP auf Mikrocontrollern nützlich?

Entfernt C++ den Programmierer zu weit von der Hardware, um effizient zu sein?

Sollte Arduinos C++ (ohne dynamische Speicherverwaltung, Vorlagen, Ausnahmen) als "echtes C++" betrachtet werden?

(Hoffentlich dient dieses Wiki als Ort, um diesen potenziellen heiligen Krieg einzudämmen)

Kurze Frage: Wenn Sie eingebettet sagen , meinen Sie Mikrocontroller? Mikroprozessor? eingebetteter x86 / eingebetteter PC?
C++ vs. Embedded ist ein umstrittenes Thema. Ich habe eine starke Meinung, aber ich fand es nicht fair, eine Frage zu stellen und um Punkte zu spielen. Ich hoffe, dass ein Community-Wiki für eine ausgewogenere Diskussion sorgen wird.
Ich habe verstanden, warum Sie es getan haben, ich stütze meine Meinung darauf, was es ermöglicht, eine Arbeit gut und schnell zu erledigen, aber die meisten Leute, die ich kenne, halten es für ein heißes Thema und werden schnell rot.
Ich stimme @Joby zu, dies ist ein 100% klares Community-Wiki ...
Ja, ich habe etwas überlesen und dachte, jemand anderes sei das Originalplakat. Mein Fehler, ich lösche meine Kommentare.
Dies ist eine schlechte Frage, da "eingebettet" ein bedeutungsloses Attribut bei der Entscheidung ist, ob eine bestimmte Sprache und das damit verbundene Gepäck geeignet sind. Der Punkt ist, dass kleine im Vergleich zu großen Systemen, wo kleine Systeme kein Betriebssystem ausführen, begrenzten Speicher haben, möglicherweise nicht von-Neuman sind, möglicherweise verschiedene Hardwareeinschränkungen für Aufrufstapel und Datenstapel haben und Sie nicht einfach dynamisch ein MB zuweisen können oder sogar ein kb usw. Die meisten Mikrocontroller sind "kleine" Systeme. Einplatinencomputer sind normalerweise eingebettet, aber im Allgemeinen "große" Systeme.
Die Frage scheint "c++=OOP" zu implizieren. Das stimmt überhaupt nicht, C++ ist eine Multi-Paradigmen-Sprache, sie kann OOP, streng prozedural, funktional, was auch immer!

Antworten (16)

Ja, C++ ist in eingebetteten Systemen immer noch nützlich. Wie alle anderen gesagt haben, hängt es immer noch vom System selbst ab, wie ein 8-Bit-uC in meinem Buch wahrscheinlich ein No-No wäre, obwohl es einen Compiler gibt und einige Leute es tun (schaudern). Die Verwendung von C++ hat immer noch einen Vorteil, selbst wenn Sie es auf etwas wie "C+" herunterskalieren, selbst in einer 8-Bit-Mikrowelt. Was meine ich mit "C+"? Ich meine, verwenden Sie nicht new/delete, vermeiden Sie Ausnahmen, vermeiden Sie virtuelle Klassen mit Vererbung, vermeiden Sie möglicherweise Vererbung insgesamt, seien Sie sehr vorsichtig mit Vorlagen, verwenden Sie Inline-Funktionen anstelle von Makros und verwenden Sie constVariablen anstelle von #defines.

Ich arbeite jetzt seit weit über einem Jahrzehnt sowohl in C als auch in C++ in eingebetteten Systemen, und ein Teil meiner jugendlichen Begeisterung für C++ ist aufgrund einiger realer Probleme, die die Naivität erschüttern, definitiv nachgelassen. Ich habe das Schlimmste von C++ in einem eingebetteten System gesehen, das ich als „CS-Programmierer, die in einer EE-Welt wild geworden sind“ bezeichnen möchte. Daran arbeite ich tatsächlich mit meinem Kunden, um unter anderem diese eine Codebasis zu verbessern.

Die Gefahr von C++ besteht darin, dass es ein sehr, sehr mächtiges Werkzeug ist, ähnlich wie ein zweischneidiges Schwert, das Ihnen sowohl den Arm als auch das Bein abschneiden kann, wenn Sie nicht richtig in seiner Sprache und der allgemeinen Programmierung selbst ausgebildet und diszipliniert sind. C ist eher wie ein einschneidiges Schwert, aber immer noch genauso scharf. Mit C++ ist es zu einfach, sehr hohe Abstraktionsebenen zu erreichen und verschleierte Schnittstellen zu erstellen, die auf lange Sicht bedeutungslos werden, und das liegt teilweise an der Flexibilität von C++, dasselbe Problem mit vielen verschiedenen Sprachfunktionen (Vorlagen, OOP, prozedurale, RTTI, OOP+Templates, Überladen, Inlining).

Ich habe zwei 4-stündige Seminare über eingebettete Software in C++ von dem C++-Guru Scott Meyers absolviert. Er wies auf einige Dinge über Vorlagen hin, die ich zuvor nie in Betracht gezogen hatte, und wie viel mehr sie beim Erstellen von sicherheitskritischem Code helfen können. Die Quintessenz ist, dass Sie keinen toten Code in Software haben können, die strenge sicherheitskritische Codeanforderungen erfüllen muss. Vorlagen können Ihnen dabei helfen, da der Compiler nur den Code erstellt, den er beim Instanziieren von Vorlagen benötigt. Man muss sich jedoch gründlicher in ihrer Verwendung ausbilden, um für dieses Feature richtig zu entwerfen, was in C schwieriger zu erreichen ist, da Linker nicht immer toten Code optimieren.

Scott Meyers ist ein sehr großer Befürworter von Templates und der vernünftigen Verwendung von Inlining, und ich muss sagen, dass ich immer noch skeptisch bin, was Templates betrifft. Ich neige dazu, davor zurückzuschrecken, obwohl er sagt, dass sie nur dort angewendet werden sollten, wo sie das beste Werkzeug werden. Er weist auch darauf hin, dass C++ Ihnen die Werkzeuge gibt, um wirklich gute Schnittstellen zu erstellen, die einfach richtig zu verwenden sind und es schwierig machen, sie falsch zu verwenden. Auch das ist der schwierige Teil. Man muss C++ beherrschen, bevor man weiß, wie man diese Funktionen am effizientesten anwendet, um die beste Designlösung zu sein.

Dasselbe gilt für OOP. In der eingebetteten Welt müssen Sie sich damit vertraut machen, welche Art von Code der Compiler ausspuckt, um zu wissen, ob Sie die Laufzeitkosten des Laufzeitpolymorphismus bewältigen können. Sie müssen bereit sein, auch Messungen vorzunehmen, um zu beweisen, dass Ihr Design Ihre Terminanforderungen erfüllt. Wird diese neue InterruptManager-Klasse meine Interrupt-Latenz zu lang machen? Es gibt andere Formen von Polymorphismus, die möglicherweise besser zu Ihrem Problem passen, wie z .

Ich sage das nur, um zu sagen, dass C++ seinen Platz in der Embedded-Welt hat. Du kannst es hassen, so viel du willst, aber es geht nicht weg. Es kann auf sehr effiziente Weise geschrieben werden, aber es ist schwieriger zu lernen, wie man es richtig macht, als mit C. Es kann manchmal besser als C funktionieren, um ein Problem zu lösen und manchmal eine bessere Schnittstelle auszudrücken, aber auch hier müssen Sie es tun bilden Sie sich und haben Sie keine Angst zu lernen, wie.

Dies stimmt mit dem überein, was ich von anderen Beratern für eingebettete Systeme gelesen habe. Mir wurde immer beigebracht, dass man sich mit C ständig in kleine Teile schneidet, aber ein Fehler in C++ wird seltener sein, aber wenn man es vermasselt, verliert man ein Bein. Vielen Dank, dass Sie eine klare Antwort mit einigen Fachkenntnissen geschrieben haben, die ich nicht habe.
Mir wurde gesagt, dass Sie wirklich Zeit aufwenden und sich in den richtigen Techniken ausbilden lassen müssen, bevor Sie sich kopfüber in C++ stürzen.
In Anbetracht der Tatsache, dass Informatik-Majors im EE-Land verrückt werden. Bei meiner Arbeit wurde das schlechteste Stück Code, das wir haben, von einem CS-Major geschrieben. Wir verbrachten ewig damit, ihm beizubringen, was die Hardware war. Er erstellte ein strukturiertes System unter Verwendung von UML und baute das gesamte System darauf basierend in Java unter Verwendung der richtigen Vererbung und so weiter auf. Es funktionierte, bis sich etwas änderte, und dann war es ein schlechter Patch-Job, um Funktionen hinzuzufügen, oder ein komplettes Redesign. Der Code ist fast nicht verwendbar, weil er das Ganze mit Vererbung verschleiert hat.
Es sind nicht nur Bugs, die Sie beißen, sondern auch nicht wartbarer Code. Sicher, wenn Sie dieses Projekt mit diesen netten C++-Funktionen beginnen, läuft alles reibungslos, aber nach 2 oder 3 Jahren tritt Entropie ein, wenn Sie während der Entwicklung keine ernsthaften Anstrengungen in das Refactoring stecken. Das ist es, womit ich gerade konfrontiert bin ... der Code verrottet mit der Zeit in C++ schneller.
UML und Zustandsmaschinen. Sie müssen sich unbedingt die Sachen von Miro Samek auf state-machine.com ansehen . Er hat ein effizientes System gebaut, das leicht umzugestalten und zu ändern ist, aber es braucht einige Zeit, um es zu groken.
Das hat uns in Java DoxaLogos umgebracht. Und für uns sind Laufzeitfehler ein Problem und bei den meisten unserer Wartungsarbeiten haben wir die Möglichkeit von Laufzeitfehlern, weil unser System über den 95% Lastpunkt läuft.
@Kortuk - noch ein Kommentar zu "kopfüber in C++ laden". Ich stimme zu, dass Sie idealerweise Zeit damit verbringen möchten, zuerst C++ zu lernen, bevor Sie sich kopfüber ins Zeug legen, aber manchmal funktioniert es nicht so. Bei meinem vorherigen Arbeitgeber hatten wir eine Situation, in der wir den in C++ geschriebenen Code einer anderen Abteilung für seine Funktionen ausgewählt haben, aber die gesamte Etage bestand aus einer Gruppe von EEs, die größtenteils C kannten. Ich war einer der wenigen, die eine C++-Ausbildung hatten Zeit, aber nicht viel. Das Chaos, das danach folgte, kann man sich vorstellen :-)
@Doxa, ja kann ich. Ich habe Bücher gekauft und versucht, mich so gut wie möglich zu schulen, bevor wir neue Systeme starten.
@Kortuk. Ich bin mir sicher, dass Sie das können, und ich bin froh, dass Sie den Weitblick haben, es zu tun. Deshalb habe ich angefangen, es selbst im College zu lernen, während ich EE war. Mein Punkt war, dass es manchmal nicht so funktioniert. Alle diese Ingenieure mussten nun aufgrund der kontinuierlichen „Time to Market“ ohne Schulung in einer C++-Umgebung arbeiten. Der Fehler lag natürlich auf der Managementseite und nicht bei ihnen.
@doxa, ja, der Typ vor mir war sehr geschickt und hat einen tollen Job gemacht. Sein Code ist unbrauchbar wegen der lächerlichen Zeit bis zur Bereitstellung und dem Kriechen von Funktionen, er musste Hack-Bearbeitungen vornehmen, um seinen Kopf über Wasser zu halten. Ich kann nur kämpfen und manchmal die Konsequenzen akzeptieren.
@DoxaLogos - Sie sagen, dass es eine gute Sache ist, Vererbung/virtuelle Funktionen zu vermeiden - können Sie erklären, was Sie meinen? Schließt diese Idee die Möglichkeit aus, Klassen in eine abstrakte Basisklassenschnittstelle zu schreiben?
Es hängt wirklich von Ihrem System ab und wie viel Speicher Sie zur Verfügung haben. Schreiben Sie Code auf einem 8-Bit-Mikro mit sehr wenig RAM? Dann sollten Sie es vielleicht besser vermeiden, auf abstrakten Schnittstellen verrückt zu werden. Wenn Sie so etwas wie eingebettete 32-Bit-Systeme mit viel Speicher schreiben, sollten Sie es tun. Das muss man wirklich abwägen. Jedes Mal, wenn Sie beispielsweise die Welt "virtuell" auf diese Klasse kleben, erhalten Sie einen zusätzlichen Zeiger, der je nach System 8-Bit, 16-Bit oder 32-Bit sein kann, für jede einzelne Instanz, die Sie von diesem Objekt deklarieren. Du wirst es nicht einmal merken, und Mann,
wurde ich von dem gebissen. Ich hätte wahrscheinlich nicht vermeiden sollen, sondern eher sicherstellen, dass Sie es wirklich im Design brauchen. Das Problem mit C++ ist, wie es die Menge des verwendeten Speichers "versteckt", verglichen mit der Betrachtung von reinem C-Code, und ich beziehe mich auch nicht auf die STL-Bibliothek, die bei Verwendung schnell RAM aufsaugt.
Entschuldigung ... stellen Sie sicher, dass Sie es wirklich brauchen und es sich in Ihrem Design leisten können.
Können wir dann alles zusammenfassen und sagen, dass es in C++ viel einfacher ist, Dinge zu überbauen als in C?
Basierend auf einigen Google-Treffern entschied ich mich zu sehen, wie C++ auf MSP430 funktionieren würde. „Hallo Welt“ scheitert, was peinlich ist. Zum Kompilieren sind atemberaubende 128 KB FRAM erforderlich, da die iostream-Bibliothek anscheinend zu monolithisch oder so ist. Das zeigt auf jeden Fall, dass es von vornherein kippen kann.
C++ hat einige wirklich großartige Funktionen (ich benutze es ziemlich oft - hängt vom Projekt ab), aber eine Sache, die berücksichtigt werden muss, ist die Disziplin, die erforderlich ist, um Dinge nicht zu stark zu abstrahieren; Das kann ein großes Problem für die Wartung sein.
"wie ein zweischneidiges Schwert, das dir sowohl den Arm als auch das Bein abtrennen kann" - Dies gilt auch für C.
@Kortuk, On the note of Computer Science majors going crazy in EE land ...--> wir hatten einmal einige CS Professional Consultants bei uns, die viele Jahre Erfahrung hatten und die darauf bestanden, dass Programmieren ausnahmslos einfach nur ekelhaft ist. Sie aktivierten Ausnahmen mit -fexceptionsauf den eingebetteten Controllern (was vom Hersteller dieser Controller aktiv entmutigt wurde), während sie sich beschwerten, dass EE-Leute Software einfach nicht verstanden und einfach so oder so Ausnahmen auslösten. (-‸ლ) Nach ein paar Jahre in der Entwicklungshölle starb das Projekt.

C++ ist absolut geeignet für eingebettete Systeme. Ich benutze jetzt das Vorhandensein/Fehlen guter Entwicklungswerkzeuge (oder deren Fehlen) als mein Hauptkriterium dafür, ob ich einen bestimmten Mikroprozessor verwenden soll oder nicht.

Bereiche von C++, die sich gut für eingebettete Systeme eignen, da sie geringe Ressourcenkosten haben:

  • Modularität durch gute Nutzung von Klassen/Strukturen
  • Templates , wenn der Compiler sie effizient kompiliert. Vorlagen sind ein gutes Werkzeug, um die Wiederverwendung von Algorithmen für verschiedene Datentypen zu ermöglichen.

OK-Bereiche:

  • virtuelle Funktionen -- Ich war früher dagegen, aber die Ressourcenkosten sind sehr gering (eine vtable pro Klasse , nicht pro Objekt; ein Zeiger auf die vtable pro Objekt; eine Dereferenzierungsoperation pro virtuellem Funktionsaufruf) und der große Vorteil davon ist, dass es Ihnen ermöglicht, ein Array zu haben, das mehrere verschiedene Objekttypen enthält, ohne wissen zu müssen, um welchen Typ es sich handelt. Ich habe dies kürzlich verwendet, um ein Array von Objekten zu haben, die jeweils ein I2C-Gerät darstellen, jedes mit separaten Methoden.

Nicht zu verwendende Bereiche, hauptsächlich wegen des Laufzeit-Overheads, der auf kleinen Systemen nicht akzeptabel ist:

  • dynamische Speicherzuweisung -- andere haben dies erwähnt, aber ein weiterer wichtiger Grund , die dynamische Speicherzuweisung nicht zu verwenden, ist, dass sie eine Unsicherheit im Timing darstellt; Viele Gründe für den Einsatz eingebetteter Systeme sind Echtzeitanwendungen.
  • RTTI (run time type information) – die Speicherkosten sind ziemlich hoch
  • Ausnahmen – ein definitives No-No, wegen der getroffenen Ausführungsgeschwindigkeit
Tatsächlich ist die dynamische Speicherzuweisung in Ordnung und manchmal unvermeidlich. Das Problem ist die dynamische Speicher-DEallocation (und nachfolgende Wiederverwendung). RTTI ist ein Speicherfresser, dem stimme ich zu. Aber was ist das Problem mit Ausnahmen?
@WoutervanOoijen: Das Problem mit Ausnahmen besteht darin, dass das System, wenn es innerhalb eines / -Blocks fooaufruft und einige Objekte erstellt und aufruft , was eine Ausnahme auslöst, das System irgendwie die Destruktoren für die erstellten Objekte aufrufen muss, bevor es die Kontrolle an zurückgibt . Wenn Ausnahmen nicht vollständig deaktiviert sind, kann es nicht wissen, ob möglicherweise welche ausgelöst werden, und muss daher zusätzlichen Code enthalten, um diese Möglichkeit zu ermöglichen. Ich würde gerne eine Variante von C++ mit "geprüften Ausnahmen" sehen, um damit umzugehen; Wenn Routinen, die Ausnahmen zulassen könnten, entkommen ... bartrycatchbarbozbarfoobarboz
... als solche deklariert werden müssten, dann müsste der Compiler nur Ausnahmebehandlungscode in die Aufrufer solcher Routinen einfügen. Natürlich wäre es etwas mühsam, alle erforderlichen Deklarationen hinzufügen zu müssen, während hoffentlich unnötige vermieden werden, aber es würde es ermöglichen, Ausnahmen an Stellen zu verwenden, an denen sie nützlich sind, ohne zusätzlichen Overhead, wo sie nicht erforderlich sind.
@WoutervanOoijen: Übrigens, wenn ich die ABI für eine solche Ausnahmebehandlung auf dem ARM entwerfen würde, würde ich angeben, dass Code, der eine Routine aufruft, die über eine Ausnahme beendet werden kann, R14 auf eine Adresse zwei Bytes vor der gewünschten Rücksprungadresse zeigen sollte (dies würde treten natürlich auf, wenn der Aufrufer der CALL-Anweisung ein 16-Bit-Wort folgt). Die aufgerufene Routine würde dann normal über add r15,r14,#2statt mit mov r15,r14; über eine Ausnahme beenden, ldrhs r0,[r14] / add r15,r14,r0. Null Zykluskosten für den normalen Ausgang und keine Stack-Frame-Einschränkungen.
Daher besteht das Problem mit Ausnahmen darin, dass die derzeitige Implementierung eine nicht zu vernachlässigende Belastung für den Fall ohne Ausnahme darstellt. Dies macht Ausnahmen unbeliebt, daher haben die Implementierer keine Notwendigkeit, sie effizienter zu gestalten :( Ich habe die Details der Ausnahmerückgabe nie studiert, aber intuitiv hatte ich etwas erwartet, wie Sie es beschreiben.
@WoutervanOoijen: Du hast es verstanden. Die Ausnahmebehandlung muss effektiv global aktiviert oder deaktiviert werden; Wenn es für 1 % des Codes aktiviert ist, wird der gesamte Code weniger platzsparend und normalerweise langsamer. Ob Implementierer es effizienter machen wollen, es spielen Kompatibilitätsprobleme eine Rolle. Unter anderem erfordert das Zulassen, dass Ausnahmen C- oder Assembler-Code durchlaufen, dass der throwCode die finden kann catch, entweder indem der C- oder Assembler-Code einem Strict folgen muss Stack-Frame-Protokoll, oder verwenden Sie einen statischen Speicherort, um die erforderlichen Informationen oder einen Zeiger darauf zu speichern.
@WoutervanOoijen: Da vorhandener Nicht-C++-Code möglicherweise keinem bestimmten Stacking-Protokoll entspricht und ein Task-Switcher alle diese statischen Speicherorte kennen und beim Wechseln von Tasks austauschen muss, ist es nicht möglich, die Funktionsweise der Ausnahmebehandlung zu ändern ohne dass irgendein nicht-ausnahmebezogener Code neu geschrieben werden muss.
Ich denke nicht "umgeschrieben", sondern nur "mit demselben C++-Compiler neu kompiliert".
@WoutervanOoijen: Ich meine "umgeschrieben". Code, der in C++ geschrieben wurde, wäre nicht das Problem. Wenn auf einem Multitasking-System eine C++-Routine foodie Assembler-Sprachroutine aufruft bar, die vor dem Aufruf der C++-Routine unbekannte beliebige Daten auf den Stapel legt bozund bozeine Ausnahme auslöst, muss das System irgendwie den Stapelrahmen von finden foo. Dies kann nicht geschehen, ohne entweder die Assembler-Routine barzu ändern, um ihren anfänglichen Stack-Pointer-Wert für den aufgerufenen Code verfügbar zu machen, oder den Multitasker zu ändern, um einen statischen Ausnahme-Frame-Zeiger zu speichern/wiederherstellen.
@WoutervanOoijen Wie hoch sind die Speicherkosten von RTTI?
@supercat " Zulassen, dass Ausnahmen C- oder Assembler-Code passieren " ist von Natur aus unzuverlässig , da dieser Code nicht für einen solchen Fall vorbereitet ist und Müll hinterlassen kann
Die Speicherkosten von RTTI sind einige Flash-gespeicherte Informationen über die Klassen in Ihrer Anwendung. Meiner Meinung nach sind dies keine großen Kosten, aber ich finde die Anwendungsfälle von RTTI (hauptsächlich Dowcasts) sowieso unattraktiv, also warum nicht die Kosten vermeiden.
@curiousguy: Sie haben Recht, wenn ein Pass-Through-Code bereinigt werden muss, muss er wissen, mit welchen Mitteln die Kontrolle ihm entkommen könnte. Mein Punkt ist, dass die Nichtverwendung von unwindbaren Stack-Frames ein Problem bei Implementierungen darstellen würde, die keine Thread-statische Speicherung verwenden können, selbst in Fällen, in denen der Nicht-C++-Code keine Bereinigung durchführen musste.

Ja, C++ ist durchaus für eingebettete Systeme geeignet. Lassen Sie uns zunächst ein paar Missverständnisse über den Unterschied zwischen C und C++ aufklären:

In einem eingebetteten Mikro müssen Sie Hochsprachen immer sorgfältig verwenden, wenn Sie sich Sorgen um Zeit- oder Platzbeschränkungen machen. Zum Beispiel handhaben viele MCUs Zeiger nicht gut und sind daher sehr ineffizient, wenn sie den Stack verwenden. Das bedeutet, dass Sie vorsichtig sein müssen, wenn Sie Variablen an Funktionen übergeben, Arrays und Zeiger verwenden und Rekursionen verwenden. Eine einfache Zeile von C wie:

a[i] = b[j] * c[k];

kann je nach Art dieser Variablen etwa 4 Seiten mit Anweisungen generieren.

Wann immer Sie eine Hochsprache verwenden und sich Sorgen um Zeit- und Platzbeschränkungen machen, müssen Sie wissen, wie jede Funktion dieser Sprache in Maschinenanweisungen auf Ihrer MCU übersetzt wird (zumindest jede Funktion, die Sie verwenden). Das gilt für C, C++, Ada, was auch immer. Wahrscheinlich enthalten alle Sprachen Funktionen, die auf kleinen MCUs nicht effizient übersetzt werden können. Überprüfen Sie immer die Disassemblierungslisten, um sicherzustellen, dass der Compiler nicht Unmengen von Anweisungen für etwas Triviales generiert.

Ist C für eingebettete MCUs geeignet? Ja, solange Sie den generierten Code im Auge behalten.
Ist C++ für eingebettete MCUs geeignet? Ja, solange Sie den generierten Code im Auge behalten.

Deshalb denke ich, dass C++ sogar auf 8-Bit-MCUs besser ist als C: C++ bietet verbesserte Unterstützung für:

  • Daten verstecken
  • Stärkeres Tippen / Prüfen
  • Multi-Peripherie-Transparenz mithilfe von Klassen
  • Vorlagen (wie immer bei sorgfältiger Verwendung)
  • Initialisierungslisten
  • konst

Keines dieser Merkmale ist schwerer als typische Merkmale von C.

Wenn Sie auf 16- oder 32-Bit-MCUs aufsteigen, wird es sinnvoll, schwerere Funktionen von C (Stack, Heap, Pointer, Arrays, Printf usw.) zu verwenden. Auf die gleiche Weise wird es auf einer leistungsfähigeren MCU angemessen schwerere Funktionen von C++ zu verwenden (Stack, Heap, Referenzen, STL, Neu/Löschen).

Bei dem Gedanken an C++ auf einem PIC16 braucht man also nicht zu schaudern. Wenn Sie Ihre Sprache und Ihre MCU richtig kennen, wissen Sie, wie Sie beide effektiv zusammen verwenden können.

" a[i] = b[j] * c[k];kann abhängig von der Art dieser Variablen etwa 4 Seiten mit Anweisungen generieren." Wenn Ihr MCU / Compiler dies tut, liegt dies daran, dass Sie eine Garagen-Hobby-CPU aus den 80er Jahren verwenden.
@Lundin - Seufz. Nein, es bedeutet, dass Sie eine billige kleine MCU verwenden, die so klein und billig wie möglich ist und keine komplexen Dinge wie die Stapelindizierung hat.
@Rocketmagnet Ok vielleicht in den 1990er Jahren? Heutzutage haben beschissene 8-Bitter den gleichen Preis wie 32-Bitter. Der einzige Grund für die Auswahl des ersteren ist der Stromverbrauch. Und in Bezug auf diese extra beschissenen 8-Bitter ohne Stack: Wenn Sie C anstelle von Assembler für eine so begrenzte MCU schreiben, machen Sie wahrscheinlich etwas falsch. Die 4 generierten Seiten sind dann selbst schuld, weil sie zu komplexe Programme für die CPU geschrieben haben, und im Grunde ist C das falsche Werkzeug für die Aufgabe. (Ich habe das in der Vergangenheit bei Freescale RS08 gemacht, es war eine sehr dumme Idee.)
@Lundin 32-Bit-Prozessor ist nicht schneller als 16-Bit erforderlich. Dies war bereits zu Zeiten offensichtlich, als in der PC-Welt der Programmwechsel von 16 auf 32 Bit stattfand.
@Barleyman Es gibt andere Bedenken als die Geschwindigkeit, die 8 und 16 Bit zu einer schlechten Wahl machen, insbesondere die Unfähigkeit, mehr als 65 KB Speicher bequem zu adressieren. Aber auch die verschiedenen C-Sprachprobleme, die bei so kleinen MCUs auftauchen, wie z. B. implizite Integer-Promotion-Bugs. Darüber hinaus hat fast jede 8-Bit-Architektur auf dem Markt einen absolut schrecklichen Kern , der aus den 1970er-80er Jahren stammt. Einschließlich: PIC, 8051, HC08, AVR.
@Lundin Ich habe hauptsächlich Erfahrung mit MSP430, einer 16-Bit-Architektur. Ich würde das nicht mit 8051 oder so vergleichen, es ist eine ganz andere Bestie. Und warum sollten Sie mehr als 64 kB Speicher benötigen, da diese µCU nur 64 kB hat?! Im Ernst, dachte ich, es hat eine 20-Bit-Adressierung, und das ist wirklich eine ziemlich übliche Anordnung. FRAM ist wirklich nett im Vergleich zu FLASH. Echter nicht flüchtiger RAM mit wahlfreiem Zugriff. Mein Hauptkritikpunkt ist, dass es kein DIV gibt, was bedeutet, dass Sie sich nach hinten beugen müssen, um Teilungen zu vermeiden. Wie auch immer, Texas hat hervorragende Unterstützung und Dokumentation, also ist es ein großes Plus gegenüber zB ST Micro.

Ich finde diese Debatten immer unterhaltsam zu lesen. Nicht so sehr für die intellektuelle Diskussion über die Vor- und Nachteile der verschiedenen verfügbaren Sprachen, sondern weil Sie normalerweise die Haltung einer Person zum Thema anhand ihres Jobs / ihrer Erfahrung / ihres Interessengebiets festmachen können. Es ist genau dort oben mit den Argumenten der "vorzeitigen Optimierung", wo die CS-Majors und Wartungsprogrammierer Knuth links und rechts zitieren und diejenigen, die in der realen Welt arbeiten, wo Leistung wichtig ist, denken, dass sie alle verrückt sind (ich bin ein Mitglied der letzteren Gruppe um fair zu sein).

Letztendlich kann man hier hervorragende Software in C oder C++ entwickeln oder Sprache einfügen . Es kommt auf die Fähigkeiten des Entwicklers an, nicht auf die Sprache. Ein Experte in einer Sprache zu sein, ist normalerweise nur erforderlich, wenn Sie anfangs die falsche Sprache gewählt haben und sie nun zur Lösung Ihres Problems verzerren müssen. In den meisten Fällen sind dies die einzigen Situationen, in denen Sie in obskure Funktionen oder Compiler eintauchen müssen Tricks, um das Ziel zu erreichen.

Ich höre oft, wie Leute diese Argumente mit "Ich bin ein Experte in Sprache X und bla bla" beginnen. Ich diskreditiere diese Leute ehrlich gesagt sofort, weil sie meiner Meinung nach das Problem bereits aus der falschen Perspektive angegangen sind und alles danach verdorben ist durch ihren Wunsch, ihr Werkzeug zu verwenden, um das Problem zu lösen und zu zeigen, wie "cool" es ist.

Ich beobachte so oft, wie Entwickler zuerst ein Tool-Set auswählen und dann versuchen, es an ihr Problem anzupassen, was völlig falsch ist und zu beschissenen Lösungen führt.

Wie ich in einem Kommentar zu einer anderen Antwort erwähnt habe, führen diese Sprachkriege oft zu der Argumentation, dass Sprache X dem Programmierer erlaubt, dummere Dinge zu tun. Obwohl es unterhaltsam zu lesen ist, bedeuten all diese Aussagen in Wirklichkeit, dass Sie ein Problem damit haben, gute Entwickler einzustellen, und dieses Problem direkt angehen müssen, anstatt zu versuchen, die Situation zu lindern, indem Sie weiterhin schlechte Entwickler einstellen und Tools so auswählen, dass sie so wenig tun können Schaden wie möglich.

Meiner Meinung nach erforschen gute Entwickler, sei es Software- oder Hardwareentwicklung, das Problem, entwerfen eine Lösung und finden die Werkzeuge, mit denen sie die Lösung auf die „beste Art und Weise“ ausdrücken können. Es sollte keine Rolle spielen, ob das erforderliche Tool etwas ist, das Sie noch nie zuvor verwendet haben, nachdem Sie 3-4 Sprachen/Entwicklungstools für Projekte verwendet haben, sollte die Aufnahme eines neuen eine minimale Auswirkung auf Ihre Entwicklungszeit haben.

„Best Way“ ist natürlich ein subjektiver Begriff und muss auch in der Recherchephase definiert werden. Man muss eine Vielzahl von Aspekten berücksichtigen: Leistung, einfache Ausdrucksweise, Codedichte usw., je nach vorliegendem Problem. Ich habe die Wartbarkeit aus einem bestimmten Grund nicht in diese Liste aufgenommen, es ist mir egal, welche Sprache Sie wählen, wenn Sie das richtige Tool ausgewählt und sich die Zeit genommen haben, das Problem zu verstehen, sollte dies "kostenlos" kommen. Schwierig zu wartender Code ist oft das Ergebnis der Wahl des falschen Tools oder einer schlechten Systemstruktur, was zu einem hässlichen Durcheinander führt, damit es funktioniert.

Zu behaupten, eine Sprache sei „besser“ als jede andere, ist albern, ohne ein bestimmtes Problem von Interesse zu definieren. Ein objektorientierter Ansatz ist nicht immer besser als ein funktionaler Ansatz. Es gibt einige Probleme, die sich sehr gut für ein objektorientiertes Entwurfsparadigma eignen. Es gibt viele, die das nicht tun. Die gleiche Aussage kann über viele Sprachmerkmale gemacht werden, auf denen die Leute gerne herumreiten.

Wenn Sie mehr als 20 % Ihrer Zeit für ein Problem damit verbringen, Code einzutippen, produzieren Sie wahrscheinlich ein sehr schlechtes System oder haben sehr schlechte Entwickler (oder Sie lernen noch). Sie sollten den größten Teil Ihrer Zeit damit verbringen, das Problem grafisch darzustellen und zu bestimmen, wie verschiedene Teile der Anwendung interagieren. Wenn Sie eine Gruppe talentierter Entwickler in einen Raum mit einer Markierungstafel und einem zu lösenden Problem stecken und ihnen sagen, dass sie keinen Code schreiben oder keine Tools auswählen dürfen, bis sie sich mit dem gesamten System wohl fühlen, wird dies mehr zur Verbesserung der Qualität beitragen Leistung und Entwicklungsgeschwindigkeit, als die Wahl eines heißen neuen Tools, das garantiert die Entwicklungszeit verkürzt. (Schauen Sie sich die Scrum-Entwicklung als Referenz für das genaue Gegenteil meiner Argumentation an)

Die unglückliche Realität ist oft, dass viele Unternehmen den Wert eines Entwicklers nur an der Anzahl der geschriebenen Zeilen oder am „greifbaren Ergebnis“ messen können. Sie betrachten die 3 Wochen in einem Raum mit einer Markierungstafel als Produktivitätsverlust. Entwickler werden oft gezwungen, durch die „Gedanken“-Phase der Entwicklung zu eilen oder werden durch ein politisches Problem innerhalb des Unternehmens gezwungen, ein Toolset zu verwenden, „Der Bruder meines Chefs arbeitet für IBM, also können wir nur ihre Tools verwenden“, diese Art von Müll . Oder noch schlimmer, Sie erhalten ständig wechselnde Anforderungen vom Unternehmen, weil sie nicht in der Lage sind, angemessene Marktforschung zu betreiben oder die Auswirkungen von Änderungen auf den Entwicklungszyklus nicht verstehen.

Tut mir leid, dass ich mit diesem Geschwätz etwas vom Thema abgekommen bin, ich habe ziemlich starke Meinungen zu diesem Thema.

+1, ich habe es genossen, wenn auch etwas lang. Ich benutze C nur bei der Arbeit, aber ich versuche wirklich, zu C++ zu wechseln, weil es viele Boni gibt. Ich habe meine Vorurteile, hauptsächlich aufgrund dessen, was schief gelaufen ist, oder der Tatsache, dass ich einen Chef habe, der die Spezifikationen innerhalb von 2 Tagen ändert, nachdem er uns mitgeteilt hat, was benötigt wird. Ich lerne immer noch, und ich gehe davon aus, dass ich mich noch etwa 4-5 Jahre so fühlen werde. Ich werde danach noch lernen, aber mein Unternehmen hat keinen Stilleitfaden, der befolgt wird, oder einen Inspektionsprozess, und ich versuche, beides zu bekommen.
Ich denke, Sie haben mit Ihrem Kommentar, das Werkzeug um das Problem herum zu biegen, den Nagel auf den Kopf getroffen. Es erinnert mich an das Sprichwort: "Wenn alles, was du hast, ein Hammer ist, sieht alles aus wie ein Nagel!" War das ein Wortspiel?;-) TDD steht definitiv im Widerspruch zum Vorausdenken, und ich glaube nicht, dass ich mich jemals mit der Idee anfreunden werde, Tests zur Erstellung des Designs durchzuführen ... besonders bei eingebetteten Systemen. Wie um alles in der Welt können Sie ein Hardwareteil nachahmen, um einen Gerätetreiber zu testen, ohne viel Zeit mit dem Scheinstück zu verschwenden?!
Nun, ich klopfe keine Komponententests auf Anwendungsebene (über dem Treiber) auf bestimmten eingebetteten Systemen ab. Es gibt einen gewissen Wert des sofortigen Feedbacks von Komponententests und der Beseitigung von Fehlern in der frühen Entwicklungsphase, aber das gesamte TDD-Paradigma, um das Design hervorzubringen, scheint mir ein wenig hirntot zu sein. Ich bevorzuge es, mir etwas Zeit zu nehmen, um über das Problem nachzudenken und es entweder in meinem Kopf, auf Papier oder auf einem Whiteboard zu skizzieren, bevor ich mit dem Programmieren beginne. Ich denke auch, dass TDD den Markt dazu ermutigt, keine Vorabrecherchen zu Anforderungen durchzuführen, da es bei ständigen Anforderungsänderungen helfen soll.
Und um eine letzte Anmerkung zu meinem superlangen Kommentar zu machen. Wir brauchen keine Sprachexperten, um am Design zu arbeiten. Wir brauchen erfahrene Designer, die mit der/den Sprache(n) umgehen können.
Entschuldigung für die episch lange Tirade, ich neige dazu, ziemlich hektisch zu werden, wenn ich sehe, dass ein Thema heftig diskutiert wird, wenn ich es als einfaches Ergebnis eines tieferen Problems sehe, von dem ich denke, dass es das eigentliche Diskussionsthema sein sollte.
@doxalogos Der TDD-Punkt ist gut. Das einzige Problem, das ich in meiner zugegebenermaßen begrenzten Erfahrung mit Unternehmen hatte, die diese Probleme erzwingen, ist, dass sie eine TDD verlangen, aber nicht bereit sind, eine feste PRD zu erstellen.
@doxalogos äh, tut mir leid, ich bin gegangen und habe TDD mit TRD verwechselt, zu viele TLA (Akronyme mit drei Buchstaben)
@mark, kannst du PRD und TRD definieren.
PRD = Product Requirements Document, MRD = Marketing Requirements Document, TRD = Technical Requirements Document. TDD = Testgetriebene Entwicklung.
@doxalogos, Die TDD-Idee (Test Driven Development) ist interessant, aber ich denke, sie ist von Natur aus fehlerhaft. Ich stimme zu 100% mit allem überein, was Sie dazu sagen. In Bezug auf Unit-Tests habe ich Geschichten von Unternehmen gelesen, dass sie bei der Implementierung eines gut entwickelten Software-Inspektionsprozesses Unit-Tests fallen lassen und Zeit für die Veröffentlichung sparen konnten.
@DoxaLogos, Danke für die Definitionen, mein Unternehmen ist klein, ich kannte TDD, aber das war es.
@Mark - Ich stimme Ihrer Meinung zum Design im Voraus zu, aber nur bis zu einem gewissen Punkt. Ich denke, dass sich umfangreiche Design-Up-Front-Arbeiten auszahlen, wenn a) Ihre Anforderungen ziemlich stabil/bekannt sind und b) die Entwickler, die das Design durchführen, erfahren sind . In einem vor. Job, ich wurde beauftragt, ein Design zu erstellen, und es wurde von meinem Teamleiter stark zeitlich festgelegt, und ich dachte: "Was für eine dumme Sache! Design im Voraus spart Geld (vgl. Code Complete-Buch)?? Aber beim Programmieren entdeckte ich Tonnen von Dingen, nach denen ich nicht suchen musste. Wenn ich viel Design gemacht und die Codezeit minimiert hätte, wäre es eine Verschwendung gewesen. JME.
@sheepsimulator Ich stimme dem zweiten Punkt offensichtlich zu, ich gehe davon aus, dass die leitenden Systemarchitekten erfahrene Entwickler sind. Beim ersten Punkt bin ich eigentlich anderer Meinung. Ich denke, je mehr Sie erwarten, dass sich die Anforderungen ändern, desto mehr Zeit sollten Sie in die Designphase investieren, da Sie ein gutes, leicht zu änderndes Design erstellen müssen. Ich weiß, dass einige Philosophien eine schnelle Entwicklung vorschlagen. In einigen Fällen funktioniert dies gut wie viele schlechte oder unerfahrene Programmierer im Personal. All diese Designphilosophien laufen darauf hinaus, zu sagen: "Ich habe kein Verlangen danach, ein flexibles System zu entwerfen, also lasst uns keine Zeit damit verschwenden, es zu versuchen".
Unsere Anforderungen an uns ändern sich hier ständig, aber die Systeme, für deren Entwicklung wir die meiste Zeit aufwenden, haben die schnellste Codierungs- und Wartungszeit. Wir haben sehr viel Zeit auf ein Subsystem verwendet, weil wir wissen, wie wichtig es in Zukunft sein würde und dass es geändert werden müsste. Es ist das einzige Subsystem, an dem Änderungen weniger als einen Tag Arbeit gekostet haben, und es hat viele gegeben.

Meiner Erfahrung nach ist C++ für kleine eingebettete Systeme normalerweise schlecht geeignet. Damit meine ich Mikrocontroller und Geräte ohne Betriebssystem.

Viele C++-OOP-Techniken beruhen auf dynamischer Speicherzuordnung. Das fehlt oft in kleinen Anlagen.

STL und Boost demonstrieren wirklich die Leistungsfähigkeit von C++, beide haben einen enormen Platzbedarf.

C++ ermutigt den Programmierer, die Maschine zu abstrahieren, wo sie in eingeschränkten Systemen umarmt werden muss.

Letztes Jahr habe ich ein kommerzielles Remote-Desktop-Produkt auf Mobiltelefone portiert. Es wurde in C++ geschrieben und lief unter Windows, Linux und OSX. Aber es stützte sich stark auf STL, dynamischen Speicher und C++-Ausnahmen. Um es auf WinCE, Symbian und OS-losen Umgebungen zum Laufen zu bringen, war ein C-Rewrite die vernünftigste Option.

Ich stimme in Bezug auf kleine Systeme zu, aber ich denke, wir haben unterschiedliche Definitionen von kleinen Systemen. Wenn Sie 1 KB ROM haben und gut geschriebener C-Code alles außer 1 Byte ROM benötigt, ist das ein kleines System.
Ich behaupte nicht, dass C nicht kleiner sein kann, aber Sie hätten trotzdem C++ verwenden und ein sehr ähnliches Ergebnis für das Design des gerade besprochenen erzielen können. Ich denke, das Problem ist, dass die meisten OOP-Programmierer an Systeme mit dynamischem Speicher gewöhnt sind und sehr ineffiziente Konstrukte verwenden, was zu völlig nutzlosem Code für Systeme mit geringerem Stromverbrauch führt.
Ich habe hauptsächlich größere x86-SBC-Entwicklungen in C++ durchgeführt, daher finde ich es hilfreich, Erfahrungen (positiv oder negativ) bei der Verwendung von C++ auf Plattformen mit weniger Ressourcen zu hören.
Ich habe sehr viel auf Computern mit C++ entwickelt, ich habe fast keine auf eingebetteten Plattformen gemacht. Ich habe sehr, sehr viel auf eingebetteten Plattformen entwickelt, und ich meine im Bereich von MSP430s und PICs. Das wurde alles in C gemacht. Keine Bibliotheken oder so. Ich möchte C++ in einem Embedded-Systems-Projekt verwenden, es gibt viele gute Gründe, bisher habe ich keine Gründe gehört, die nicht gelöst werden können, indem strenge Regeln dafür verwendet werden, welche Konstrukte von C++ verwendet werden können.
Was Sie also sagen, ist, dass Sie C++ nicht verwenden möchten, sondern etwas zwischen C und C++ verwenden möchten (nennen wir es einfach C+?). In diesem Fall stimme ich zu, es gibt eine Menge Mist in C++, die Leute benutzen, nur weil es verfügbar ist, nicht weil es optimal ist. Fast jede Sprache ist in der Lage, guten, schnellen Code zu produzieren, es kommt darauf an, wie sie verwendet wird. Die meisten heiligen Kriege um Sprachen sind kein Ergebnis der Sprachfähigkeiten, sondern ein Streit darüber, wie einfach es für einen Idioten ist, idiotische Dinge zu tun, was wirklich ein idiotischer Streit ist: p
Ich versuche darüber zu argumentieren, wie mächtig eine Sprache ist, um Dinge zu erledigen. Ich denke, C++ bietet große Leistungssprünge, um Dinge zu erledigen. Mit dem richtigen Design und der richtigen Implementierung sehe ich große Verbesserungen in der Art und Weise, wie wir Systeme entwickeln, was ich möchte, und wenn es anderen helfen kann, aber nur ein wenig mehr Training erfordert, dann bin ich interessiert.
Ich benutze den dynamischen Speicher in C aber auch nicht wirklich. Nirgendwo muss ich es haben. Langfristig habe ich gelesen, dass es sehr sehr segmentiert werden und Probleme verursachen kann. Ich brauche sehr klar gestaltete Fälle, in denen der Speicher knapp wird, und ich muss in der Lage sein, genau zu überwachen, wie viel noch übrig ist.

Jede Sprache kann für ein eingebettetes System geeignet sein. Eingebettet bedeutet nur: Teil eines größeren Apparats, im Gegensatz zu einem frei nutzbaren Computer.

Die Frage ist relevanter, wenn sie nach einem (harten) Echtzeit- oder einem System mit begrenzten Ressourcen gestellt wird.

Für ein Echtzeitsystem ist C++ eine der höchsten Programmiersprachen, die noch für die Programmierung unter strengen Zeitvorgaben geeignet ist. Mit Ausnahme der Heap-Nutzung (freier Operator) gibt es keine Konstrukte, die eine unbestimmte Ausführungszeit haben, sodass Sie testen können, ob Ihr Programm seine Timing-Anforderungen erfüllt, und mit etwas mehr Erfahrung können Sie es vielleicht sogar vorhersagen. Die Heap-Nutzung sollte natürlich vermieden werden, obwohl der neue Operator immer noch für die einmalige Zuweisung verwendet werden kann. Die Konstrukte, die C++ über C bietet, können in einem eingebetteten System sinnvoll eingesetzt werden: OO, Exceptions, Templates.

Für sehr ressourcenbeschränkte Systeme (8-Bit-Chips, weniger als ein paar Kb RAM, kein zugänglicher Stack) könnte vollständiges C++ ungeeignet sein, obwohl es immer noch als 'besseres C' verwendet werden könnte.

Ich finde es schade, dass Ada nur in einigen Nischen eingesetzt zu werden scheint. In vielerlei Hinsicht ist es ein Pascal++, aber ohne die Bürde, mit einer Sprache aufwärtskompatibel zu sein, die schon von Anfang an ein großes Durcheinander war. (Bearbeiten: Das ernsthafte Durcheinander ist natürlich C. Pascal ist eine schöne, aber etwas unpraktische Sprache.)

=============================================== ==============

BEARBEITEN: Ich habe eine Antwort auf eine neue Frage eingegeben ("In welchen Fällen ist C ++ erforderlich, wenn wir Mikrocontroller programmieren"? ), die sich auf diese bezieht, also füge ich hinzu, was ich geschrieben habe:

Es gibt nie einen allumfassenden Grund für die Verwendung einer Programmiersprache, aber es kann Argumente geben, die in einer bestimmten Situation mehr oder weniger Gewicht haben. Diskussionen darüber finden sich an vielen Stellen, wobei Positionen vertreten werden, die von „niemals C++ für einen Mikrocontroller verwenden“ bis „immer C++ verwenden“ reichen. Ich bin eher mit der letzten Position. Ich kann einige Argumente anführen, aber Sie müssen selbst entscheiden, wie viel Gewicht sie in einer bestimmten Situation haben (und in welche Richtung).

  • C++-Compiler sind seltener als C-Compiler; für einige Ziele (z. B. 12- und 14-Bit-Core-PICs) gibt es überhaupt keine C++-Compiler.
  • (Gute) C++-Programmierer sind seltener als (gute) C-Programmierer, insbesondere unter denen, die sich auch (etwas) mit Elektronik auskennen.
  • C++ hat mehr Konstrukte als C, die für kleine Systeme nicht geeignet sind (wie Ausnahmen, RTTI, häufige Verwendung des Heaps).
  • C++ hat einen reichhaltigeren Satz an (Standard-)Bibliotheken als C, aber eine Konsequenz aus dem vorherigen Punkt ist, dass C++-Bibliotheken häufig Funktionen verwenden, die für kleine Systeme ungeeignet sind und daher auf kleinen Systemen nicht verwendbar sind.
  • C++ hat mehr Konstrukte als C, mit denen Sie sich selbst ins Knie schießen können.
  • C++ hat mehr Konstrukte als C, mit denen Sie verhindern können, dass Sie sich selbst in den Fuß schießen (ja, meiner Meinung nach sind dies und das vorherige beide wahr).
  • C++ verfügt über einen reichhaltigeren Satz an Abstraktionsmechanismen und ermöglicht daher bessere Programmiermethoden, insbesondere für Bibliotheken.
  • C++-Sprachmerkmale (z. B. Konstruktoren/Destruktoren, Konvertierungsfunktionen) erschweren das Durchschauen des Codes, um die generierte Maschine und damit die Raum- und Zeitkosten eines Sprachkonstrukts zu erkennen.
  • Das C++-Sprachkonstrukt macht es weniger notwendig, sich darüber im Klaren zu sein, wie genau sie in Maschinencode übersetzt werden, da sie auf abstraktere Weise „das Richtige“ tun.
  • Der Sprachstandard C++ entwickelt sich schnell und wird schnell von den großen Compilern (gcc, clang, microsoft) übernommen. C entwickelt sich ziemlich schleichend, und die Übernahme einiger neuerer Funktionen (Varianten-Arrays) ist beängstigend und wurde in einem späteren Standard sogar rückgängig gemacht. Gerade dieser Punkt ist insofern interessant, als dass verschiedene Personen ihn nutzen, um die gegensätzlichen Positionen zu unterstützen.
  • C++ ist zweifellos ein schärferes Werkzeug als C. Vertrauen Sie Ihren Programmierern (oder sich selbst), ein solches Werkzeug zu verwenden, um eine schöne Skulptur zu erstellen, oder befürchten Sie, dass sie sich verletzen, und würden Sie sich lieber mit einem weniger schönen, aber risikoärmeren Produkt zufrieden geben ? (Ich erinnere mich, dass mir mein Bildhauerlehrer einmal gesagt hat, dass stumpfe Werkzeuge in manchen Situationen gefährlicher sein können als scharfe.)

Mein Blog enthält einige Schriften zur Verwendung von C++ auf kleinen Systemen (= Mikrocontrollern).

Ich hoffe, dieser Diskussion über C++ auf Bare-Metal- und ressourcenbeschränkten Systemen mehr Licht als Wärme hinzufügen zu können.

Probleme in C++:

  • Ausnahmen sind insbesondere ein RAM-Problem, da der erforderliche "Notfallpuffer" (wo beispielsweise die Ausnahme wegen fehlendem Speicher gilt) größer sein kann als der verfügbare RAM und für Mikrocontroller sicherlich eine Verschwendung ist. Weitere Informationen finden Sie unter n4049 und n4234 . Sie sollten ausgeschaltet werden (was derzeit ein nicht spezifiziertes Verhalten ist, also seien Sie sicher und werfen Sie es niemals). SG14 arbeitet derzeit an besseren Möglichkeiten, dies zu tun.

  • RTTI ist wahrscheinlich nie den Mehraufwand wert, es sollte abgeschaltet werden

  • Große Debug-Builds, obwohl dies in der klassischen Desktop-Entwicklung kein Problem darstellt, wenn das Debug nicht auf den Chip passt, kann es ein Problem sein. Das Problem ergibt sich aus Codevorlagen oder zusätzlichen Funktionsaufrufen, die der Übersichtlichkeit halber hinzugefügt wurden. Diese zusätzlichen Funktionsaufrufe werden vom Optimierer wieder entfernt und die zusätzliche Klarheit oder Flexibilität kann ein großer Vorteil sein, jedoch kann dies in Debug-Builds ein Problem sein.

  • Heap-Zuweisung. Obwohl die STL die Verwendung von benutzerdefinierten Zuweisungen zulässt, kann dies für die meisten Programmierer komplex sein. Die Heap-Zuweisung ist nicht deterministisch (dh keine harte Echtzeit) und die Fragmentierung kann zu unerwarteten Speichermangelsituationen führen, die auftreten, obwohl beim Testen gearbeitet wurde. Die Buchhaltung, die der Haufen benötigt, um den freien Platz und die unterschiedliche Größe zu verfolgen, kann bei kleinen Objekten ein Problem sein. Es ist normalerweise besser, die Pool-Zuweisung zu verwenden (sowohl in C als auch in C++), aber dies kann für C++-Programmierer ungewöhnlich sein, die daran gewöhnt sind, nur den Heap zu verwenden.

  • Laufzeitpolymorphismus und andere indirekte Aufrufe sind normalerweise ein großer Leistungseinbruch, das Problem liegt normalerweise eher darin, dass der Optimierer nicht mehr durch sie hindurchsehen kann, als das eigentliche Abrufen und Springen zu der Adresse. Indirekte Aufrufe sind aus diesem Grund in C und C++ zu vermeiden, wo sie wie in C++ stärker in der Kultur verwurzelt sind (und in anderen Domänen sehr nützlich sind).

  • Die implizite Anbindung an clib kann problematisch sein. Es mag widersprüchlich sein, dass clib-Probleme in die C++-Kategorie fallen, aber das Problem ergibt sich aus der impliziten gemeinsamen Nutzung von Ressourcen in gleichzeitigen Umgebungen (die gemeinsame Nutzung ist in C expliziter). Die Verwendung der gemeinsamen newLib-Implementierung zieht oft eine Menge Bloat mit sich, was normalerweise in uCs nicht benötigt wird, andererseits ist newLibNanno nicht reentrant, sodass der Zugriff darauf serialisiert werden muss (hier zu stark vereinfacht). Dies ist auch ein Problem für C, aber der Zugriff ist expliziter. Als Faustregel sollte man im ISR-Kontext im Wesentlichen nichts aus dem Namensraum std verwenden, es sei denn, Sie sind sicher, dass es nicht irgendwie auf den Status in clib zugreift (zum Beispiel errorno oder der Heap). Es ist auch wichtig, wenn Sie Threads verwenden (ich bevorzuge RTC), um new und delete zu überschreiben, um den Zugriff auf malloc und free zu synchronisieren.

Zusammenfassend lässt sich sagen, dass C++ einige Probleme hat, die aber im Wesentlichen alle behebbar oder vermeidbar sind.

Jetzt für C, hier ist das Problem höherer Ordnung. Ich habe in C nicht die syntaktische Fähigkeit, Dinge so zu abstrahieren, dass ich Optimierungen durchführen oder Invarianten zur Kompilierzeit überprüfen kann. Daher kann ich Dinge nicht richtig so kapseln, dass der Benutzer nicht wissen muss, wie sie funktionieren, um sie zu verwenden, und der Großteil meiner Fehlererkennung erfolgt zur Laufzeit (was nicht nur zu spät ist, sondern auch Kosten verursacht). Im Wesentlichen ist die einzige Möglichkeit, in C generisch zu sein, über Daten. Ich übergebe einen Formatstring an printf oder scanf, der zum Beispiel zur Laufzeit ausgewertet wird. Es ist dann ziemlich schwierig für den Compiler zu beweisen, dass ich einige der Optionen nicht verwende, die theoretisch möglich sind, wenn die richtigen Daten übergeben werden, was eine potenzielle Generierung von totem Code und den Verlust von Optimierungspotenzial bedeutet.

Ich weiß, dass ich hier vielleicht einen Shitstorm entfessele, aber meine Erfahrung mit 32-Bit-Mikrocontrollern ist, dass in einem Äpfel-zu-Äpfel-Vergleich von C und C++, die beide von Experten geschrieben wurden (wie in C++ möglicherweise stark vorlagenbasiert), C++ die viel effizientere Sprache ist alles muss überhaupt generisch sein (wie in jeder Bibliothek) und sie sind in nicht generischen Fällen im Wesentlichen äquivalent. Es ist auch für einen Anfänger einfacher, das Fachwissen eines erfahrenen Bibliotheksimplementierers in C++ zu nutzen.

Gleichzeitig gibt es eigentlich nur wenige Funktionen, an die ich keine falschen Daten übergeben kann, sobald die Eingabe kein int ist, sondern eine, somethingfür die ich zufällig ein int als Darstellungsmethode verwende, besteht die Möglichkeit, es zu bekommen falsch (übergeben Sie einen ungültigen Wert oder ein 'otherThing' anstelle von 'something'). In C ist meine einzige Methode, um zu überprüfen, ob der Benutzer etwas falsch gemacht hat, zur Laufzeit. In C++ habe ich die Möglichkeit, einige Prüfungen durchzuführen, nicht alle Prüfungen, aber einige Prüfungen zur Kompilierzeit, die kostenlos sind.

Am Ende des Tages ist ein C-Team oft so mächtig wie sein schwächster Programmierer und der daraus resultierende Code hat entweder einen Multiplayer von 1 oder eine Leistungsstrafe. Was ich damit meine, ist, dass es entweder eine hohe Leistung für einen und nur einen einzigartigen Job in einer einzigartigen Umgebung mit einzigartigen Designentscheidungen ist oder dass es generisch genug ist, um in mehreren Umgebungen verwendet zu werden (anderer Mikrocontroller, andere Speicherverwaltungsstrategie, andere Latenz vs. Durchsatzkompromisse usw. usw.), hat aber inhärente Leistungskosten.

In C++ können Dinge von Experten gekapselt und in vielen Umgebungen verwendet werden, in denen sich die Codegenerierung zur Kompilierzeit an die jeweilige Aufgabe anpasst und die statische Überprüfung die Benutzer davon abhält, dummes Zeug zum Nulltarif zu machen. Hier müssen wir weitaus weniger Kompromisse zwischen generisch und schnell machen und sind daher letztendlich aus Kosten-Nutzen-Sicht die leistungsfähigere, sicherere und produktivere Sprache.

Es ist eine berechtigte Kritik, dass es immer noch einen großen Mangel an guten C++-Bibliotheken für Embedded gibt, was zu pragmatischen Entscheidungen führen kann, hauptsächlich C auf einem C++-Compiler zu verwenden. Entscheidungen, in einem Projekt nur C zu verwenden, sind im Wesentlichen entweder ideologisch motiviert, aus der Notwendigkeit von Legacy-Unterstützung oder aus dem Eingeständnis, dass das Team nicht diszipliniert genug ist, um von einer sehr ausgewählten Reihe von dummen Dingen Abstand zu nehmen, die man in C++, aber nicht in C machen kann und gleichzeitig diszipliniert genug, um keine der weitaus größeren Dummheiten zu machen, vor denen man sich in C nicht schützen kann, aber in C++ könnte.

Schöne Ergänzung zu meiner Antwort :) Wer wäre dieser mysteriöse C++-Liebhaber? In seinem Profil heißt es: „Anscheinend zieht es dieser Benutzer vor, einen Hauch von Mysterium über sich zu bewahren.“ (schlechtes Englisch, übrigens) AHA, der Ort ist "Bochum, Deutschland"..... Wir sehen uns auf der Konferenz!
Ah ja, mein Profil wurde aktualisiert ;) Schön zu wissen, dass Sie zu emBO++ kommen, es wird eine gute Menge sein

Mein Hintergrund: gerade aus der Schulausbildung bei alten Bell Labs-Programmierern; Arbeitet seit 3 ​​Jahren, 2 an Bachelor-Forschungsprojekt; Datenerfassung / Prozesssteuerung in VB.NET. Verbrachte 1,5 Jahre mit der Arbeit an einer Unternehmensdatenbankanwendung in VB6. Arbeitet derzeit an einem Projekt für einen eingebetteten PC mit 2 GB Speicher, 512 MB RAM, 500 MHz x86-CPU; Mehrere Apps, die gleichzeitig ausgeführt werden und in C++ geschrieben sind, mit einem dazwischen liegenden IPC-Mechanismus. Ja, ich bin jung.

Meine Meinung: Ich denke, dass C++ angesichts der Umgebung, die ich oben geschrieben habe, effektiv funktionieren kann . Zugegeben, harte Echtzeitleistung ist keine Voraussetzung für die App, auf der ich mich befinde, und in einigen eingebetteten Anwendungen kann das ein Problem sein. Aber hier sind die Dinge, die ich gelernt habe:

  • C++ unterscheidet sich grundlegend von C (dh es gibt kein C/C++). Während alles, was gültiges C ist, gültiges C++ ist, ist C++ eine ganz andere Sprache und man muss lernen, wie man in C++ programmiert, nicht in C, um es in jeder Situation effektiv einzusetzen. In C++ müssen Sie objektorientiert programmieren, nicht prozedural und keine Mischung aus beidem (große Klassen mit vielen Funktionen). Im Allgemeinen sollten Sie sich darauf konzentrieren, kleine Klassen mit wenigen Funktionen zu erstellen, und alle kleinen Klassen zu einer größeren Lösung zusammensetzen. Einer meiner Kollegen erklärte mir, dass ich früher prozedural in Objekten programmiert habe, was ein großes Durcheinander ist und schwer zu warten ist. Als ich anfing, mehr objektorientierte Techniken anzuwenden, stellte ich fest, dass die Wartbarkeit/Lesbarkeit meines Codes zunahm.

  • C++ bietet zusätzliche Funktionen in Form von objektorientierter Entwicklung, die eine Möglichkeit bieten können, Code zu vereinfachen, um ihn leichter lesbar/verwaltbar zu machen . Ehrlich gesagt glaube ich nicht, dass einer Verbesserung der Leistung/Platzeffizienz bei OOP viel im Wege steht. Aber ich denke, OOP ist eine Technik, die helfen kann, ein komplexes Problem in viele kleine Teile aufzuteilen. Und das ist hilfreich für die Leute, die am Code arbeiten, ein Element dieses Prozesses, das nicht ignoriert werden sollte.

  • Viele Argumente gegen C++ haben in erster Linie mit dynamischer Speicherallokation zu tun. C hat das gleiche Problem auch. Sie können eine objektorientierte Anwendung schreiben, ohne dynamischen Speicher zu verwenden, obwohl einer der Vorteile der Verwendung von Objekten darin besteht, dass Sie diese Dinge auf einfache Weise dynamisch zuweisen können. Genau wie in C müssen Sie darauf achten, wie Sie die Daten verwalten, um Speicherlecks zu reduzieren, aber die RAII-Technik macht dies in C++ einfacher (lassen Sie den dynamischen Speicher automatisch zerstören, indem Sie ihn in Objekte kapseln). In einigen Anwendungen, wo jeder Speicherplatz zählt, kann dies zu wild und wollig sein, um es zu verwalten.

BEARBEITEN:

  • WRT die "Arduino C++"-Frage : Ich würde argumentieren, dass C++ ohne dynamische Speicherverwaltung immer noch nützlich sein kann. Sie können Ihren Code in Objekte organisieren und diese Objekte dann an verschiedenen Stellen in Ihrer Anwendung platzieren, Callback-Schnittstellen einrichten usw. Jetzt, da ich in C++ entwickelt habe, sehe ich viele Möglichkeiten, wie eine Anwendung mit allen Daten, die auf der Stack kann bei Objekten immer noch nützlich sein. Ich gebe jedoch zu - ich habe nie eine solche eingebettete App für den Arduino geschrieben, daher habe ich keinen Beweis für meine Behauptung. Ich habe einige Möglichkeiten, in einem bevorstehenden Projekt Arduino-Entwicklungen durchzuführen - hoffentlich kann ich meinen Anspruch dort testen.
Ich möchte Ihren zweiten Punkt kommentieren, Sie sagen, dass es hilft, ein komplexes Problem in viele kleine Teile zu zerlegen, und dieses Merkmal sollte ignoriert werden. Das ist der genaue Grund, warum ich so pro-C++ bin. Eine sehr große Anzahl von Studien zur Programmierung zeigt, dass ein lineares Wachstum der Programmgröße zu einem exponentiellen Wachstum der Entwicklungszeit führt. dies folgt dem umgekehrten Weg, wenn Sie ein Programm richtig aufteilen können, können Sie einen exponentiellen Abfall der Entwicklungszeit erzielen. Das ist mit Abstand das Wichtigste.
auch zu Ihrem zweiten Punkt: Die einfache Verwendung einer OOP-Designmethodik führt nicht zu mehr unterteiltem Code. Ein gutes Basisdesign zu haben, bleibt dem Entwickler überlassen, wie man dieses Design ausdrückt. OOP definiert nicht, dass Sie Ihren Code richtig trennen, es bietet eine andere Option und darüber hinaus den Anschein, dass Sie dies getan haben, aber es erzwingt sicherlich kein gutes Design, das ist Sache des Entwicklers.
Das ist immer wahr. Ich habe noch nie von einer Sprache gehört, die gutes Design erzwingt. Ich denke, wir implizieren hauptsächlich, dass dies die Aufgabe des Entwicklers ist und dass C++ es einfach macht, es auf organisierte Weise zu verwenden und zu implementieren.
@ Mark - ich stimme zu. Es war ein Lernprozess für mich.
„In C++ muss man objektorientiert programmieren, nicht prozedural“ – warum? Sie können C-ähnliches prozedurales C++ ausführen und einfach aus den C++-Features auswählen, die Ihnen gefallen. zum Beispiel Const, reference, enum calss, namespace, ... Sie würden die vollen Vorteile von C++ verpassen, aber Sie wären immer noch besser dran als mit einfachem C.

Ja, das Problem bei C++ ist der größere Platzbedarf des Codes.

In einigen Systemen zählen Sie Bytes, und in diesem Fall müssen Sie die Kosten für den Betrieb akzeptieren, die nahe an den Grenzen Ihres Systems liegen, nämlich erhöhte Entwicklungskosten von C.

Aber selbst in C müssen Sie für ein gut entworfenes System alles gekapselt halten. Gut entworfene Systeme sind schwierig, und C++ gibt Programmierern einen Platz für eine sehr strukturierte und kontrollierte Entwicklungsmethode. Das Erlernen von OOP ist mit Kosten verbunden, und wenn Sie darauf umsteigen möchten, akzeptieren Sie es sehr, und in vielen Fällen möchte das Management lieber mit C fortfahren und die Kosten nicht bezahlen, da es schwierig ist, die Ergebnisse eines solchen Umstiegs zu messen erhöht die Produktivität. Einen Artikel von Jack Ganssle, Guru für eingebettete Systeme, finden Sie hier .

Dynamisches Speichermanagement ist der Teufel. Nicht wirklich, der Teufel ist die automatische Weiterleitung, die dynamische Speicherverwaltung funktioniert auf einem PC hervorragend, aber Sie können davon ausgehen, dass Sie einen PC mindestens alle paar Wochen neu starten müssen. Sie werden feststellen, dass, wenn ein eingebettetes System 5 Jahre lang läuft, die dynamische Speicherverwaltung wirklich durcheinander geraten und tatsächlich versagen kann. Ganssle geht in seinem Artikel auf Dinge wie Stack und Heap ein.

Es gibt einige Dinge in C++, die anfälliger für Probleme sind und viele Ressourcen verbrauchen. Das Entfernen der dynamischen Speicherverwaltung und von Vorlagen sind große Schritte, um den Fußabdruck von C++ näher an dem Fußabdruck von C zu halten. Dies ist immer noch C++, Sie brauchen kein dynamisch Speicherverwaltung oder Vorlagen, um gutes C++ zu schreiben. Ich habe nicht bemerkt, dass sie Ausnahmen entfernt haben, ich betrachte Ausnahmen als einen wichtigen Teil meines Codes, den ich in der Version entferne, aber bis zu diesem Punkt verwende. In Feldtests kann ich Ausnahmen veranlassen, Nachrichten zu generieren, die mich darüber informieren, dass eine Ausnahme abgefangen wurde.

Ich habe früher zugestimmt, dass der Code-Footprint ein Problem ist, aber in letzter Zeit scheint es, dass die Flash-Größe nur sehr wenig Einfluss auf den Preis eines Mikrocontrollers hat, viel viel weniger als die RAM-Größe oder die Anzahl der IO-Pins.
Das Argument zum dynamischen Speicher ist meiner Meinung nach wichtiger. Ich habe industrielle Systeme gesehen, die wochenlang ununterbrochen laufen konnten, aber die Diagnoseschicht (in C++ geschrieben) würde die Zeit für den Neustart auf etwa 12 Stunden begrenzen.

Ich fand diesen Anti-C++-Rant von Linus Torvalds interessant.

Eines der absolut schlimmsten Features von C++ ist, dass es viele Dinge so kontextabhängig macht - was einfach bedeutet, dass eine lokale Ansicht beim Betrachten des Codes einfach selten genug Kontext liefert, um zu wissen, was vor sich geht.

Er spricht nicht über die Welt der eingebetteten Systeme, sondern über die Linux-Kernel-Entwicklung. Für mich ergibt sich die Relevanz daraus: C++ erfordert das Verständnis eines größeren Kontexts, und ich kann lernen, eine Reihe von Objektvorlagen zu verwenden, ich traue mich nicht, mich an sie zu erinnern, wenn ich den Code in ein paar Monaten aktualisieren muss.

(Auf der anderen Seite arbeite ich derzeit an einem eingebetteten Gerät mit Python (nicht C++, aber mit dem gleichen OOP-Paradigma), das genau dieses Problem haben wird. Zu meiner Verteidigung ist es ein eingebettetes System, das leistungsfähig genug ist, um als PC bezeichnet zu werden vor 10 Jahren.)

Wir mögen unterschiedlich sein, aber ich finde, dass ich beim Öffnen eines Projekts nicht sofort sagen kann, was vor sich geht, aber wenn ich etwas darüber weiß, was es tut, und ich etwas gut in C und etwas gut in C++ codiert habe, scheint C++ immer mehr zu sein klar. Sie müssen immer noch die Kapselung für eine gute Entwicklung in C implementieren, was C++ sehr einfach macht. Die richtige Verwendung von Klassen kann sehr deutlich machen, wo sich Ihre Schnittstellen befinden, und sie können vollständig über ein Objekt gehandhabt werden.
Völlig einverstanden mit Kapselung und Klassen. Überladen von Operatoren und Vererbung, nicht so sehr.
Haha, ja, das Überladen von Operatoren kann verwendet werden, um die Funktion von Code zu verschleiern. Wenn jemand den Operator überlädt, muss dies aus klaren Gründen geschehen oder überhaupt nicht geschehen. Die Vererbung sollte nur in bestimmten Fällen verwendet werden, in denen Sie tatsächlich etwas tun, das genau wie das übergeordnete Element mit einigen Ergänzungen ist. Ich denke, ich würde nicht jede Funktion in OOP verwenden. Ich habe beide verwendet, aber in einem eingebetteten System kann ich mir keinen Fall vorstellen, in dem ich das tun würde. Genauso wie ich denke, dass ein Compiler mit einem Limit von 80 Zeichen für Variablennamen sofort verschrottet werden sollte.
Ich habe mich gerade ein wenig übergeben bei dem Gedanken, eine MCU in Python zu programmieren ...
Du bist nicht der einzige, aber wenn es gut funktioniert und effizient ist, kann ich dir verzeihen.
Ich habe eine allgemeine Regel, wenn es die Aufgabe besser und schneller erledigen kann, dann werde ich es verwenden. in manchen Fällen ist das eine wichtiger als das andere.
Meine Behauptung ist, dass Programmierzeit oft teurer ist als Hardware. Stellen Sie sich vor, Sie versuchen, Netzwerkprogrammierung in C vs. Python durchzuführen, um 50 US-Dollar an Hardware zu sparen. Bei Anwendungen mit kleinem Volumen gewinnen Hochsprachen, jetzt wo eingebettete Hardware so leistungsfähig ist wie sie ist.
Ich stimme zu, ich arbeite in einem Unternehmen, in dem wir voraussichtlich viele Millionen Geräte verkaufen werden, daher sind die einmaligen Kosten pro Einheit eher gering, aber die Hardware zählt. In einem kleinen Projekt macht es Sinn. Ich meinte mit meinem früheren Kommentar, dass besser und schneller wichtig sind. Du brauchst aber nur schneller.
@pingswept Ich stimme zu, Programmierzeit ist teuer. Wie Linus feststellt, ist es im Fall von Open-Source-Freiwilligen unbezahlbar. Kommerziell gesehen haben die Entwicklungskosten gegenüber den Herstellungskosten jedoch einen großen Einfluss, sobald Sie ins Volumen gehen. Meine aktuelle Rolle ist die Portierung eines eingebetteten Linux+Python+Zigbee-Systems auf Arm-Cortex-M3, um Kosten zu sparen.
Klingt nach einem tollen Projekt Joby.
@Kortuk "Operatorüberladung kann verwendet werden, um die Funktion von Code zu verschleiern". Ich weiß nicht, warum jeder dies als Argument gegen das Überladen von Operatoren verwendet. Ich könnte sagen "Funktionsnamen können verwendet werden, um die Funktion von Code zu verschleiern". Ich könnte eine Funktion namens OpenFile() haben, die die Datei tatsächlich löscht.
@Rocketmagnet, einverstanden, der wichtige Teil besteht darin, Ihren Code so zu gestalten, dass er klaren und prägnanten Methoden folgt und einen Operator nur überlädt, wenn die Überladung sinnvoll ist, z.
Python ist eine schöne Sprache, aber eher für Betriebssysteme konzipiert. Aber ich hätte kein Problem damit, es für MCUs auszuprobieren, wenn jemand die richtige Plattform hätte ... Python wird ziemlich oft mit C-Bibliotheken erweitert, wie ich gesehen habe. Ich programmiere meinen Himbeer-Pi in Python, ist das ein eingebettetes System?

Ich denke, andere Antworten haben die Vor- und Nachteile und Entscheidungsfaktoren ziemlich gut dargestellt, daher möchte ich nur zusammenfassen und ein paar Kommentare hinzufügen.

Für kleine Mikrocontroller (8-Bit) auf keinen Fall. Sie bitten nur darum, sich selbst zu verletzen, es gibt keinen Gewinn und Sie werden zu viele Ressourcen aufgeben.

Für High-End-Mikrocontroller (z. B. 32-Bit, 10 oder 100 MB für RAM und Speicher) mit einem anständigen Betriebssystem ist es vollkommen in Ordnung und, ich wage zu sagen, sogar zu empfehlen.

Die Frage ist also: Wo ist die Grenze?

Ich weiß es nicht genau, aber ich habe einmal ein System für ein 16-Bit-uC mit 1 MB RAM und 1 MB Speicher in C++ entwickelt, nur um es später zu bereuen. Ja, es hat funktioniert, aber die zusätzliche Arbeit, die ich hatte, war es nicht wert. Ich musste es passend machen, sicherstellen, dass Dinge wie Ausnahmen keine Lecks produzieren würden (der OS+RTL-Support war ziemlich fehlerhaft und unzuverlässig). Darüber hinaus führt eine OO-App normalerweise viele kleine Zuweisungen durch, und der Haufen Overhead für diese war ein weiterer Alptraum.

Angesichts dieser Erfahrung würde ich für zukünftige Projekte davon ausgehen, dass ich C++ nur in Systemen mit mindestens 16 Bit und mit mindestens 16 MB für RAM und Speicher wählen werde. Das ist eine willkürliche Grenze und wird wahrscheinlich je nach Art der Anwendung, Codierungsstilen und Redewendungen usw. variieren. Aber angesichts der Vorbehalte würde ich einen ähnlichen Ansatz empfehlen.

Ich muss hier widersprechen, es gibt keinen plötzlichen Punkt, an dem C++ aufgrund von Systemressourcen akzeptabel wird, gute Designpraxis kann den Fußabdruck von C++ dort halten, wo der Fußabdruck von C ist. Dies führt zu Code mit OOP-Designs, die den gleichen Platz einnehmen. Schlecht geschriebenes C kann genauso schlimm sein.
Nun, es hängt davon ab, wie groß Ihre Anwendung ist und wie oft Sie bestimmte Funktionen verwenden, die mehr Platz benötigen (wie Vorlagen und Ausnahmen). Aber ich persönlich nutze lieber C, als mich auf ein zurückhaltendes C++ beschränken zu müssen. Aber selbst dann haben Sie den Overhead einer größeren RTL, virtueller Methoden-Thunks, Konstruktor/Destruktor-Kettenaufrufe ... diese Effekte können durch sorgfältiges Codieren gemildert werden, aber dann verlieren Sie den Hauptgrund für die Verwendung von C++, Abstraktion und Perspektive auf hohem Niveau.
"Aber selbst dann haben Sie den Overhead einer größeren RTL" - Unsinn, wenn ich C++ verwende, bekomme ich die gleiche RTL wie für C: im Wesentlichen 0.
"Virtual Method Thunks" - keine Ahnung, was das ist, aber Sie bekommen sie sicherlich nicht, es sei denn, Sie verwenden virtual functions , in diesem Fall hätte Ihr äquivalentes C-Programm Funktionszeiger gehabt.
"Ich würde lieber C verwenden, als mich auf ein zurückhaltendes C++ beschränken zu müssen" - Ich kann nicht für Sie sprechen, aber wenn ich C auf einem kleinen Mikrocontroller mache, verwende ich zum Beispiel ein umtrainiertes C: kein Heap, keine Floats , kein setjmp/jongjmp. Ebenso habe ich ein zurückhaltendes C++ verwendet.

Es gibt einige Funktionen von C++, die in eingebetteten Systemen nützlich sind. Es gibt andere, wie Ausnahmen, die teuer sein können und deren Kosten nicht immer offensichtlich sind.

Wenn ich meine Brüder hätte, gäbe es eine populäre Sprache, die das Beste aus beiden Welten kombiniert und einige Merkmale enthält, die in beiden Sprachen fehlen; Einige Anbieter bieten einige solcher Funktionen an, aber es gibt keine Standards. Ein paar Dinge, die ich sehen möchte:

  1. Ausnahmebehandlung etwas mehr wie Java, wo Funktionen, die Ausnahmen auslösen oder lecken können, als solche deklariert werden müssen. Während eine Anforderung für solche Deklarationen aus Programmierperspektive etwas ärgerlich sein kann, würde es die Klarheit des Codes in Fällen verbessern, in denen eine Funktion bei Erfolg eine beliebige Ganzzahl zurückgeben kann, aber auch fehlschlagen kann. Viele Plattformen könnten dies kostengünstig im Code handhaben, indem sie zB den Rückgabewert in einem Register und eine Erfolgs-/Fehleranzeige im Carry-Flag haben.
  2. Überladen nur von statischen und Inline-Funktionen; Mein Verständnis ist, dass die Standardisierungsgremien für C eine Funktionsüberladung vermieden haben, um die Notwendigkeit einer Namensverstümmelung zu vermeiden. Das Zulassen von Überladungen nur von statischen und Inline-Funktionen würde dieses Problem vermeiden und 99,9 % des Vorteils des Überladens externer Funktionen bieten (da .h-Dateien Inline-Überladungen in Form von anders benannten externen Funktionen definieren könnten).
  3. Überladungen für beliebige oder bestimmte, zur Kompilierzeit auflösbare konstante Parameterwerte. Einige Funktionen können sehr effizient eingebunden werden, wenn sie mit einem beliebigen konstanten Wert übergeben werden, aber sehr schlecht, wenn eine Variable übergeben wird. Andere Zeitcodes, die eine Optimierung sein können, wenn ein Wert konstant ist, können eine Pessimierung sein, wenn dies nicht der Fall ist. Zum Beispiel:
    inline void copy_uint32s(uint32_t *dest, const uint32_t *src, __is_const int n)
    {
      wenn (n <= 0) zurück;
      sonst wenn (n == 1) {dest[0] = src[0];}
      Sonst wenn (n == 2) {dest[0] = src[0]; Ziel[1] = Quelle[1];}
      Sonst wenn (n == 3) {dest[0] = src[0]; Ziel[1] = Quelle[1]; Ziel[2] = Quelle[2];}
      Sonst wenn (n == 4) {dest[0] = src[0]; Ziel[1] = Quelle[1]; Ziel[2] = Quelle[2]; Ziel[3] = Quelle[3];}
      sonst memcpy((void*)dest, (const void*)src, n*sizeof(*src));
    }
    
    Wenn 'n' zur Kompilierzeit ausgewertet werden kann, ist der obige Code effizienter als ein Aufruf von memcpy, aber wenn 'n' zur Kompilierzeit nicht ausgewertet werden kann, wäre der generierte Code viel größer und langsamer als Code, der einfach heißt memcpy.

Ich weiß, dass der Vater von C++ nicht allzu scharf auf eine reine Embedded-Version von C++ ist, aber ich würde denken, dass es einige erhebliche Verbesserungen gegenüber der Verwendung von C bieten könnte.

Weiß jemand, ob so etwas wie das oben Genannte für irgendeine Art von Standard in Betracht gezogen wird?

@Joby Taffey: Ich glaube, ich habe meinen Beitrag bearbeitet, um zu erwähnen, dass der Schöpfer von C ++ nicht an einer eingebetteten Teilmenge interessiert war. Ich bin mir bewusst, dass es Bemühungen gab, aber nach meinem Verständnis waren sie nicht wirklich so weit gekommen. Ich denke, es gäbe definitiv eine Verwendung für eine standardisierte Sprache, die für 8-Bit-Prozessoren geeignet wäre, und Funktionen, wie ich sie oben beschrieben habe, würden auf jeder Plattform nützlich erscheinen. Haben Sie von Sprachen gehört, die Dinge wie Nr. 3 oben anbieten? Es scheint sehr nützlich zu sein, aber ich habe noch nie gesehen, dass eine Sprache es anbietet.
„Der Vater von C++“ hat null Erfahrung in der Programmierung eingebetteter Systeme, also warum sollte sich irgendjemand um seine Meinung kümmern?
@Lundin: Die Tatsache, dass sich einige einflussreiche Leute um seine Meinung zu verschiedenen Themen zu kümmern scheinen, scheint an und für sich ein Grund für andere Leute zu sein, dies zu tun. Ich denke, dass, seit ich das oben Gesagte geschrieben habe, die zunehmende Leistungsfähigkeit von Vorlagen möglicherweise neue Möglichkeiten für Überladungen hinzugefügt hat, die darauf basieren, welche Konstanten zur Kompilierzeit aufgelöst werden können, wenn auch weit weniger sauber, als wenn so etwas als Kompilierung unterstützt würde. Zeitfunktion (soweit ich weiß, würde man eine Vorlage angeben, die verschiedene Dinge der Reihe nach ausprobieren und mit der ersten gehen sollte, die nicht fehlschlägt ...
... aber das würde erfordern, dass der Compiler einiges an Aufwand verschwendet, um mögliche Substitutionen zu kompilieren, die dann verworfen würden. In der Lage zu sein, sauberer zu sagen "Wenn dies eine Konstante ist, tue dies; andernfalls tue dies" ohne "Fehlstarts" scheint ein saubererer Ansatz zu sein.
Ja gut, der Hauptgrund, warum EC++ so kühl aufgenommen wurde, scheint dieser unscharfe "Vater in den Wolken" zu sein, der wahrscheinlich noch nie einen Mikrocontroller gesehen hat. Ich habe vor 10 Jahren ein Mikrocontroller-Projekt in EC++ gemacht und es machte damals sehr viel Sinn für mich. Aber das war auch das letzte Mal, dass ich C++ auf einem eingebetteten System verwendet habe, also bin ich ziemlich veraltet :) Heute klingt MISRA C++ nach einer interessanten Alternative, wenn man eine eingeschränkte Teilmenge der Sprache benötigt.
@Lundin: Ich bin mit MISRA C++ nicht vertraut. Ich denke, MISRA C sollte sich wahrscheinlich von "anderem" C abspalten und angeben, dass zB ein MISRA C-Compiler bei gegebener uint16_t x;Anweisung x=(uint16_t)(x*x);ein mod-65536-Ergebnis liefern muss, unabhängig vom Wert xoder der Größe von int. Obwohl der Standard nie irgendwelche Anforderungen gestellt hat, wenn die Größe von int17 bis 32 Bit und x46341 oder höher ist, verhalten sich 32-Bit-Compiler in solchen Fällen früher genauso wie 16-Bit-Compiler, aber heutzutage können sie sich auf seltsame und bizarre Weise verhalten.
@Lundin: Obwohl ich annehme, dass man argumentieren könnte, dass x=(uint16_t)(1u*x*x);es besser wäre, den Code so zu schreiben, da selbst ein hypermoderner Compiler ihn nicht durcheinander bringen kann, sehe ich keine Möglichkeit, eine Sprachspezifikation zu verwenden, die nur Compiler erfordert sich konsequent verhalten, wenn die letztere Formulierung als "besser" angesehen werden sollte als eine, die vorschreibt, dass die erstere Formulierung die gleichen Ergebnisse liefern muss, wenn intsie 17 bis 32 Bit beträgt, wie dies bei größeren oder kleineren intGrößen der Fall wäre.

C++ ist mehr als eine Programmiersprache:

a) Es ist ein "besseres" C b) Es ist eine objektorientierte Sprache c) Es ist eine Sprache, die uns erlaubt, generische Programme zu schreiben

Obwohl alle diese Funktionen separat verwendet werden können, werden die besten Ergebnisse erzielt, wenn alle drei gleichzeitig verwendet werden. Wenn Sie sich jedoch entscheiden, nur eine davon auszuwählen, wird die Qualität der eingebetteten Software steigen.

a) Es ist ein "besseres" C

C++ ist eine stark typisierte Sprache; stärker als C. Ihre Programme werden von dieser Funktion profitieren.

Manche Leute haben Angst vor Pointern. C++ enthält die Referenzen. Überladene Funktionen.

Und erwähnenswert: Keines dieser Features tritt in größeren oder langsameren Programmen auf.

b) Es ist eine objektorientierte Sprache

Jemand hat in diesem Beitrag gesagt, dass es keine gute Idee ist, die Maschine in Mikrocontrollern zu abstrahieren. Falsch! Wir alle, die Embedded-Ingenieure, haben die Maschine immer abstrahiert, nur mit einer anderen Syntax als der von C++. Das Problem, das ich bei diesem Argument sehe, ist, dass einige Programmierer nicht daran gewöhnt sind, in Objekten zu denken, so dass sie die Vorteile von OOP nicht sehen.

Wann immer Sie bereit sind, das Peripheriegerät eines Mikrocontrollers zu verwenden, ist es wahrscheinlich, dass das Peripheriegerät für uns (von Ihnen oder einem Dritten) in Form des Gerätetreibers abstrahiert wurde. Wie ich bereits sagte, verwendet dieser Treiber die C-Syntax, wie das nächste Beispiel zeigt (direkt aus einem NXP LPC1114-Beispiel entnommen):

/* Timer-Setup für Match und Interrupt bei TICKRATE_HZ */

Chip_TIMER_Reset (LPC_TIMER32_0);

Chip_TIMER_MatchEnableInt (LPC_TIMER32_0, 1);

Chip_TIMER_SetMatch (LPC_TIMER32_0, 1, (timerFreq / TICKRATE_HZ2));

Chip_TIMER_ResetOnMatchEnable (LPC_TIMER32_0, 1);

Chip_TIMER_Enable (LPC_TIMER32_0);

Siehst du die Abstraktion? Wenn Sie also C++ für denselben Zweck verwenden, wird die Abstraktion durch den Abstraktions- und Kapselungsmechanismus von C++ zum Nulltarif auf die nächste Ebene gebracht!

c) Es ist eine Sprache, die es uns erlaubt, generische Programme zu schreiben

Generische Programme werden durch Vorlagen erreicht, und Vorlagen sind für unsere Programme ebenfalls kostenlos.

Außerdem wird statischer Polymorphismus mit Schablonen erreicht.

Virtuelle Methoden, RTTI und Ausnahmen.

Bei der Verwendung virtueller Methoden gibt es einen Kompromiss: bessere Software vs. Leistungseinbußen. Denken Sie jedoch daran, dass die dynamische Bindung wahrscheinlich mithilfe einer virtuellen Tabelle (einem Array von Funktionszeigern) implementiert wird. Ich habe das Gleiche oft in C gemacht (sogar regelmäßig), daher sehe ich keine Nachteile bei der Verwendung virtueller Methoden. Außerdem sind virtuelle Methoden in C++ eleganter.

Abschließend noch ein Ratschlag zu RTTI und Ausnahmen: VERWENDEN SIE SIE NICHT in eingebetteten Systemen. Vermeiden Sie sie um jeden Preis!!

Mein Hintergrund, eingebettet (MCU, PC, Unix, andere), Echtzeit. Sicherheitskritisch. Ich habe STL einem früheren Arbeitgeber vorgestellt. Das mache ich nicht mehr.

Einige Flame-Inhalte

Ist C++ für eingebettete Systeme geeignet?

Meh. C++ ist mühsam zu schreiben und mühsam zu warten. C+ ist irgendwie in Ordnung (benutze einige Funktionen nicht)

C++ in Mikrocontrollern? Echtzeitbetriebssysteme? Toaster? Embedded-PCs?

Wieder sage ich Meh. C+ ist nicht so schlimm, aber ADA ist weniger schmerzhaft (und das sagt wirklich etwas aus). Wenn Sie wie ich Glück haben, können Sie eingebettetes Java verwenden. Geprüfter Array-Zugriff und keine Zeigerarithmetik sorgen für sehr zuverlässigen Code. Garbage Collectors in Embedded Java haben nicht die höchste Priorität, und es gibt eine bereichsbezogene Wiederverwendung von Speicher und Objekten, sodass gut gestalteter Code für immer ohne GC ausgeführt werden kann.

Ist OOP auf Mikrocontrollern nützlich?

Sicher ist. Der UART ist ein Objekt..... Der DMAC ist ein Objekt...

Objektzustandsmaschinen sind sehr einfach.

Entfernt C++ den Programmierer zu weit von der Hardware, um effizient zu sein?

Wenn es sich nicht um einen PDP-11 handelt, ist C nicht Ihre CPU. C++ war ursprünglich ein Präprozessor auf C, also hörte Bjarne Stroustrup auf, ausgelacht zu werden, weil er bei AT&T langsame Simula-Simulationen hatte. C++ ist nicht deine CPU.

Holen Sie sich eine MCU, die Java-Bytecodes ausführt. Programm in Java. Lach über die C-Jungs.

Sollte Arduinos C++ (ohne dynamische Speicherverwaltung, Vorlagen, Ausnahmen) als "echtes C++" betrachtet werden?

Nö. genau wie alle bastardisierten C-Compiler da draußen für MCUs.

Forth, Embedded Java oder Embedded ADA sind standardisiert (ish); alles andere ist Leid.

Ist es so einfach, Mikrocontroller zu finden, die Java unterstützen? Ich denke, das würde die Auswahl erheblich einschränken. Und was sind Ihre Erfahrungen mit Leistungseinbußen (da Sie in uCs normalerweise kein JIT hätten)? Was ist mit den Auswirkungen der GC-Unvorhersehbarkeit in Echtzeitsystemen?
Welche MCUs gibt es, die Embedded Java unterstützen?
www.ajile.com für den Anfang.
+1 für Ada. Es spricht viel für Embedded, einschließlich Arduinos.
Portable Java VM für Micros, geschrieben in C, ist Open Source. dmitry.co/index.php?p=./04.Thoughts/…

<schimpfen>

Ich denke, C++ ist in erster Linie eine beschissene Sprache. Wenn Sie OOP verwenden möchten, schreiben Sie Java-Programme. C++ unternimmt nichts, um OOP-Paradigmen zu erzwingen, da der direkte Speicherzugriff vollständig in Ihrer Macht steht, ihn zu (missbrauchen) zu verwenden.

Wenn Sie eine MCU haben, sprechen Sie höchstwahrscheinlich von weniger als 100 KB Flash-Speicher. Sie möchten in einer Sprache programmieren, deren Abstraktion von Speicher ist: Wenn ich eine Variable oder ein Array deklariere, erhält sie Speicher, Punkt; malloc (auch bekannt als "new"-Schlüsselwort in C++) sollte mehr oder weniger von der Verwendung in eingebetteter Software ausgeschlossen werden, außer vielleicht in seltenen Fällen ein Aufruf während des Programmstarts.

Verdammt, es gibt (häufig) Zeiten in der Embedded-Programmierung, in denen C nicht ganz niedrig genug ist und Sie Dinge wie das Zuweisen von Variablen zu Registern und das Schreiben von Inline-Assembler tun müssen, um Ihre Interrupt-Service-Routinen (ISRs) zu straffen. Schlüsselwörter wie „flüchtig“ werden verdammt wichtig, um sie zu verstehen. Sie verbringen viel Zeit damit, Speicher auf Bitebene zu manipulieren , nicht auf Objektebene .

Warum sollten Sie sich der Illusion hingeben wollen, dass die Dinge einfacher sind, als sie tatsächlich sind?

</rant>

Mein Problem hier ist einfach, warum ich die Komplexität des Treibers wissen möchte, der geschrieben wurde, um USART1 zu steuern, wenn er vollständig entwickelt wurde, um die Schnittstelle zu handhaben.
Ich habe Sie nicht abgelehnt, aber ich möchte darauf hinweisen, dass C++ OOP nicht erzwingen muss, sondern nur die Werkzeuge dafür bereitstellt. Die Durchsetzung guter Codierungsstandards ist die Aufgabe des Entwicklers. Es kann helfen, wenn die Sprache es einfacher macht, aber die Sprache allein wird es nie tun. C kann in manchen Fällen unlesbar sein.
Alle Sprachen sind für etwas gut. C++ ist schnell. OOP macht es, wenn es gut gemacht ist, für mehrere Entwickler viel einfacher, parallel zu arbeiten und für das Unbekannte zu codieren. Ich denke, das ist der Grund, warum es in der Spieleentwicklung so viel Zugkraft hat.
Ja, ich stimme zu. Der Grund, warum ich es für die eingebettete Welt sehe, ist die schiere Menge an Merkmalen und Funktionen, die vielen der verschiedenen bereits vorhandenen Systeme hinzugefügt und neue Systeme entwickelt werden. Projekte werden immer größer. Entweder brauchen wir länger, um sie zu entwickeln, oder wir fangen an, das anzuwenden und zu verzerren, was die CS-Welt bereits auf PCs getan hat.
Ich stimme Vicatsu zu. Warum wird er abgewählt? Ich bin Software-Profi mit mehr als 20 Jahren Erfahrung (nicht eingebettet). Und meiner Meinung nach ist C++ die teuerste Sprache, leider beliebt wegen der frühen Popularität von Plain C. Plain C war nur aus einem Grund beliebt: Es gab ein einziges wirklich gutes Buch über diese Sprache, das wirklich gut geschrieben war (die Benutzerfreundlichkeit der Sprache C und C++ ist das komplette Gegenteil)
@Rocketmagnet Diese Frage bat um eine Antwort wie meine:-p. Es ist nur eine Meinung, und ich kann zustimmen, anderen zu widersprechen. Verdammt, ich mache viel Arduino-Programmierung und das ist alles C++-basiert. Das heißt nicht, dass ich es mögen muss.
Ich würde sogar noch einen Schritt weiter gehen. Wenn Ihr "eingebettetes System" 8 Bit ist, sehr speicher- / ressourcenbeschränkt ist und kein Betriebssystem vorhanden ist, fügt alles, was über das gute alte C hinausgeht, nur Overhead hinzu. Wenn es sich andererseits beispielsweise um ein BeagleBoard mit Linux handelt, viele Ressourcen vorhanden sind und keine nahezu RT-Anforderungen bestehen, verwenden Sie Mono und C#. Produktivität und Wartbarkeit werden um Größenordnungen höher sein. C++ bereitet Ihnen nur all die Kopfschmerzen von C ohne die Einfachheit und den geringen Overhead.
malloc (aka "new" keyword in C++) should be more or less banned from use in embedded software--> Die zusätzliche Süße (Sarkasmus beabsichtigt) ist, dass newAusnahmen auch werfen können. Nun viel Glück damit.

Eingebettete Systeme sind darauf ausgelegt, eine bestimmte Aufgabe zu erledigen, anstatt ein Allzweckcomputer für mehrere Aufgaben zu sein. Ein eingebettetes System ist eine Kombination aus Computerhardware und -software. C ist die Mutter aller modernen Sprachen. Es ist eine niedrige, aber leistungsstarke Sprache und behandelt alle Arten von Hardware. Daher ist C/C++ eine optimale Wahl für die Entwicklung von Software für eingebettete Systeme, die für jedes eingebettete System sehr nützlich ist. Wie wir wissen, ist C eine Entwicklungssprache. Das Betriebssystem UNIX ist in C geschrieben. Da es bei erfolgreicher Softwareentwicklung so häufig um die Auswahl der besten Sprache für ein bestimmtes Projekt geht, ist es überraschend festzustellen, dass sich die Sprache C/C++ sowohl für 8-Bit- als auch für 64-Bit-Prozessoren bewährt hat ; in Systemen mit Bytes, Kilobytes und Megabytes Speicher. C hat den Vorteil der Prozessorunabhängigkeit, Dadurch können sich Programmierer auf Algorithmen und Anwendungen konzentrieren, anstatt auf die Details einer bestimmten Prozessorarchitektur. Viele dieser Vorteile gelten jedoch gleichermaßen für andere Hochsprachen. Aber C/C++ war dort erfolgreich, wo so viele andere Sprachen weitgehend gescheitert sind?

Ich bin mir wirklich nicht sicher, was das zur Diskussion beiträgt.