Nach einigen Jahren Codierung in C ++ wurde mir kürzlich eine Jobcodierung in C im eingebetteten Bereich angeboten.
Abgesehen von der Frage, ob es richtig oder falsch ist, C ++ im eingebetteten Feld zu schließen, gibt es einige Funktionen / Redewendungen in C ++, die ich sehr vermissen würde. Nur um ein paar zu nennen:
- Generische, typsichere Datenstrukturen (unter Verwendung von Vorlagen).
- RAII. Insbesondere bei Funktionen mit mehreren Rückgabepunkten, z. B. wenn Sie nicht daran denken müssen, den Mutex an jedem Rückgabepunkt freizugeben.
- Zerstörer im Allgemeinen. Wenn Sie also einmal einen d'tor für MyClass schreiben, muss MyOtherClass die MyClass-Instanz nicht explizit deinitialisieren, wenn eine MyClass-Instanz Mitglied von MyOtherClass ist - ihr d'tor wird automatisch aufgerufen.
- Namespaces.
Welche Erfahrungen haben Sie mit dem Wechsel von C ++ zu C gemacht?
Welche C-Substitute haben Sie für Ihre bevorzugten C ++ - Funktionen / Redewendungen gefunden? Haben Sie C-Funktionen entdeckt, die C ++ gerne hätte?
Antworten:
Bei der Arbeit an einem eingebetteten Projekt habe ich einmal versucht, in allen C zu arbeiten, und konnte es einfach nicht aushalten. Es war so ausführlich, dass es schwierig war, etwas zu lesen. Außerdem gefielen mir die für eingebettete Container optimierten Container, die ich geschrieben hatte und die sich in weniger sichere und schwer zu reparierende
#define
Blöcke verwandeln mussten .Code, der in C ++ so aussah:
verwandelt sich in:
Was viele Leute wahrscheinlich sagen werden, ist in Ordnung, wird aber lächerlich, wenn Sie mehr als ein paar "Methoden" -Aufrufe in einer Zeile ausführen müssen. Aus zwei Zeilen C ++ werden fünf Zeilen C (aufgrund von Zeilenlängenbeschränkungen von 80 Zeichen). Beide würden den gleichen Code generieren, es ist also nicht so, als würde sich der Zielprozessor darum kümmern!
Einmal (1995) habe ich versucht, viel C für ein Multiprozessor-Datenverarbeitungsprogramm zu schreiben. Die Art, bei der jeder Prozessor seinen eigenen Speicher und sein eigenes Programm hat. Der vom Hersteller bereitgestellte Compiler war ein C-Compiler (eine Art HighC-Derivat), seine Bibliotheken waren Closed Source, sodass ich GCC nicht zum Erstellen verwenden konnte, und ihre APIs wurden mit der Einstellung entworfen, dass Ihre Programme in erster Linie die Initialisierung / der Prozess sein würden Ich beende die Sorte, so dass die Kommunikation zwischen Prozessoren bestenfalls rudimentär war.
Ich kam ungefähr einen Monat, bevor ich aufgab, und fand eine Kopie von cfront fand und sie in die Makefiles hackte, damit ich C ++ verwenden konnte. Cfront unterstützte nicht einmal Vorlagen, aber der C ++ - Code war viel, viel klarer.
Generische, typsichere Datenstrukturen (unter Verwendung von Vorlagen).
Das, was C den Vorlagen am nächsten kommt, ist das Deklarieren einer Header-Datei mit viel Code, der wie folgt aussieht:
dann ziehen Sie es mit etwas wie:
Beachten Sie, dass dies für zusammengesetzte Typen (z. B. keine Warteschlangen von
unsigned char
) nur funktioniert, wenn Sie einetypedef
erste erstellen .Oh, und denken Sie daran, wenn dieser Code nirgendwo verwendet wird, wissen Sie nicht einmal, ob er syntaktisch korrekt ist.
BEARBEITEN: Noch etwas: Sie müssen die Instanziierung von Code manuell verwalten. Wenn Ihr "Vorlagen" -Code nicht alles ist Inline-Funktionen enthält, müssen Sie einige Steuerelemente festlegen, um sicherzustellen, dass die Dinge nur einmal instanziiert werden, damit Ihr Linker keinen Stapel "mehrerer Instanzen von Foo" -Fehlern ausspuckt .
Dazu müssen Sie das nicht inlinierte Material in einen Abschnitt "Implementierung" in Ihrer Header-Datei einfügen:
Und dann müssen Sie an einer Stelle in Ihrem gesamten Code pro Vorlagenvariante :
Außerdem muss dieser Implementierungsabschnitt außerhalb der Standard
#ifndef
/#define
/#endif
Litanei liegen, da Sie die Vorlagen-Header-Datei möglicherweise in eine andere Header-Datei aufnehmen, diese jedoch anschließend in einer.c
Datei instanziieren müssen .Ja, es wird schnell hässlich. Deshalb versuchen es die meisten C-Programmierer nicht einmal.
RAII.
Insbesondere bei Funktionen mit mehreren Rückgabepunkten, z. B. wenn Sie nicht daran denken müssen, den Mutex an jedem Rückgabepunkt freizugeben.
Nun, vergessen Sie Ihre hübschen Code und erhalten auf alle Ihre Rückkehrpunkte (außer dem Ende der Funktion) verwendet wird
goto
s:Zerstörer im Allgemeinen.
Wenn Sie also einmal einen d'tor für MyClass schreiben, muss MyOtherClass die MyClass-Instanz nicht explizit deinitialisieren, wenn eine MyClass-Instanz Mitglied von MyOtherClass ist - ihr d'tor wird automatisch aufgerufen.
Die Objektkonstruktion muss explizit auf die gleiche Weise behandelt werden.
Namespaces.
Das ist eigentlich einfach zu beheben: Kleben Sie einfach ein Präfix auf jedes Symbol. Dies ist die Hauptursache für das Aufblähen der Quelle, über das ich zuvor gesprochen habe (da Klassen implizite Namespaces sind). Die C-Leute haben das schon immer gelebt und werden wahrscheinlich nicht sehen, was die große Sache ist.
YMMV
quelle
AT91C_BASE_PMC->PMC_MOR = (0x37 << 16) | BOARD_OSCOUNT | AT91C_CKGR_MOSCRCEN | AT91C_CKGR_MOSCXTEN | AT91C_CKGR_MOSCSEL;
BOARD_OSCOUNT
(was ist der Wert der Zeitüberschreitung für das Warten auf das Umschalten einer Uhr; klar, nicht wahr?) Tatsächlich ein#define
In istboard.h
. In derselben Funktion gibt es auch viel kopierten und eingefügten Spin-Loop-Code, der in einen zweizeiligen Code hätte umgewandelt werden sollen#define
(und als ich genau das tat, wurden ein paar Bytes Code gespeichert und der Code erstellt Funktion besser lesbar, indem Registersätze und Spin-Loops deutlicher hervorgehoben werden). Einer der Hauptgründe für die Verwendung von C ist, dass Sie damit alles mikromanagen und optimieren können, aber der meiste Code, den ich gesehen habe, stört nicht.Ich bin aus einem anderen Grund (einer allergischen Reaktion;) von C ++ zu C gewechselt, und es gibt nur wenige Dinge, die ich vermisse und einige Dinge, die ich gewonnen habe. Wenn Sie sich an C99 halten, gibt es Konstrukte, mit denen Sie insbesondere sehr gut und sicher programmieren können
for
Die Variable -scope kann Ihnen dabei helfen, eine bereichsgebundene Ressourcenverwaltung durchzuführen , insbesondere um sicherzustellen, dassunlock
Mutexe oderfree
Arrays auch bei vorläufigen Funktionsrückgaben vorhanden sind__VA_ARGS__
Makros können verwendet werden, um Standardargumente für Funktionen zu haben und um das Abrollen von Code durchzuführeninline
Funktionen und Makros, die sich gut kombinieren lassen, um überlastete Funktionen zu ersetzenquelle
for
Bereiche angegeben habe, landen Sie auf P99, wo Sie sich auch nach Beispielen und Beschreibungen der anderen Teile umsehen können.void DoSomething(unsigned char* buf, size_t bufSize) { unsigned char temp[bufSize]; ... }
) zur Laufzeitgröße (z. B. Stack) und Strukturinitialisierung durch Feldnamen (z. B. ) hinzufügten.struct Foo bar = { .field1 = 5, .field2 = 10 };
Letzteres würde ich gerne in C ++ sehen, insbesondere bei Nicht-POD-Objekten (z. B.UART uart[2] = { UART(0x378), UART(0x278) };
). .Für C
gibt es nichts Vergleichbares wie die STL. Es sind Bibliotheken verfügbar, die ähnliche Funktionen bieten, aber nicht mehr integriert sind.
Ich denke, das wäre eines meiner größten Probleme ... Zu wissen, mit welchem Tool ich das Problem lösen könnte, aber nicht die Tools in der Sprache zur Verfügung zu haben, die ich verwenden muss.
quelle
Der Unterschied zwischen C und C ++ ist die Vorhersagbarkeit des Verhaltens des Codes.
Es ist einfacher, mit großer Genauigkeit vorherzusagen, was Ihr Code in C tun wird. In C ++ wird es möglicherweise etwas schwieriger, eine genaue Vorhersage zu erstellen.
Die Vorhersagbarkeit in C gibt Ihnen eine bessere Kontrolle darüber, was Ihr Code tut, aber das bedeutet auch, dass Sie mehr tun müssen.
In C ++ können Sie weniger Code schreiben, um das Gleiche zu erreichen, aber (zumindest für mich) ich habe gelegentlich Probleme zu wissen, wie der Objektcode im Speicher angeordnet ist und welches Verhalten erwartet wird.
quelle
-s
Flag hinzu,gcc
um den Assembly-Dump abzurufen, nach der betreffenden Funktion zu suchen und mit dem Lesen zu beginnen. Es ist eine großartige Möglichkeit, die Macken einer kompilierten Sprache zu lernen.In meiner Arbeit - die übrigens eingebettet ist - wechsle ich ständig zwischen C und C ++ hin und her.
Wenn ich in C bin, vermisse ich von C ++:
Vorlagen (einschließlich, aber nicht beschränkt auf STL-Container). Ich verwende sie für spezielle Zähler, Pufferpools usw. (habe meine eigene Bibliothek mit Klassenvorlagen und Funktionsvorlagen aufgebaut, die ich in verschiedenen eingebetteten Projekten verwende).
sehr leistungsfähige Standardbibliothek
Destruktoren, die natürlich RAII ermöglichen (Mutexe, Interrupt-Deaktivierung, Tracing usw.)
Zugriffsspezifizierer, um besser durchzusetzen, wer was verwenden (nicht sehen) kann
Ich verwende die Vererbung für größere Projekte, und die integrierte Unterstützung von C ++ ist viel sauberer und schöner als der C-Hack, bei dem die Basisklasse als erstes Mitglied eingebettet wird (ganz zu schweigen vom automatischen Aufruf von Konstruktoren, Initialisierungslisten usw.). ) aber die oben aufgeführten Punkte sind die, die ich am meisten vermisse.
Außerdem verwendet wahrscheinlich nur etwa ein Drittel der eingebetteten C ++ - Projekte, an denen ich arbeite, Ausnahmen, sodass ich mich daran gewöhnt habe, ohne sie zu leben, damit ich sie nicht zu sehr vermisse, wenn ich zurück zu C gehe.
Auf der anderen Seite gibt es, wenn ich zu einem C-Projekt mit einer beträchtlichen Anzahl von Entwicklern zurückkehre, ganze Klassen von C ++ - Problemen, die ich gewohnt bin, Leuten zu erklären, die weggehen. Meistens Probleme aufgrund der Komplexität von C ++ und Leute, die glauben zu wissen, was los ist, aber sie sind wirklich am "C mit Klassen" Teil der C ++ - Konfidenzkurve .
Wenn ich die Wahl habe, würde ich es vorziehen, C ++ für ein Projekt zu verwenden, aber nur, wenn das Team ziemlich solide in der Sprache ist. Natürlich auch unter der Annahme, dass es sich nicht um ein 8K μC-Projekt handelt, bei dem ich sowieso effektiv "C" schreibe.
quelle
Einige Beobachtungen
quelle
Ziemlich genau die gleichen Gründe, die ich für die Verwendung von C ++ oder einer Mischung aus C / C ++ anstelle von reinem C habe. Ich kann ohne Namespaces leben, aber ich benutze sie die ganze Zeit, wenn der Codestandard dies zulässt. Der Grund dafür ist, dass Sie in C ++ viel kompakteren Code schreiben können. Das ist sehr nützlich für mich, ich schreibe Server in C ++, die ab und zu zum Absturz neigen. An diesem Punkt hilft es sehr, wenn der Code, den Sie betrachten, kurz und beständig ist. Betrachten Sie beispielsweise den folgenden Code:
In C sieht das so aus:
Keine Welt voller Unterschiede. Noch eine Codezeile, aber das summiert sich. Normalerweise versuchen Sie Ihr Bestes, um es sauber und schlank zu halten, aber manchmal müssen Sie etwas Komplexeres tun. Und in solchen Situationen schätzen Sie Ihre Zeilenanzahl. Eine weitere Zeile ist eine weitere Sache, die Sie sich ansehen müssen, wenn Sie herausfinden möchten, warum Ihr Broadcast-Netzwerk plötzlich keine Nachrichten mehr übermittelt.
Wie auch immer, ich finde, dass C ++ es mir ermöglicht, komplexere Dinge auf sichere Weise zu erledigen.
quelle
Ich denke, das Hauptproblem, warum es schwieriger ist, C ++ in einer eingebetteten Umgebung zu akzeptieren, ist der Mangel an Ingenieuren, die verstehen, wie man C ++ richtig verwendet.
Ja, die gleiche Argumentation kann auch auf C angewendet werden, aber zum Glück gibt es in C nicht so viele Fallstricke, die sich in den Fuß schießen können. In C ++ hingegen müssen Sie wissen, wann Sie bestimmte Funktionen in C ++ nicht verwenden sollen.
Alles in allem mag ich c ++. Ich verwende das auf der Ebene der O / S-Dienste, des Treibers, des Verwaltungscodes usw. Aber wenn Ihr Team nicht genug Erfahrung damit hat, wird es eine schwierige Herausforderung.
Ich hatte Erfahrung mit beiden. Als der Rest des Teams nicht dazu bereit war, war es eine totale Katastrophe. Auf der anderen Seite war es eine gute Erfahrung.
quelle
Ja! Ich habe beide Sprachen erlebt und fand, dass C ++ eine freundlichere Sprache ist. Es erleichtert mit mehr Funktionen. Es ist besser zu sagen, dass C ++ eine Obermenge der C-Sprache ist, da es zusätzliche Funktionen wie Polymorphismus, Interitanz, Überladung von Operatoren und Funktionen sowie benutzerdefinierte Datentypen bietet, die in C nicht wirklich unterstützt werden. Die tausend Codezeilen werden auf wenige Zeilen reduziert Die Hilfe der objektorientierten Programmierung ist der Hauptgrund für den Wechsel von C nach C ++.
quelle