Zignorowanie nic nie znaczącej wartości czy assert?

0

Jest obiekt zawierający metody albo biblioteka funkcji. Otrzymują one argumenty o pewnych typach danych, które pozwalają na wartości spoza zakresu. Te wartości mogą mieć znaczenie: albo zachowanie ‘puste’, albo ‘niezdefiniowane’ takiej metody lub funkcji.
Pytanie jest o to, czy przekazywać takie wartości i ignorować je w implementacji czy raczej zapobiegać ich przekazywaniu warunkami w kodzie wołającym funkcję.
Przykładem takiej funkcji może być free z języka C. Ta funkcja może otrzymać wartość NULL, która oznacza zachowanie puste, i ją ignoruje (nic nie robiąc).
Ale przykładem mogą być też funkcje kopiowania danych z jednego miejsca pamięci w inne, dla których wartością pustą jest kopiowanie danych o wielkości 0 bajtów. Albo też funkcje dodawania elementów do tablicy lub ich usuwania, w których wartością pustą jest dodawanie lub usuwanie 0 elementów.
Więc ignorować wartość pustą w funkcji i ją dla niej wywoływać czy raczej zapobiegać takiemu wywołaniu assertem i testować przed wywołaniem?
Nie określam kryteriów, którymi się kieruję, ponieważ zakładam, że tutaj kompilator nie zoptymalizuje takiego kodu, ponieważ funkcja jest w osobnej bibliotece lub zewnętrznym obiekcie.
To pytanie jest powiązane z tym wątkiem, ale w nim kwestia jest wygenerowania skoku warunkowego a kodu obliczającego, czyli kompilator może to optymalizować.
W moim kodzie obecnie stosuję assert i zapobiegam wywołaniu pustych funkcji, ale nie mam jeszcze uzasadnienia do takiego postępowania. Może łatwiej jest wykryć kod wykonujący tylko puste wywołania funkcji, zapobiegać takim sytuacjom. A może jest to kwestia podejścia do poprawności programu, w którym typy danych są ściśle określone.

1

@overcq:

Mgliście piszesz, czy piszesz o mgle.

Podane przykłady nie łączą się z tym, o czym piszesz (mogę to źle czytać, ściana tekstu)
free(NULL) to dość szczególny i UDOKUMENTWANY przypadek., wybrano jedno z dwóch ... właściwie w C to mały wybór, bo nie da się rzucac wyjatkami.

memcpy(,,0) to nawet nie trzeba dokumentować. Po prostu ma wykonać, to co jest rozkazane (czyli zero)

Załączasz logo z C++ *), to powienieś wiedzieć, co robić w sytuacjach niewłaściwych: wyjątek.

Ale piszesz jak gość od C czy Pascala
asert ma (na produkcji) GŁEBOKIE wady: nie pozwala się wygrzebać z problemu, choć dobry programista by obsłużył kod powrotu (C) czy catchował wyjatek

Dziwnie piszesz, jakby jednocześnie w wielu paradygmatach (czy praktycznie w żadnym)

*) może mam głupie mniemanie, ale zakładam,. ze się załącza awatar "dziedzinowy" jak się ma pojęcie

2
ZrobieDobrze napisał(a):

@overcq:
asert ma (na produkcji) GŁEBOKIE wady: nie pozwala się wygrzebać z problemu, choć dobry programista by obsłużył kod powrotu (C) czy catchował wyjatek

Z drugiej strony ignorowanie takiego błędu może być dużo gorsze, bo najczęściej spowodowane jest to błędnym użyciem API lub zwykły błąd po stronie programisty. No i w przypadku C/C++ standardowo raczej się wyłącza assert na produkcji, bo asercje mają sprawdzać sytuacje, które na 100% są błędem. W przypadku interakcji użytkownik <-> biblioteka lepiej zwracać błąd w postaci wyjątku czy return value.

Nie określam kryteriów, którymi się kieruję, ponieważ zakładam, że tutaj kompilator nie zoptymalizuje takiego kodu, ponieważ funkcja jest w osobnej bibliotece lub zewnętrznym obiekcie.

Teraz raczej używa się LTO jak nie masz legacy projektu, więc kompilator zoptymalizuje taki kod.

Co do pytania: imo lepiej zwracać błąd. free(NULL) jeszcze może mieć sens, bo to bardzo popularna funkcja i takie ułatwienie może mieć sens. W innych przypadkach takie zachowanie może rodzić bugi trudne do znalezienia.

A może jest to kwestia podejścia do poprawności programu, w którym typy danych są ściśle określone.

