Pracując nad projektem osadzonym, próbowałem raz pracować w języku C i po prostu nie mogłem tego znieść. Było tak rozwlekłe, że trudno było cokolwiek przeczytać. Podobały mi się również napisane przeze mnie kontenery zoptymalizowane pod kątem osadzania, które musiały zmienić się w znacznie mniej bezpieczne i trudniejsze do naprawienia #definebloki.
Kod, który w C ++ wyglądał następująco:
if(uart[0]->Send(pktQueue.Top(), sizeof(Packet)))
pktQueue.Dequeue(1);
zamienia się w:
if(UART_uchar_SendBlock(uart[0], Queue_Packet_Top(pktQueue), sizeof(Packet)))
Queue_Packet_Dequeue(pktQueue, 1)
co wielu ludzi prawdopodobnie powie, że jest w porządku, ale staje się śmieszne, jeśli musisz zrobić więcej niż kilka wywołań „metod” w jednej linii. Dwa wiersze C ++ zamieniłyby się w pięć C (ze względu na 80-znakowe ograniczenia długości wiersza). Oba generowałyby ten sam kod, więc nie obchodziło się to z procesorem docelowym!
Pewnego razu (w 1995) próbowałem napisać dużo C dla wieloprocesorowego programu do przetwarzania danych. Rodzaj, w którym każdy procesor ma własną pamięć i program. Kompilator dostarczony przez dostawcę był kompilatorem C (czymś w rodzaju pochodnej HighC), ich biblioteki były zamkniętymi źródłami, więc nie mogłem używać GCC do budowania, a ich API zostały zaprojektowane z myślą, że twoje programy będą przede wszystkim inicjalizacją / procesem / terminate, więc komunikacja między procesorami była w najlepszym przypadku elementarna.
Dostałem około miesiąca, zanim się poddałem, znalazłem kopię cfront i włamałem ją do plików makefile, abym mógł używać C ++. Cfront nawet nie obsługiwał szablonów, ale kod C ++ był dużo, dużo jaśniejszy.
Ogólne, bezpieczne dla typów struktury danych (przy użyciu szablonów).
Najbliższą rzeczą, jaką C ma do szablonów, jest zadeklarowanie pliku nagłówkowego z dużą ilością kodu, który wygląda następująco:
TYPE * Queue_##TYPE##_Top(Queue_##TYPE##* const this)
{ }
następnie pociągnij go za pomocą czegoś takiego:
#define TYPE Packet
#include "Queue.h"
#undef TYPE
Zauważ, że to nie zadziała dla typów złożonych (np. Bez kolejek unsigned char), chyba że zrobisz typedefpierwszy.
Aha, i pamiętaj, jeśli ten kod nie jest nigdzie używany, to nawet nie wiesz, czy jest poprawny składniowo.
EDYCJA: Jeszcze jedno: musisz ręcznie zarządzać tworzeniem kodu. Jeśli Twój kod „szablonu” nie obejmuje wszystkich funkcji wbudowanych, musisz mieć pewną kontrolę, aby upewnić się, że instancje są tworzone tylko raz, aby linker nie wypluł stosu błędów „wielu wystąpień Foo” .
Aby to zrobić, musisz umieścić elementy niewymienione w sekcji „implementacja” w pliku nagłówkowym:
#ifdef implementation_##TYPE
#endif
Następnie w jednym miejscu, w całym kodzie na wariant szablonu , musisz:
#define TYPE Packet
#define implementation_Packet
#include "Queue.h"
#undef TYPE
Ponadto ta sekcja implementacji musi znajdować się poza standardową #ifndef/ #define/ #endiflitanią, ponieważ możesz dołączyć plik nagłówkowy szablonu do innego pliku nagłówkowego, ale musisz później utworzyć instancję w .cpliku.
Tak, szybko robi się brzydko. Dlatego większość programistów C nawet nie próbuje.
RAII.
Zwłaszcza w funkcjach z wieloma punktami powrotu, np. Nie trzeba pamiętać o zwolnieniu muteksu w każdym punkcie powrotu.
Cóż, zapomnij o swoim ładnym kodzie i przyzwyczaj się do tego, że wszystkie punkty powrotu (z wyjątkiem końca funkcji) są 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:;
}
Queue_##TYPE##_Top_exit:
Mutex_Lock(this->lock);
return result;
}
Destruktory w ogóle.
Oznacza to, że raz napiszesz d'tor dla MyClass, a następnie jeśli instancja MyClass jest członkiem MyOtherClass, MyOtherClass nie musi jawnie deinicjalizować instancji MyClass - jej d'tor jest wywoływany automatycznie.
Konstrukcja obiektu musi być jawnie obsługiwana w ten sam sposób.
Przestrzenie nazw.
W rzeczywistości jest to proste do naprawienia: po prostu umieść przedrostek na każdym symbolu. Jest to główna przyczyna wzdęcia źródła, o którym mówiłem wcześniej (ponieważ klasy są niejawnymi przestrzeniami nazw). Ludzie z C żyją tak, cóż, od zawsze i prawdopodobnie nie zobaczą, o co chodzi.
YMMV