Och tak, jest używane. Pracuję w dziedzinie przetwarzania pakietów sieciowych. Byłem w dwóch różnych firmach, w których przetwarzamy pakiety sieciowe. Tak więc działamy na poziomie Ethernet lub IP, a nie na poziomie powyżej TCP.
Co ciekawe, w obu firmach C wybrano zamiast C ++. W jednej z firm jeden z dwóch produktów został zbudowany na jądrze Linuksa, podczas gdy drugi produkt został zbudowany w przestrzeni użytkownika Linuksa. Produkt jądra oczywiście używał C, ponieważ jądro Linuksa jest programowane w C, ale zdecydowali się używać C również dla produktu przestrzeni użytkownika. Oba produkty zostały opracowane od około 2000 roku (produkt jądra nieco przed 2000 rokiem, a produkt przestrzeni użytkownika nieco po 2000 roku).
W firmie, do której poszedłem później, produkt został zbudowany na C, a nie na C ++. W rzeczywistości jest to kontynuacja projektu z połowy lat 90., chociaż ze względu na ostatnie wymagania dotyczące poprawy wydajności zdecydowano, że zasadniczo wszystko zostanie przepisane. Mieliśmy opcję wybrania C ++ z powodu tego przepisania, ale nie zrobiliśmy tego.
W dziedzinie przetwarzania pakietów sieciowych wiele się liczy. Chcę więc wdrożyć własną tabelę skrótów o wyższej wydajności niż istniejące tabele skrótów. Ja, a nie autor tabeli skrótów, wybieram, która funkcja skrótu ma być używana. Może chcę wydajności i wybiorę MurMurHash3 . Być może chcę bezpieczeństwa i idę po SipHash . Alokatory pamięci są oczywiście niestandardowe. W rzeczywistości wszystkie ważne struktury danych, których używamy, zostały zaimplementowane na zamówienie w celu uzyskania najwyższej możliwej wydajności.
Chociaż nic nie stoi na przeszkodzie, aby użyć C ++, jest to zwykle zły pomysł. Pojedynczy zgłoszony wyjątek na pakiet spowoduje spadek szybkości przetwarzania pakietów do niedopuszczalnego poziomu! Dlatego nie możemy używać wyjątków C ++. O wiele za wolno. Już używamy rodzaju zorientowanego obiektowo kodu C, implementując struktury danych jako struktury, a następnie implementując funkcje działające na tych strukturach. C ++ zezwalałby na posiadanie funkcji wirtualnych, ale ponownie wywołania funkcji wirtualnych zabiłyby wydajność, gdyby były używane wszędzie. Dlatego lepiej jest być jawnym i mieć wskaźnik funkcji, jeśli potrzebne są wirtualne wywołania funkcji.
C ++ zrobi wiele rzeczy za twoimi plecami: alokacja pamięci itp. Z drugiej strony, w C zwykle tak się nie dzieje. Możesz napisać funkcję, która alokuje pamięć, ale zwykle z interfejsu funkcji wynika, że alokacja ma miejsce.
Jako przykład rodzaju mikrooptymalizacji, które można wykonać podczas programowania w C, spójrz na makro kontener_of w jądrze Linuksa. Jasne, możesz użyć kontener_of w kodzie C ++, ale kto to robi? To znaczy, jest to całkowicie akceptowalne w większości programów w C, ale typowi programiści C ++ natychmiast zaproponują coś innego, na przykład połączoną listę, która alokuje węzły łącza jako osobne bloki. Nie chcemy tego, ponieważ każdy przydzielony blok pamięci ma negatywny wpływ na wydajność.
Być może jedyną rzeczą, która przyniosłaby nam korzyść w C ++, jest to, że C ++ pozwala na metaprogramowanie szablonu, co oznacza, że czasami można uniknąć wirtualnych wywołań funkcji, wciąż mając parametr funkcji, i pozwolić kompilatorowi wstawić funkcje. Metaprogramowanie szablonów jest jednak skomplikowane i udało nam się spełnić wszystkie wymagania w C, więc korzyści płynące z tej funkcji w C ++ nie są tak ważne.
W jednej z firm rzeczywiście mieliśmy niestandardowy język kompilacji, w którym zaimplementowano część funkcji. Zgadnij, który był językiem docelowym kompilatora? Montaż? Nie, musieliśmy obsługiwać zarówno architekturę 32-bitową, jak i 64-bitową. C ++? Na pewno żartujesz. Oczywiście było to C z obliczonym goto GCC . Tak więc niestandardowy język został skompilowany do C (lub w rzeczywistości wariant C gcc, który obsługiwał obliczone goto), a kompilator C wyprodukował zestaw.