W wielu językach z zaawansowanym systemem typów tak właśnie się robi. Jest to widocznie szczególne w językach funkcyjnych, gdzie stara się ograniczać dziedzinę/przeciwdziedzinę, bo w przeciwnym wypadku trzeba dodawać obsługę błędów, co powoduje, że kod jest bardziej złożony i brzydki. Jak język pozwala to lepiej dodać specyficzny błąd, przez co dostajemy dwie przeczenie na jednym ogniu: kod nie zwraca błędu a szczegółowe typy są znakomitą dokumentacją

0

@ZrobieDobrze: Ale czy przekazanie wartości oznaczającej puste zachowanie to jest sytuacja wyjątkowa, by trzeba było ją obsługiwać? Czy raczej sytuacja do zapobiegania, by nie marnować czasu obliczeń i zasobów?

@slsy: assert właśnie ma tę zaletę, że można go nie wkompilować w wynikowy kod po jego przetestowaniu, a obsługę wyjątków czy kodu powrotu musiałbym dopisać wszędzie, gdzie używałbym funkcji, podczas gdy te funkcje zwykle nie wymagają obsługi błędów.

Może lepiej byłoby dodać tylko warunkowo logowanie takiej sytuacji z wewnątrz funkcji...

1
overcq napisał(a):

@ZrobieDobrze: Ale czy przekazanie wartości oznaczającej puste zachowanie to jest sytuacja wyjątkowa, by trzeba było ją obsługiwać? Czy raczej sytuacja do zapobiegania, by nie marnować czasu obliczeń i zasobów?

O ile to naprawdę puste - i tak napisałem, dla mnie w 100% OK
NULL we free, zerowy bufor, OK.

(edit: w tytule wątku użyłeś dość szczęśliwego słowa: nic nie znacząca wartość (wystarczy doprecyzować co to jest "nic", polecam wojskową definicję) - w treści gorzej)

Ale ogólna "wartość spoza zakresu" ... oj, niedobrze.... po pierwsze dlatego, że nie mamy armat .... bo to nie jest puste
Uwierz, ze model działania "as usual" po świadomym pozamiataniu pod dywan, jest bardzo złym modelem
Wybaczę (tzw "zwykły" błąd zawodowy, choć czasem błąd za 1M$) pominiecie kodu powrotu, ale świadome zamiatanie pod dywan, by nigdy do tego nie powrócić ... brrr.

overcq napisał(a):

Może lepiej byłoby dodać tylko warunkowo logowanie takiej sytuacji z wewnątrz funkcji...

I tu zaczynają się schody.
programowanie proceduralnie nie dostarcza żadnych bezpiecznych / pewnych / stabilnych / automatycznych środków radzenia sobie z błędami.
Sam młody programistom przypominam "sprawdzaj kod powrotu ze scanf gdy sam tego czasem nie robię ;) (na szczęście niemal już w ogóle nie robię)

O (obiektowych) wyjątkach się mówi (ze strony wyznawców jedynej religii C), że są kosztowne, ale logowanie takiej sytuacji z wewnątrz funkcji... tańsze nie jest, przy czym wyjątek przecina węzeł gordyjski z natury, a wszystko inne się kisi (np logowanie musisz narzuć całemu projektowi (to strasznie psuje architekturę biblioteki) - i dokonywać jego okresowej analizy, co jest niemożliwe na produkcjach w setkach sztuka)

a obsługę wyjątków ... musiałbym dopisać wszędzie, gdzie używałbym funkcji, podczas gdy te funkcje zwykle nie wymagają obsługi błędów.

Masz złe wyobrażenie o wyjątkach. Właśnie po to są, aby "jak nie wiem co z nimi zrobić", to ich nie łapię.
jest takim lepszym asert, z którego można się wygrzebać, jak masz pomysł.

0

@ZrobieDobrze:

Dopuszczam miękkie przepuszczenie "wartości spoza zakresu"
a) w czymś bzinesowym, z obcego środowiska, jak import CSV mniej ważnych danych. pewnie bym raportował do logu. Log zostanie przeczytany lub nie, ale śmieci nie będa długo pod dywanem.
b) protokół komunikacyjny z urządzeniem. Umiem obłsużyć ekstrakody (kody ramki) 1,2,3 i 7
Gdy z drugiej strony jest nowsza wersja, ignoruję nieznane mi EKSTRAKODY (ale mówię tylko o jednym polu z ramki - dla znanych mi ramek wymagam specyfikacji).
To jest mocno różniący się przypadek od twojego pyatnia

1 użytkowników online, w tym zalogowanych: 0, gości: 1