Czy porzucenie STL w rozwoju C ++ jest praktyczne? [Zamknięte]

19

Wiem, że w niektórych obszarach (na przykład w branży gier) STL nie jest zalecane. Więc moje pytanie brzmi: czy naprawdę dobrą praktyką jest nie używanie STL w niektórych przypadkach? Jeśli tak, jakie są główne powody, dla których nie używamy STL nowoczesnego C ++?

Kapitanie JH
źródło
powiązane pytanie
fredoverflow
niektórzy z moich kolegów twierdzą, że iterator utrudnia debugowanie, ponieważ czasem interakcja nie jest łatwa, a dotyczy to także lambda. Jaka jest twoja odpowiedź?
CaptainJH
Jeśli chodzi o pomijanie rzeczy podczas debugowania, patrz na przykład: stackoverflow.com/questions/2062881/...
Martin Ba
To wydaje się dobre pytanie. Może ktoś mógłby dodać „Dlaczego projekt miałby nie używać STL?”
Matthew James Briggs

Odpowiedzi:

25
  • Mogę wymyślić tylko jeden ważny powód i to naprawdę rzadkie: ciężko w czasie rzeczywistym. Wiele elementów w standardowej bibliotece przydziela pamięć wewnętrznie i nie jest to wystarczająco deterministyczne w przypadku trudnych aplikacji w czasie rzeczywistym, więc należy ich unikać. Aplikacje te są zwykle dość proste, choć ich opracowanie zajmuje nieproporcjonalnie dużo czasu ze względu na bardzo rygorystyczny przegląd i testy.

  • Mogę wymyślić jeden nieprawidłowy, ale bardzo częsty powód: programiści, którzy nie rozumieją złożoności obliczeniowej, niewłaściwie używają STL, a następnie obwiniają bibliotekę.

    STL jest zwykle szybszy w czasie wykonywania niż rozwiązania w stylu C z wskaźnikami oddzwaniania lub rozwiązania oparte na polimorfizmie z metodami wirtualnymi ( patrz też główna uwaga Bjarne Stroustrupa ). Jednak gdy deweloper nie rozumie podanych specyfikacji złożoności i niewłaściwie wykorzystuje bibliotekę, tworząc coś w rodzaju wektora wektorów niektórych złożonych obiektów (w C ++ 11 to już nie jest problem!), Powoduje problem z wydajnością i broni się z „widzisz, wektory są raczej wolne”, może powodować wrażenie, że standardowa biblioteka jest wolna. A kiedy menedżerowie uzyskają taką percepcję, może żyć bardzo długo w organizacji.

  • Oczywiście nie możesz użyć niczego, czego nie obsługuje platforma, na którą celujesz. Jednak obecnie celujemy w cztery najpopularniejsze platformy mobilne (Android, iOS, Bada i stary WinCE) i na wszystkich z nich korzystamy ze standardowej biblioteki i niektórych części Boost .

    Znaczna część standardowej biblioteki była wcześniej nieobsługiwana przez Microsoft we wczesnej fazie WinCE (iostreams IIRC pojawił się tylko w Visual Studio 2005), ale zamiast tego można było używać STLport na długo wcześniej. I zwykle można to zrobić, aby skompilować z czymkolwiek. Dlatego też nazwałbym ten powód nieważnym.

    Poza tym od dłuższego czasu nie jest to „STL”, ale biblioteka standardowa ANSI C ++. Jest zdefiniowany przez ten sam standardowy dokument, który definiuje sam język. Wszystko, co go nie obsługuje, tak naprawdę nie zasługuje na miano C ++.

Jan Hudec
źródło
6
Pierwszy argument (w czasie rzeczywistym) nie jest specyficzny dla części STL biblioteki standardowej. sprintfczęsto również przydziela pamięć. Na platformach czasu rzeczywistego standardowe funkcje biblioteczne mają również ograniczenia deterministyczne. Utrudnia to implementacje C ++ w czasie rzeczywistym: trzeba by starannie opracować całą standardową bibliotekę C ++, która jest więcej pracy niż tylko zwykła biblioteka C.
MSalters
@MSalters: Jasne, wiele rzeczy nie może być używanych zgodnie z wymogami czasu rzeczywistego. Nawet niektóre funkcje językowe, takie jak wyjątki, nie mogą. Nadal C ++ jest świetnym językiem dla tych systemów, ponieważ może łączyć wydajność i precyzyjną kontrolę z silnymi zabezpieczeniami (RAII jest najważniejszą funkcją do tego).
Jan Hudec,
@JanHudec: Rzeczywiście dlatego części STL są wymagane tylko w przypadku „hostowanych” implementacji C ++.
MSalters
7

