Na stronie comp.lang.c ++. Moderowana jest dyskusja na temat tego, czy asercje, które w C ++ istnieją domyślnie tylko w kompilacjach do debugowania, powinny być utrzymywane w kodzie produkcyjnym, czy nie.
Oczywiście każdy projekt jest wyjątkowy, więc moje pytanie nie dotyczy tego, czy należy zachować asercje, ale w jakich przypadkach jest to zalecane / nie jest to dobry pomysł.
Przez twierdzenie mam na myśli:
- Sprawdzenie w czasie wykonywania, które testuje warunek, który, jeśli jest fałszywy, ujawnia błąd w oprogramowaniu.
- Mechanizm zatrzymywania programu (może po naprawdę minimalnym czyszczeniu).
Niekoniecznie mówię o C lub C ++.
Osobiście uważam, że jeśli jesteś programistą, ale nie jesteś właścicielem danych (co ma miejsce w przypadku większości komercyjnych aplikacji komputerowych), powinieneś je włączyć, ponieważ błędne stwierdzenie pokazuje błąd i nie powinieneś iść z błędem, z ryzykiem uszkodzenia danych użytkownika. Zmusza to do zdecydowanego przetestowania przed wysyłką i sprawia, że błędy są bardziej widoczne, a tym samym łatwiejsze do wykrycia i naprawienia.
Jaka jest Twoja opinia / doświadczenie?
Twoje zdrowie,
Carl
Zobacz powiązane pytanie tutaj
Odpowiedzi i aktualizacje
Hej Graham,
Stwierdzenie jest błędem, czystym i prostym i dlatego powinno być traktowane jak jedno. Ponieważ błąd powinien być obsługiwany w trybie wydania, tak naprawdę nie potrzebujesz asercji.
Dlatego wolę słowo „błąd”, gdy mówię o asercjach. To sprawia, że wszystko jest znacznie jaśniejsze. Dla mnie słowo „błąd” jest zbyt niejasne. Brakujący plik jest błędem, a nie błędem i program powinien sobie z tym poradzić. Próba wyłuskania pustego wskaźnika jest błędem, a program powinien przyznać, że coś pachnie jak zepsuty ser.
Dlatego należy przetestować wskaźnik z asercją, ale obecność pliku z normalnym kodem obsługi błędów.
Nieco nie na temat, ale ważny punkt w dyskusji.
Jako ostrzeżenie, jeśli twoje asercje włamują się do debugera, gdy zawodzą, dlaczego nie. Ale jest wiele powodów, dla których plik nie może istnieć, a które są całkowicie poza kontrolą twojego kodu: prawa do odczytu / zapisu, pełny dysk, odłączone urządzenie USB itp. Ponieważ nie masz nad nim kontroli, uważam, że nie jest właściwym sposobem radzenia sobie z tym.
Carl
Tomasz,
Tak, mam Code Complete i muszę powiedzieć, że zdecydowanie nie zgadzam się z tą konkretną radą.
Powiedzmy, że niestandardowy alokator pamięci spieprzy i zeruje fragment pamięci, który jest nadal używany przez inny obiekt. Zdarza mi się wyzerować wskaźnik, który regularnie odwołuje się do tego obiektu, a jednym z niezmienników jest to, że ten wskaźnik nigdy nie jest zerowy i masz kilka twierdzeń, aby upewnić się, że tak pozostanie. Co zrobisz, jeśli wskaźnik nagle stanie się pusty. Po prostu jeśli () go otaczasz, mając nadzieję, że to zadziała?
Pamiętaj, że mówimy tutaj o kodzie produktu, więc nie ma potrzeby włamywania się do debuggera i sprawdzania stanu lokalnego. To jest prawdziwy błąd na komputerze użytkownika.
Carl
Odpowiedzi:
Asercje to komentarze, które nie stają się nieaktualne. Dokumentują, które stany teoretyczne są zamierzone, a które nie powinny wystąpić. Jeśli kod zostanie zmieniony, tak aby wskazać dozwoloną zmianę, programista zostanie wkrótce poinformowany i musi zaktualizować potwierdzenie.
źródło
Pozwólcie, że zacytuję Code Complete Steve'a McConnella. Sekcja dotycząca twierdzeń to 8.2.
Jednak w dalszej części tej samej sekcji podano następujące porady:
Myślę, że dopóki wydajność nie jest problemem, zostaw potwierdzenie, ale zamiast wyświetlać komunikat, zapisz go w pliku dziennika. Myślę, że ta rada jest również w Code Complete, ale teraz jej nie znajduję.
źródło
Pozostaw asercje włączone w kodzie produkcyjnym, chyba że zmierzyłeś, że program działa znacznie szybciej, gdy są wyłączone.
http://c2.com/cgi/wiki?ShipWithAssertionsOn
źródło
assert ref != null;
jest inny niżif (ref == null) throw new IllegalArgumentException();
Nie powinieneś używać pierwszego dla warunków wstępnych, które mogą być fałszywe. Musisz używaćassert
do rzeczy, które nie mogą być fałszywe. Przykład,int i = -1 * someNumber; i = i * i;
a później, aby przypomnieć ludziom, żei
jest to pozytywne,assert i > 0;
Jeśli nawet myślisz o pozostawieniu asercji w produkcji, prawdopodobnie myślisz o nich źle. Cały sens twierdzeń polega na tym, że możesz je wyłączyć w produkcji, ponieważ nie są one częścią twojego rozwiązania. Są narzędziem programistycznym, służącym do weryfikacji poprawności założeń. Ale zanim zaczniesz produkcję, powinieneś już mieć zaufanie do swoich założeń.
To powiedziawszy, jest jeden przypadek, w którym włączę asercje w środowisku produkcyjnym: jeśli napotkamy odtwarzalny błąd w środowisku produkcyjnym, którego odtworzenie w środowisku testowym jest trudne, pomocne może być odtworzenie błędu z włączonymi asercjami w produkcji, aby sprawdzić, czy dostarczają przydatnych informacji.
Bardziej interesujące pytanie brzmi: kiedy w fazie testowania wyłączasz asercje?
źródło
Asercje powinny nigdy nie pozostać w kodzie produkcyjnym. Jeśli konkretne stwierdzenie wydaje się być przydatne w kodzie produkcyjnym, to nie powinno to być twierdzenie; powinno być sprawdzenie błędu czasu pracy, czyli coś zakodowane tak:
if( condition != expected ) throw exception
.Termin „asercja” zaczął oznaczać „kontrolę tylko w czasie rozwoju, która będzie nie zostanie przeprowadzona w terenie”.
Jeśli zaczniesz myśleć, że asercje mogą dotrzeć do pola, to nieuchronnie zaczniesz też tworzyć inne niebezpieczne myśli, na przykład zastanawiać się, czy dane twierdzenie jest naprawdę warte wyrażenia. Nie ma twierdzenia, które nie jest warte wyrażenia. Nigdy nie powinieneś zadawać sobie pytania „czy mam to potwierdzić, czy nie?” Powinieneś tylko zadawać sobie pytanie: „Czy jest coś, o czym zapomniałem potwierdzić?”
źródło
O ile profilowanie nie wykaże, że asercje powodują problemy z wydajnością, mówię, że powinny one również pozostać w wersji produkcyjnej.
Myślę jednak, że wymaga to również, abyś radził sobie z niepowodzeniami asercji z pewnym wdziękiem. Na przykład powinny one skutkować ogólnym typem okna dialogowego z opcją (automatycznego) zgłaszania problemu programistom, a nie tylko zamykaniem lub zawieszaniem programu. Powinieneś także uważać, aby nie używać asercji do warunków, na które faktycznie zezwalasz, ale prawdopodobnie nie lubisz lub nie uważasz za niepożądane. Warunki te powinny zostać uwzględnione w innych częściach kodu.
źródło
W moim C ++ definiuję REQUIRE (x), który jest podobny do assert (x), z tą różnicą, że zgłasza wyjątek, jeśli asercja nie powiedzie się w kompilacji wydania.
Ponieważ nieudane potwierdzenie wskazuje na błąd, powinno być traktowane poważnie nawet w kompilacji wydania. Kiedy wydajność mojego kodu ma znaczenie, często używam REQUIRE () dla kodu wyższego poziomu i assert () dla kodu niższego poziomu, który musi działać szybko. Używam również REQUIRE zamiast assert, jeśli stan błędu może być spowodowany danymi przesłanymi z kodu napisanego przez osobę trzecią lub uszkodzeniem pliku (optymalnie zaprojektowałbym kod specjalnie tak, aby zachowywał się dobrze w przypadku uszkodzenia pliku, ale my nie zawsze mam na to czas).
Mówią, że nie powinieneś pokazywać tych wiadomości użytkownikom końcowym, ponieważ ich nie zrozumieją. Więc? Użytkownicy końcowi mogą wysłać Ci wiadomość e-mail ze zrzutem ekranu lub tekstem komunikatu o błędzie, co pomoże w debugowaniu. Jeśli użytkownik powie po prostu „awaria”, masz mniej możliwości, aby to naprawić. Lepiej byłoby wysyłać komunikaty o niepowodzeniu do siebie automatycznie przez Internet, ale działa to tylko wtedy, gdy użytkownik ma dostęp do Internetu i możesz uzyskać jego zgodę.
źródło
Jeśli chcesz je zachować, zastąp je obsługą błędów. Nie ma nic gorszego niż program po prostu znikający. Nie widzę nic złego w traktowaniu niektórych błędów jako poważnych błędów, ale powinny być skierowane do sekcji programu, która jest przygotowana do radzenia sobie z nimi poprzez zbieranie danych, rejestrowanie ich i informowanie użytkownika, że Twoja aplikacja ma niepożądany stan i wychodzi.
źródło
Pod warunkiem, że są obsługiwane tak jak każdy inny błąd, nie widzę z tym problemu. Należy jednak pamiętać, że nieudane asercje w C, podobnie jak w innych językach, po prostu zakończą działanie programu, a to zwykle nie wystarcza w przypadku systemów produkcyjnych.
Istnieją pewne wyjątki - na przykład PHP pozwala na utworzenie niestandardowego modułu obsługi błędów asercji, dzięki czemu można wyświetlać niestandardowe błędy, wykonywać szczegółowe rejestrowanie itp. Zamiast po prostu wychodzić.
źródło
Nasze oprogramowanie serwera bazy danych zawiera zarówno potwierdzenia produkcyjne, jak i debugowania. Asercje debugowania są po prostu tym - są usuwane w kodzie produkcyjnym. Twierdzenia produkcyjne mają miejsce tylko wtedy, gdy (a) istnieje jakiś warunek, który nigdy nie powinien istnieć oraz (b) nie jest możliwe niezawodne wyjście z tego stanu. Twierdzenie produkcyjne wskazuje, że napotkano błąd w oprogramowaniu lub nastąpiło uszkodzenie danych.
Ponieważ jest to system baz danych, a my przechowujemy dane potencjalnie krytyczne dla przedsiębiorstwa, robimy wszystko, co w naszej mocy, aby uniknąć uszkodzenia danych. Jeśli istnieje warunek, to może spowodować, że będziemy przechowywać niepoprawne dane, natychmiast potwierdzamy, wycofujemy wszystkie transakcje i zatrzymujemy serwer.
To powiedziawszy, staramy się również unikać asercji produkcyjnych w procedurach krytycznych dla wydajności.
źródło
Potwierdzenia traktuję jako testy jednostkowe in-line. Przydatne do szybkiego testowania podczas opracowywania, ale ostatecznie te twierdzenia powinny zostać refaktoryzowane, aby można je było przetestować zewnętrznie w testach jednostkowych.
źródło
Uważam, że najlepiej radzić sobie ze wszystkimi błędami w zakresie i używać potwierdzeń dla założeń, które twierdzimy, że SĄ prawdziwe.
tzn. jeśli Twój program otwiera / odczytuje / zamyka plik, to brak możliwości otwarcia pliku jest objęty zakresem - jest to realna możliwość, której zignorowanie byłoby, innymi słowy, niedbałe. Powinien więc mieć powiązany kod sprawdzający błędy.
Załóżmy jednak, że funkcja fopen () jest udokumentowana jako zawsze zwracająca prawidłowy, otwarty uchwyt pliku. Otwierasz plik i przekazujesz go do funkcji readfile ().
Ta funkcja readfile, w tym kontekście i prawdopodobnie zgodnie ze specyfikacją projektu, może w dużym stopniu zakładać, że uzyska prawidłowy plik ptr. Tak więc marnotrawstwo byłoby dodawanie kodu obsługi błędów dla przypadku negatywnego w tak prostym programie. Powinien jednak przynajmniej udokumentować założenie, w jakiś sposób - upewnić się - że tak jest, zanim będzie kontynuował jego realizację. Nie powinien FAKTYCZNIE zakładać, że będzie zawsze poprawny, w przypadku, gdy zostanie wywołany nieprawidłowo lub na przykład zostanie skopiowany / wklejony do innego programu.
Tak więc readfile () {assert (fptr! = NULL); ..} jest odpowiednie w tym przypadku, podczas gdy pełna obsługa błędów nie jest (ignorowanie faktu, że faktyczne odczytanie pliku i tak wymagałoby systemu obsługi błędów).
I tak, te zapewnienia powinny pozostać w kodzie produkcyjnym, chyba że jest to absolutnie konieczne, aby je wyłączyć. Nawet wtedy powinieneś prawdopodobnie wyłączać je tylko w sekcjach krytycznych dla wydajności.
źródło
Załóżmy, że w produkcji znajduje się fragment kodu, który trafia w potwierdzenie, które normalnie zostałoby uruchomione. Asercja znalazła błąd! Tyle że tak nie jest, ponieważ asercja jest wyłączona.
Więc co się teraz dzieje? Albo program (1) zawiesi się w sposób nieinformacyjny w miejscu dalej oddalonym od źródła problemu, albo (2) będzie działał wesoło do końca, prawdopodobnie dając zły wynik.
Żaden scenariusz nie jest zachęcający. Pozostaw asercje aktywne nawet w produkcji.
źródło
Rzadko używam asercji do czegokolwiek innego niż sprawdzanie typu czasu kompilacji. Użyłbym wyjątku zamiast potwierdzenia tylko dlatego, że większość języków jest zbudowana tak, aby je obsługiwać.
Podaję przykład
przeciwko
Jak aplikacja poradzi sobie z asercją? Wolę starą
try catch
metodę radzenia sobie z krytycznymi błędami.źródło
W większości przypadków, gdy używam asercji w java (słowo kluczowe assert), automatycznie dodam po. W zależności od przypadku może to być komunikat logowania, wyjątek ... lub nic.
Według mnie wszystkie twoje twierdzenia są krytyczne w wydaniu deweloperskim, a nie w produkcji. Część z nich trzeba zachować, inne wyrzucić.
źródło
ASSERTIONS nie są błędami i nie powinny być traktowane jako błędy. Gdy zostanie wyrzucone potwierdzenie, oznacza to, że jest błąd w kodzie lub alternatywnie w kodzie wywołującym twój kod.
Istnieje kilka punktów, aby uniknąć włączania asercji w kodzie produkcyjnym: 1. Nie chcesz, aby użytkownik końcowy widział komunikat typu „ASSERTION failed MyPrivateClass.cpp wiersz 147. Użytkownik końcowy NIE jest inżynierem ds. Kontroli jakości. 2. ASSERTION może wpływać na wydajność
Jest jednak jeden mocny powód, aby zostawić asercje: ASSERTION może wpłynąć na wydajność i synchronizację i niestety czasami ma to znaczenie (szczególnie w systemach wbudowanych).
Zwykle głosuję za pozostawieniem asercji włączonej w kodzie produkcyjnym, ale upewniam się, że wydruki tych stwierdzeń nie są ujawniane użytkownikowi końcowemu.
~ Yitzik
źródło
Stwierdzenie jest błędem, czystym i prostym i dlatego powinno być traktowane jak jedno.
Ponieważ błąd powinien być obsługiwany w trybie wydania, tak naprawdę nie potrzebujesz asercji.
Główną korzyścią, jaką widzę w przypadku asercji, jest warunkowa przerwa - są one znacznie łatwiejsze do skonfigurowania niż drążenie okien VC w celu skonfigurowania czegoś, co zajmuje 1 wiersz kodu.
źródło