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 #defineBlöcke verwandeln mussten .
Code, der in C ++ so aussah:
if(uart[0]->Send(pktQueue.Top(), sizeof(Packet)))
pktQueue.Dequeue(1);
verwandelt sich in:
if(UART_uchar_SendBlock(uart[0], Queue_Packet_Top(pktQueue), sizeof(Packet)))
Queue_Packet_Dequeue(pktQueue, 1);
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:
TYPE * Queue_##TYPE##_Top(Queue_##TYPE##* const this)
{ /* ... */ }
dann ziehen Sie es mit etwas wie:
#define TYPE Packet
#include "Queue.h"
#undef TYPE
Beachten Sie, dass dies für zusammengesetzte Typen (z. B. keine Warteschlangen von unsigned char) nur funktioniert, wenn Sie eine typedeferste 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:
#ifdef implementation_##TYPE
/* Non-inlines, "static members", global definitions, etc. go here. */
#endif
Und dann müssen Sie an einer Stelle in Ihrem gesamten Code pro Vorlagenvariante :
#define TYPE Packet
#define implementation_Packet
#include "Queue.h"
#undef TYPE
Außerdem muss dieser Implementierungsabschnitt außerhalb der Standard #ifndef/ #define/ #endifLitanei liegen, da Sie die Vorlagen-Header-Datei möglicherweise in eine andere Header-Datei aufnehmen, diese jedoch anschließend in einer .cDatei 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 gotos:
TYPE * Queue_##TYPE##_Top(Queue_##TYPE##* const this)
{
TYPE * result;
Mutex_Lock(this->lock);
if(this->head == this->tail)
{
result = 0;
goto Queue_##TYPE##_Top_exit:;
}
/* Figure out `result` for real, then fall through to... */
Queue_##TYPE##_Top_exit:
Mutex_Lock(this->lock);
return result;
}
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