Używam STL i boost już od wielu lat. Gdybym chciał to porzucić i użyć niestandardowych narzędzi, motywacja byłaby następująca:

  1. Skrócenie czasu kompilacji (75%). Wystarczy dołączyć iostreams, aby dodać 1 milion linii kodu do modułu. Tak, prekompilowane nagłówki bardzo pomagają, ale wciąż spowalnia kompilację w dużych projektach. Na dłuższą metę marnuje dużo czasu na każdego, kto nad tym pracuje.
  2. Występ. (25%) STL jest napisany do pracy ogólnie, ale możesz zoptymalizować swoje struktury, aby działały dokładnie tak, jak chcesz. Na przykład możesz mieć struktury danych z milionami krótkich ciągów. Może być znacznie szybsze użycie niestandardowej klasy ciągów opartych na zasadzie boost :: small_vector (mały statyczny lokalny wektor danych, dynamiczny przydział tylko dla większych ciągów), takie zmiany mogą sprawić, że krytyczne sekcje kodu będą działały wielokrotnie szybciej.
Marwin
źródło
1
Jednokrotne logowanie jest już powszechne.
Deduplicator
dla potomności: SSO -> optymalizacja małych ciągów, tzn. większość implementacji std :: string utrzymuje małe ciągi na stosie i, w razie potrzeby, przełącza się na stos
niebieski
Czasy kompilacji są duże
użytkownik1754322
4

Jest jeden ważny ważny powód, aby nie używać standardowej biblioteki szablonów C ++: Jedna z twoich platform docelowych nie ma jej w pełni zgodnej implementacji (lub wcale jej nie ma) i wiesz, że nie otrzyma takiej w ciągu najbliższych lat.

Patrick
źródło
3
Aka „nie używaj go, gdy go nie masz”, co ma sens, naprawdę. :)
Xeo,
4
Biorąc pod uwagę, że biblioteka standardowa C ++ 03 została zaprojektowana do implementacji tylko w odniesieniu do biblioteki ANSI C89, czy istnieje platforma, na której nie można uzyskać przynajmniej STLPort ?
Jan Hudec
@ JanHudec Wierzę, że istnieją platformy bez STL, ponieważ nie mają wystarczającej ilości pamięci, aby poradzić sobie z tym wszystkim. Zwykle brakuje im także innych funkcji C ++ (np. Wyjątków).
Sulthan
2
@Sulthan: Dla mikrokontrolerów trochę rozumiem, ale zwykle należą one do kategorii „trudnych zadań w czasie rzeczywistym”. Jeśli chodzi o cokolwiek innego, są to głównie założenia wstępne, ponieważ STL jest zwykle tak samo wydajna zarówno pod względem pamięci, jak i wydajności, jak ręcznie wykonany kod. Dużo wbudowania może powodować większe pliki binarne, ale można tego nawet uniknąć poprzez staranne wdrożenie przy pewnym koszcie wydajności (tak samo jak w przypadku ręcznie wykonanego rozwiązania). Brakujące wyjątki to także uprzedzenie lub lenistwo, ponieważ trzeba zdefiniować i wdrożyć wyjątek ABI.
Jan Hudec
4

Nie wiem o złożoności (wydajności implementacji), ale używam pojemników i ciągów Qt intensywnie zamiast standardowych i działają one dobrze. Uważam również, że implementacja zestawów i list Qt jest łatwiejsza w użyciu.

Dlatego może być praktyczne porzucenie STL, jeśli możesz użyć innej biblioteki, która odpowiada twoim potrzebom.

Giorgio
źródło
2
odpowiedniki Qt zostały utworzone dawno temu, gdy nie było żadnych implementacji STL, które byłyby albo a) dostępne, albo b) jakieś dobro. To jedyny powód, dla którego wciąż są używane, nic przeciwko dzisiejszemu STL.
gbjbaanb
1
@Giorgio: problem dotyczy złożonych aplikacji, w których łączysz wiele bibliotek. Standardowo pojemniki STL tworzą lingua franca . Dokładniej, to ich konwencje. Najbardziej znanym przykładem jest Boost. Może działać na kontenerach STL. może również działać na kontenerach Qt, ale tylko dlatego, że Qt przestrzega konwencji STL - np.QList<T>::iterator
MSalters
3
Nie głosowałem za tym, ale widzę jeden powód: to nie odpowiada na pytanie. Powiedziałeś, że są rzeczy do użycia zamiast STL, w porządku, ale to nie jest powód, aby unikać STL. Poza tym każdy powód do unikania STL prawdopodobnie dotyczy Qt, MFC i innych takich bibliotek, a nawet więcej.
Jan Hudec,
3
@NoOne: Rozumiemy, że jesteś przyzwyczajony do wielbłądów, a nie do snakecase, ale to nie pogarsza tego ostatniego. I z trzech nazw funkcji, które krytykujesz, pierwsza jest doskonale opisowa dla każdego, kto ma cokolwiek wspólnego z ciągami c, a pozostałe dwa są dziedziczone z C, nie będą o nich dyskutować.
Deduplicator
1
@NoOne: Jak już powiedziałem, całkowicie rozumiem, że czujesz się bardziej komfortowo z CamelCase.
Deduplicator
3

