C++ Core Guidelines #3: [P: Philosophy] Filozofia (część 2).

P.4 W idealnej sytuacji, program powinien zapewniać statyczne bezpieczeństwo typów (wiem wiem  – średnio przyjemne tłumaczenie 🙂 ).

Jest to oczywiście teoretyczna sytuacja idealna – już w czasie kompilacji wszystkie typy są znane i niezmienne, a błędy związane z typowaniem nie istnieją.

W realnym świecie mamy jednak co najmniej kilka obszarów, które generują problemy, m. in. unie, rzutowania, błędy związane z zakresami, „gubienie” typów i rozmiarów tablic oraz tzw. konwersje zawężające.

Nie są to, jak widać, błahostki. Obszary te generują naprawdę poważne i paskudne błędy, w tym naruszenia bezpieczeństwa i częste awarie aplikacji.

Jak pozbyć się problemów ?

Sposobów jest kilka, ale najlepszym jest ograniczenie lub kompletna rezygnacja z używania problematycznych konstrukcji. Możemy je zastąpić w następujący sposób:

  • unie – użyj szablonu std::variant z C++17
  • rzutowania – ogranicz ich użycie do niezbędnego minimum (mogą w tym pomóc szablony)
  • błędy związane z zakresami – użyj szablonu gsl::span z GSL (Guidelines Support Library)
  • gubienie typów i rozmiarów tablic – użyj szablonu gsl::span z GSL (Guidelines Support Library)
  • konwersje zawężające – ogranicz ich użycie do minimum, korzystaj z operatora rzutowania narrow_cast (GSL)

P.5 Preferuj kontrolę na etapie kompilacji, zamiast kontroli na etapie wykonania.

Dlaczego ? Z powodu większej czytelności i większej wydajności kodu. Jeśli błąd jest wyłapany na etapie kompilacji, nie trzeba dodawać kodu obsługującego błąd w trakcie wykonania.

Przykład 1.

Kolejny przykład kodu, który teoretycznie jest poprawny, jednak można go drastycznie uprościć niewielkim wysiłkiem. Może wyglądać tak:

Kod jest krótki i nadal doskonale zrozumiały. Statyczna asercja zapewnia sprawdzenie rozmiaru liczby całkowitej w czasie kompilacji, przez co cały kod obsługujący niepoprawną sytuację w czasie wykonania stał się zbędny.

Przykład 2.

lepiej zrobić to tak:

Wykorzystując szablon zakresu gsl::span, pozbywamy się ewentualnych błedów, związanych z zakresami.

Konkluzja: Jeśli można coś sprawdzić już na etapie kompilacji – zrób to. Unikniesz wielu problemów i uprościsz swój kod.

P.6 Co nie może być sprawdzone podczas kompilacji, powinno być możliwe do wykrycia podczas wykonania.

W idealnej sytuacji wyłapujemy wszystkie błędy na etapie kompilacji lub wykonania. Niemożliwe jest wyłapanie wszystkich błędów podczas kompilacji. Często nie ma też sensu wyłapywanie absolutnie wszystkich błędów na etapie wykonania – nie jest to ani szybkie, ani tanie.

Powinniśmy jednak pisać programy, które generalnie da się sprawdzić na obecność błędów, pod warunkiem użycia odpowiednich zasobów.

Kilka ciekawych przykładów.

Powyższy kod bardzo skutecznie eliminuje kluczową informację o liczbie elementów, co krytycznie wpływa na możliwość statycznej analizy tego kodu. Dynamiczna kontrola może być bardzo trudna w sytuacji, kiedy funkcja f jest częścią ABI (Application Binary Interface). W taki sposób można doprowadzić do sytuacji, w której detekcja błędów jest bardzo trudna z powodu decyzji projektowych.

Oczywiście można dodać informację o liczbie elementów:

Generalnie jawne przekazywanie liczby elementów do funkcji jest dużo lepszym i częściej spotykanym rozwiązaniem, niż poleganie na domysłach bądź dziwnych sposobach liczenia liczby elementów.

Napotykamy jednak kolejne problemy. Prosta pomyłka podczas pisania może wprowadzić kolejny uciążliwy błąd. Na dodatek związek pomiędzy oboma argumentami jest bardziej domyślny, niż jawnie wskazany. Niejasne jest też to, czy funkcja f powinna po wszystkim zwolnić pamięć, na którą wskazuje p.

Możemy też zmienić nasz kod w taki sposób:

Komentarz mówi wszystko – sytuacja nadal nas nie zadowala. Musimy zatem przekazać wskaźnik i rozmiar razem, zintegrowane w jednym obiekcie:

Takie rozwiązanie przenosi liczbę elementów jako integralną, wewnętrzną część obiektu. Dzięki temu błędy są bardzo mało prawdopodobne, a kontrola w czasie wykonania zawsze jest możliwa do przeprowadzenia.

0 Comments

Leave a Reply

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">