Patrick wymienił powód, dla którego nie należy korzystać z całego STL, a mianowicie, że twoja platforma (platformy) nie ma takiego.

Podsumowując, myślę, że pytanie nie ma sensu. Przeważnie nie jest to decyzja typu wszystko albo nic, ale decyzja typu „wybierz i wybierz”. Możesz zdecydować się na użycie kontenerów i algorytmów, ale zdecyduj się użyć czegoś poza standardową biblioteką lib dla ciągów i we / wy.

Martin Ba
źródło
3

Nie jest to praktyczne, chyba że istnieje ku temu poważny powód. Niektóre z takich powodów, które mogę wymyślić, to tylko częściowa lub brak implementacji STL (lub jakiejkolwiek innej części standardowej biblioteki) lub ograniczenie zasobów (pamięć, szybkość procesora, pamięć, ...), które musisz obejść rozwijając własne narzędzia zgodne z tym, co musisz osiągnąć.

W branży gier większość (nawet do pewnego stopnia mniejszych) studiów ma swoje biblioteki wewnętrzne i implementacje wielu standardowych części bibliotecznych, które są wysoce dostosowane do platformy docelowej, aw niektórych przypadkach są ukierunkowane na inżynierię, a nawet samą grę. Mówiąc wprost, przy opracowywaniu gry na konsole sprzęt jest bardzo ograniczony przez dzisiejsze standardy. Istnieją tysiące linii ręcznie wykonanego montażu z jednego powodu. Bardzo ważne jest zminimalizowanie wszelkiego rodzaju śladów zasobów w kodzie, aby gra działała szybciej, co pozwala na więcej treści w świecie gry (lub na przykład w większym świecie), co, miejmy nadzieję, skutkuje lepszym produktem.

„Każda udana gra zaczyna się od wprowadzenia własnej implementacji połączonej listy”.

zxcdw
źródło
1
Wyobrażam sobie, że każda udana gra zaczyna się od napisania kodu przy użyciu standardowej biblioteki, a następnie optymalizacji kodu dopiero po szczegółowym sprofilowaniu gry. Optymalizacja przed profilowaniem danych stwierdzających, co wymaga optymalizacji, jest bezcelowa.
Cromulent
Prawdziwe. Ostatnia fraza była po prostu grą na „starożytnym” sposobie pisania gier na początku lat 90-tych, kiedy C ++ nie było tak szeroko rozpowszechnione, a montaż + C był właściwą drogą. Być może powinienem był to wyjaśnić. Ma to jednak zastosowanie do branży gier na konsolach, większość algorytmów i struktur danych jest odręcznie zapisywana domyślnie, ponieważ liczy się każdy bajt i cykl (tak, nawet kosztem utrzymania / przenośności / cokolwiek innego).
zxcdw,
2
Trzeba powiedzieć, że życie to bardziej stare doświadczenie. Nowoczesny optymalizator zwykle generuje lepszy zestaw z prostego, łatwego do utrzymania kodu, niż programista zrobi ręcznie, a ogólne szablony, takie jak STL lub Boost, często dołączają się do tak wydajnego kodu, jak specjalne ręczne pakowanie wszystkiego. Ale jest dużo kodu, który zaczął się w czasach, gdy tak nie było, i wielu ludzi, którzy uczyli się handlu w tych dniach i nadal działają w ten sposób, nawet jeśli to już nie ma sensu.
Jan Hudec,
2
@JanHudec Chodzi o to, że implementacje (i zachowania, w rzeczywistości) muszą być bardzo dostosowane do tego zadania. Po prostu nie możesz oszczędzić kilkudziesięciu bajtów tu i tam (rujnując lokalizację referencji), upuścić kilka gałęzi, aby sprawdzić poprawność danych wejściowych (błędne przewidywania gałęzi i pominięcia pamięci podręcznej instrukcji) i założyć, że kompilator wie, jak wektoryzować struktury danych optymalnie, aby skorzystać z SIMD (nie będzie, a przynajmniej musisz sprawdzić, czy to, co próbuje, jest prawidłowe). Oczywiście pisanie oprogramowania w czasie rzeczywistym na komputerze nie jest tak surowe, zawsze możesz wrzucić szybszy procesor. Nie w konsolach.
zxcdw,