Wątek przeniesiony 2023-03-02 11:00 z Off-Topic przez Riddle.

Instrukcja goto

1

Nie wiedziałbym jak bardzo beznadziejne są wyjątki, gdyby nie obecny projekt gry — w nim nie używam wyjątków w ogóle, a kody błędów zwracanych przez funkcje (czyli stara szkoła, jak w czystym WinAPI). Wszystko widzę, wszystko kontroluję, nie ma żadnych globalnych skoków i martwienia się o to czy funkcje których używam mogą rzucić wyjątkiem czy nie (a więc martwienia się o to gdzie wstawiać bloki try). Obsługa błędów to zwyczajne ify, a jeśli wiele instrukcji może spowodować błąd i trzeba w ten sam sposób na nie zareagować, to siup — goto to wspólnej części kodu obsługi tych błędów.

Zdumiewa mnie fakt, że bardzo wielu programistów nie potrafi zrozumieć tak prostego do zrozumienia kodu i jawnego przepływu sterowania, ale jednocześnie nie widzą problemu w używaniu wyjątków, defer, przeładowanych operatorów, lambd i innych konstrukcji, które zaciemniają ten przepływ.

0

Dzięki wyjątkom nie trzeba zaciemniać algorytmu obsługą błędów. Zamiast po wywołaniu dowolnej funkcji mogącej spowodować błąd umieszczać jego sprawdzenie i obsługę, można zapisać cały pożądany, optymistyczny scenariusz w sekcji try, a wszystkie błędy obsłużyć poniżej w catch.

1

@Manna5
W Rust rozwiązali to za pomocą znaku zapytania. Jest specjalny typ Result, który może zawierać albo wartość albo błąd. I żeby dostać się do wartości, trzeba coś zrobić. Jednym ze sposobów na dostanie się do wartości z Result jest znak zapytania właśnie, który działa w ten sposób, że próbuje się dostać do wartości i jeśli mu się to uda, to masz pożądany, optymistyczny scenariusz, jednak jeśli Result zawiera błąd, to program wychodzi z bieżącej funkcji i cała funkcja zwraca również błąd.
https://doc.rust-lang.org/std/result/
czyli podobnie jak przy wyjątkach, możesz mieć optymistyczny scenariusz oraz propagowanie błędu i łapanie go później, jednak robisz to explicite, przez wartość typu Result. I jest to bardziej elastyczne, sam decydujesz, kiedy i w jaki sposób złapiesz tę wartość i obsłużysz ewentualny błąd (mógłbyś też łapać przez pattern-matching. Albo na chama zrobić .unwrap(), ale to jak wiesz, że tam musi być wartość, bo jak nie ma, to nastąpi panika całego programu).

1

@Manna5: różnica jednak polega na tym, że w przypadku obsługi błędów w formie kodów zwracanych z funkcji, przepływ sterowania nie zmienia się. Kod błędu można przetworzyć (zareagować na błąd), ale też zignorować i wykonywać instrukcje dalej. Nic nam się tutaj nie wtrąca z automatu.

Natomiast w przypadku wyjątków, przepływ sterowania zmienia się zawsze — instrukcje bezpośrednio pod linijką rzucającą błąd zostaną zignorowane i nastąpi skok do najbliższego except, w tej samej funkcji lub po call stacku. Żeby zjeść wyjątek, trzeba go gdzieś przechwycić i albo obsłużyć, albo nie (pusty try except). No a wszyscy wiemy, jak się patrzy na pusty try except. ;)


Zawsze gdy tworzę kod wykorzystujący klasę TFileStream muszę takie durne puste try except stawiać, bo jakiś geniusz wpadł na pomysł, żeby w przypadku niemożności otwarcia pliku rzucić wyjątek. A co ma się dziać w obsłudze tego wyjątku? Nic, jeśli jest problem z otwarciem pliku to mam zwrócić False z funkcji i wyjść — rezultat na False ustawiam na samej górze funkcji, więc except pozostaje pusty.

var
  Stream: TFileStream;
begin
  Result := False;

  try
    Stream := TFileStream.Create('Foo.bin', fmOpenRead);
    try
      // czytam dane i ustalam rezultat (tu też może paść wyjatek, więc "finally" potrzebne)
    finally
      Stream.Free();
    end;
  except
    // nic do roboty, ale to musi być, aby wyjątek się nie wydostał
  end;
end;

Na szczęście SDL ma SDL_RWops, czyli obsługę dowolnych strumieni w formie prostych funkcji, które zwracają dane liczbowe i pointery. Np. nie udało się otworzyć pliku, to SDL_RWFromFile zwróci nil, który rozróżniam ifem i wychodzę, jeśli nie ma nic do roboty:

var
  Stream: PSDL_RWops;
begin
  Stream := SDL_RWFromFile('Foo.bin', 'rb');
  Result := Stream <> nil;
  
  if Result then
  begin
    // czytam dane i ustalam rezultat
    SDL_RWclose(Stream);
  end;
end;

Nie ma tu żadnej magii i automatów — proste testy ifami.

2
furious programming napisał(a):

Nie ma tu żadnej magii i automatów — proste testy ifami.

A co jeśli Twoja funkcja woła inną, a tamta woła kolejną, a tamta kolejną, i ta drabinka ciągnie się powiedzmy na 10 lub 20 funkcji. Jeśli z najniższej funkcji chcemy zwrócić informację o błędzie, i obsłużyć ją wysoko, z wyjątkami musimy wprowadzić zmiany tylko w dwóch funkcjach. Jak rozumiem, z Twoim podejściem musielibyśmy zmienić każdą z tych 20 funkcji, żeby zwracały ten wynik z błędem?

0

W typowych sytuacjach, jedyne co nas interesuje to to czy dana funkcja się wykonała czy wystąpił błąd — dwa scenariusze, stąd bool jako rezultat wystarczy. Natomiast dodatkowe informacje mogą być odkładane i korzysta się z nich wtedy kiedy potrzeba (np. w WinAPI za pomocą GetLastError lub w SDL za pomocą SDL_GetError). Te funkcje zwracają stringi z komunikatami, natomiast false lub liczbowy kod błędu (np. -1) jako rezultat oznacza, że dana funkcja się wyłożyła i można samodzielnie zdecydować co robić dalej — znów zwrócić false i wyjść z funkcji, albo obsłużyć błąd i ukryć problem (funkcja niżej w call stacku się o tym problemie nie dowie).

W moim kodzie (silnika, nad którym pracuję), nie ma znaczenia jak duży jest call stack i w której funkcji potrzeba obsłużyć błąd a w której nie. Jeśli któraś funkcja się wykłada, to zwraca False i każda funkcja niższego rzędu (niżej w call stacku) może na to zareagować lub błąd olać (o ile można olać błąd, to zależy od kontekstu). Jeśli w którymkolwiek miejscu będę potrzebował zareagować na błąd, to sobie dodam/rozwinę ifa i to wszystko. Jeśli bym używał wyjątków, to zamiast rozwijania ifa, dodałbym blok try except z obsługą błędu, a potem puścił wyjątek dalej (za pomocą raise), jeśli funkcje niżej w call stacku mają ten wyjątek również dostać.

Nie ma więc żadnego problemu ani w reagowaniu na błędy, ani w kontroli przepływu sterowania, ani też z przekazywaniem informacji o błędach nawet na sam dół call stacku. No i nie ma też problemu w rozbudowywaniu kodu, dodając obsługę błędów w tych funkcjach, które jej jeszcze nie mają. To jest standardowa kontrola błędów, znana i stosowana od kilkudziesięciu lat, łącznie z systemowym API (np. Win32 API). Wprowadzenie wyjątków pozwoliło olać ify, olać sprawdzanie czegokolwiek, bo w razie czego pojawi się wyjątek i on tak sobie będzie leciał po call stacku w dół, aż którać metoda go złapie i obsłuży. W skrócie — jebnie to jebnie, co mnie to obchodzi. Niestety takie olewactwo ma swoje wady, o których pisałem wcześniej.

Jeśli mam metodę, w której coś tak się wykonuje, ale nie ma w niej try except, to nie wiadomo czy ona może walnąć wyjątkiem czy nie, nie wiadomo też czy metody, które są w niej wywoływane mogą to zrobić czy nie. Tak więc nie wiadomo, czy przez tę metodę może wyjątek przelecieć czy nie może. W przypadku klasycznej obsługi błędów, wystarczy popatrzeć na instrukcje warunkowe — są testy danych/rezultatów funkcji to znaczy, że coś może pójść nie tak (tym bardziej, jeśli w środku kodu są returny lub exity, jeśli chodzi o Pascala).


Dam przykład z mojego silnika. W głównym bloku kodu jest wywoływana funkcja z menedżera kursorów, której zadaniem jest załadowanie kursorów z pliku binarnego (zwraca wartość logiczną). W niej następuje próba otwarcia pliku — jeśli coś pójdzie nie tak (np. nie ma pliku lub jest zablokowany do odczytu), to zwraca False i tyle. Jeśli plik jest, woła funkcję odczytującą dane ze strumienia pliku. W tej funkcji wołane są kolejne — odczyt sygnatury pliku oraz sanych kursorów. Sygnatury nie ma lub jest zła? Zwraca False i tyle roboty. Sygnatura jest prawidłowa, zaczyna czytać dane kursorów — ich liczbę oraz dane. Nieprawidłowa liczba kursorów? Zwraca False i przerywa ładowanie. Liczba jest prawidłowa, to wołane są kolejne funkcje — call stack puchnie. Odczyt danych pojedynczego kursora to kolejne funkcje — odczyt i test sygnatury, dane nagłówka, następnie dane poszczególnych typów kursorów (nazywam je ”kształtami"). Nagłowek nieprawidłowy, bo danych brakuje lub nie przechodzą walidacji? Zwracam False i przerywam ładowanie. Odczyt kształtów to kolejne funkcjie — nagłówek, liczba klatek, atlas, kolejne wywołania dokładane do call stacku. Coś pójdzie nie tak to funkcja zwraca False.

Call stack ładowania kursorów z pliku wygląda tak:

Game_CursorsReadFromFile()
  Game_CursorsReadFromStream()
    Game_CursorsReadFromStreamSignature()
    Game_CursorsReadFromStreamCursors()
      Game_CursorReadFromStream()
        Game_CursorReadFromStreamSignature()
        Game_CursorReadFromStreamHeader()
        Game_CursorReadFromStreamShapes()
          Game_CursorShapeReadFromStream()
            Game_CursorShapeReadFromStreamHeader()
            Game_CursorShapeReadFromStreamFrames()
            Game_CursorShapeReadFromStreamAtlas()
              Game_StreamReadTexture() // to mój wrapper maskujący bug w SDL-u

Zauważ, że nieważne ile jest wywoływanych funkcji w funkcjach, bo na każdym etapie jeśli coś pójdzie nie tak, to funkcja zwraca False (plus dodaje informacje do pliku logu, jeśli to istotny problem) i przerywa swoje działanie. Funkcja niżej w call stacku widzi, że dostała zwrotkę w formie False, więc sama też zwraca False i przerywa działanie. I tak dalej i tak dalej, informacja o błędzie przemieszcza się aż na sam spód call stacku.

W wielu z tych funkcji, po wykryciu jakiegokolwiek problemu z odczytem danych z pliku, następuje cleanup, opcjonalny skok za pomocą goto do kodu logującego problem w pliku logu oraz zwrócenie False. Ów zwrócony False zawsze jest sprawdzany i jeśli wykryty, następuje cała drabinka exitów z False'ami, aż do samego spodu call stacku.

Jeśli będę potrzebował dodać jakikolwiek kod reagowania na błędy, dodam go w konkretnej funkcji, bez żadnego problemu — ot podłączę się pod ifa lub go dodam. Jeśli będę potrzebował ukryć problem, to zamiast exit z False, obsłużę błąd i zwrócę True, a jeśli nie, to przekażę False dalej. Te False'y czy liczbowe kody błędów lecą po call stacku tak samo jak wyjątki, tyle że niczego przede mną nie ukrywają i nie obniżają wydajności kodu wynikowego.

Taki kod jest niezwykle czytelny, wszystko jest jawne i da się go łatwo rozwijać, mam pełną kontrolę nad błędami.

0

@Manna5

Ale obsługa błędów to nie jest jakaś tam "pierdoła" która zaśmieca kod, a jedna z ważniejszych rzeczy.

Naprawdę - brak dobrze zrobionej obsługi błędów to jest najczęstszy mankament codebasów jakie spotykam.

Ja to byłbym fanem gdyby WSZYSTKO czy prawie wszystko było typu Result<T> czy jakieś inne MayFail<T>, bo tak naprawdę każdy czy prawie każdy kod w jakiś sposób może się wywalić i warto jest to obsługiwać, albo chociaż mieć możliwość. Aktualnie masz jakąś funkcje typu void czy X i nie wiesz czy masz patrzeć na NULLe, łapać wyjątki czy co tam jeszcze

1

Obsługa błędów jest - rzecz jasna - ważna, jednak jeśli cała logika programu jest poprzekładana obsługą błędubłędów, zostaje ona zaciemniona. Wyjątki pozwalają przenieść obsługę błędów do odpowiedniej sekcji, oczyszczając główną część programu.

0

Ach ta dzisiejsza młodzież. :D

Manna5 napisał(a):

Wyjątki pozwalają przenieść obsługę błędów do odpowiedniej sekcji, oczyszczając główną część programu.

Mając obsługę błędów wewnątrz logiki, od razu wiadomo w którym miejscu błąd może zaistnieć i od razu widzimy jak jest obsługiwany. Przenoszenie obsługi błędów gdzieś indziej jest obfuskacją kodu.

0

Przecież to zależy od logiki biznesowej. Czasem przy wyjątku musimy przerwać całe flow i pokazać użytkownikowi, że coś nie działa bo coś tam. A czasem musimy zrobić retry albo coś

0

Rzadko się zdarza że możemy obsłużyć błąd na tym samym poziomie na którym występuje bo np mamy za mało informacji lub wymagałoby to mocnego związania ze sobą kodu.
Zwracania inta i mechanizmy typu GetLastError to jakieś średniowiecze, właśnie po to są wyjątki i niemal w każdym języku są wrappery na takie przykładowe winapi które to zamieni na wyjątki.
Dziś często unika się wyjątków na rzecz discriminated unions (nie wiem jak to się nazywa po polsku), są mechanizmy które nawet gwarantują że każda sytuacja będzie obsłużona (w przeciwieństwie do try catch all)

0
obscurity napisał(a):

Zwracania inta i mechanizmy typu GetLastError to jakieś średniowiecze, właśnie po to są wyjątki i niemal w każdym języku są wrappery na takie przykładowe winapi które to zamieni na wyjątki.

Gdyby mi język tak dobrą obsługę błędów z automatu zamieniał na wyjątki, to bym go posłał razem z laptopem za okno.

Dziś często unika się wyjątków na rzecz discriminated unions (nie wiem jak to się nazywa po polsku), są mechanizmy które nawet gwarantują że każda sytuacja będzie obsłużona (w przeciwieństwie do try catch all)

Których sytuacji nie dało się obsłużyć kodami błędów i/lub wyjątkami, że trzeba było wymyślać koło na nowo? Pytam z ciekawości, bo jakoś tego nie widzę.

1

@furious programming:

Kody błędów są problematyczne bo m.in nie zrobisz sobie na nich switcha ze sprawdzeniem pokrycia wszystkich przypadków.

1

Język tego nie robi, raczej biblioteki / wrappery do danego api, przykładowo .NET winforms/wpf/winui opakowuje mnóstwo wywołań winapi i rzuca normalne wyjątki zamiast kodów, podobnie robił taki VCL w delphi, ale to tylko przykład, są wrappery też do api webowych które podobnie zamieniają kody statusów http.

furious programming napisał(a):

Których sytuacji nie dało się obsłużyć kodami błędów i/lub wyjątkami, że trzeba było wymyślać koło na nowo? Pytam z ciekawości, bo jakoś tego nie widzę.

Wszystko się da, głównie chodzi o to żeby zmniejszyć liczbę popełnianych przez programistów popularnych błędów / niedociągnięć. W przypadku kodów błędów popularnym błędem kodera jest to że zapomina się lub odkłada "na później" obsługę błędów dopóki nie wystąpią one na produkcji, wracając do takiego winapi czy opengl bardzo często pomijane jest sprawdzanie wyniku większości metod bo praktycznie nigdy nie zwracają statusu błędu. W ten sposób częśto błędy wyłapują dopiero użytkownicy końcowi i bug trafia do backlogu.
Z wyjątkami jest taki problem że ludzie piszą "try catch" i łapią wszystkie wyjątki, często z pustym catchem i potem nie da się dotrzeć do istoty błędu. Pół biedy jak chociaż nieznane exceptiony są logowane. Drugim problemem jest to że niezłapany exception może wywalić nawet całą aplikację / domenę / wątek / serwer. Java próbowała zaradzić na to przez checked exceptions ale zrobiło się z tego jeszcze większe łajno. Nie znasz też wszystkich możliwych wyjątków które mogą polecieć w danym momencie dopóki nie przejrzysz kodów źródłowych, dokumentacja zbyt często kłamie w tych kwestiach.

1
WeiXiao napisał(a):

@furious programming:

Kody błędów są problematyczne bo m.in nie zrobisz sobie na nich switcha ze sprawdzeniem pokrycia wszystkich przypadków.

Co masz tutaj na myśli? Jeśli chodzi ci o wartość zwracana przez funkcję - to nie widzę zasadniczo dlaczego nie mozna tego zrealizować. Wręcz widziałem w życiu masę kodu w C gdzie właśnie tak sprawdzano wartości zwracane czy to bezpośrednio przez funkcję, czy pośrednio przez jakies zmienne globalne typu errno w POSIX.

1

@Satanistyczny Awatar:

Jeżeli funkcja w wersji biblioteki 0.1 zwraca kody (-2, -1, 0)

i napiszesz pod to kod, to gdy wyjdzie wersja biblioteki 0.2 która zwraca (-3, -2, -1, 0)

to kompilator nie wywali ci błędu kompilacji że nie masz pokrytego brancha dla -3 w swoim kodzie.

W dodatku pewnie będziesz musiał mieć jakiegoś defaulta ze względu na typ int.

0
obscurity napisał(a):

Język tego nie robi, raczej biblioteki / wrappery do danego api, przykładowo .NET winforms/wpf/winui opakowuje mnóstwo wywołań winapi i rzuca normalne wyjątki zamiast kodów, podobnie robił taki VCL w delphi, ale to tylko przykład, są wrappery też do api webowych które podobnie zamieniają kody statusów http.

VCL robi to tylko dla utrzymania spójności z resztą swojego kodu, który na wyjątkach bazuje. To nie jest robione dlatego, że wyjątki są lepsze, a dlatego, że dzięki nim w teorii jest łatwiej reagować na błędy. W teorii, bo jak praktyka wygląda to raczej wszyscy wiemy.

Wszystko się da, głównie chodzi o to żeby zmniejszyć liczbę popełnianych przez programistów popularnych błędów / niedociągnięć.

Tyle że to nie jest problem metodyki, a programistów i ich słabego skilla lub niedbalstwa.

3

@furious programming:

Tyle że to nie jest problem metodyki, a programistów i ich słabego skilla lub niedbalstwa.

Poleganie na ludziach się nie skaluje, patrz C++ i memory issues.

https://msrc.microsoft.com/blog/2019/07/a-proactive-approach-to-more-secure-code/

https://www.chromium.org/Home/chromium-security/memory-safety/

0
WeiXiao napisał(a):

Poleganie na ludziach się nie skaluje, patrz C++ i memory issues.

Każdy kod da się zepsuć z własnej winy, nie tylko ten niskopoziomowy. Cały obecny kod mojego silnika ma z 30kLoC, a łącznie nie poświęciłem więcej niż 10 minut na szukanie wycieków/problemów z pamięcią — pomimo tego, że wszędzie operuję wskaźnikami, a każdy blok pamięci na stercie alokuję za pomocą GetMem (i czasem AllocMem).

Jeśli ktoś nie potrafi myśleć podczas klepania kodu to żadne ficzery i żadej cukier mu nie pomoże. Niektórzy po prostu nie potrafią napisać dobrego kawałka kodu, jeśli im język, kompilator, biblioteki i ChatGPT w tym nie pomogą. :P

1

To jest problem metodyki bo dobra metodyka jest idiotoodporna i w tę stronę idzie programowanie w ostatnich czasach. To już nie lata 50te że programistami zostawali doktoranci z danej dziedziny, teraz przeciętny programista to osoba świeżo po szkole. Zresztą poleganie na tym że jest się nieomylnym daleko nie zaprowadzi; nawet najtęższe głowy popełniają błędy - chodzi o to żeby je jak najszybciej wyłapać lub w ogóle wykluczyć możliwość wystąpienia.

2

@furious programming

Każdy kod da się zepsuć z własnej winy

To nie jest dobry argument, serio.

Jeżeli weźmiesz 100 losowych programistów i w języku X robią oni średnio 7 bugów na 1000 LoC, a w języku Y tylko 3 bugi na 1000 LoC

no to? no to jest to ogromna oszczędność na developmencie, są to lepsze produkty dla użytkowników końcowych, zwiększone security, etc, etc.

Serio, jeżeli da się eliminować bugi czy klasy problemów samą technologią to wręcz trzeba to zrobić.

1
furious programming napisał(a):

Każdy kod da się zepsuć z własnej winy, nie tylko ten niskopoziomowy. Cały obecny kod mojego silnika ma z 30kLoC, a łącznie nie poświęciłem więcej niż 10 minut na szukanie wycieków/problemów z pamięcią — pomimo tego, że wszędzie operuję wskaźnikami, a każdy blok pamięci na stercie alokuję za pomocą GetMem (i czasem AllocMem).

Jak sam testujesz własny kod to siłą rzeczy nie natrafisz na scenariusz którego nie przemyślałeś. Poczekaj aż ktoś inny zacznie używać tego silnika a spokojnie - błędy się znajdą.
Poza tym 30kLoC nic nie mówi, może napisaleś skomplikowany kod, ale równie dobrze to może być hello world w javie

0
WeiXiao napisał(a):

To nie jest argument, serio.

Kiedy w ten sposób argumentował @obscurity kilka postów wyżej — kody błędów są złe, bo programista może coś przeoczyć; wyjątki też nie są dobre, bo pusty catch i można nie dotrzeć do istoty błędu. Kod źle napisany przez programistę nigdy nie jest problemem samej metodyki.

WeiXiao napisał(a):

Jeżeli weźmiesz 100 losowych programistów i w języku X robią oni średnio 7 bugów na 1000 LoC, a w języku Y tylko 3 bugi na 1000 LoC

no to? no to jest to ogromna oszczędność na developmencie, są to lepsze produkty dla użytkowników końcowych, zwiększone security, etc, etc.

Serio, jeżeli da się eliminować bugi czy klasy problemów samą technologią to wręcz trzeba to zrobić.

Nie ma znaczenia jak się zabierzesz za obsługę błędów — jeśli zrobisz to porządnie, z dbałością i zrozumieniem, to sama metoda nie ma większego znaczenia. @obscurity wspomniał też, że dawniej za programowanie brali się ludzie wykształceni, a dziś każdy może klepać kod. No i tu jest pies pogrzebany — skoro kod może pisać ktoś z niską wiedzą, to trzeba mu technologią pomagać, a reszta poradzi sobie, bo rozumie jak to wszystko działa.

obscurity napisał(a):

Jak sam testujesz własny kod to siłą rzeczy nie natrafisz na scenariusz którego nie przemyślałeś. Poczekaj aż ktoś inny zacznie używać tego silnika a spokojnie - błędy się znajdą.

Znajduję błędy na bieżąco — głównie dzięki temu, że nic ich przede mną nie ukrywa.

Poza tym 30kLoC nic nie mówi, może napisaleś skomplikowany kod, ale równie dobrze to może być hello world w javie

Nie da się krótszego hello worlda w Javie napisać? Trochę dużo klepania.

0

@furious programming:

no to jak obsłużyć dobrze?

dodałeś rok temu do swojego silnika gierek bibliotekę X w wersji biblioteki 0.1

używasz z niej 30 metod, i m.in jest jedna metoda która zwraca kody jako int (-2, -1, 0)

wczoraj wyszło że w wersji 0.1 jest poważna luka bezpieczeństwa i jak najszybciej powinieneś podnieść do 0.2, a w międzyczasie w wersji 0.1b doszedł kod -3 do w/w

podnosisz, i co teraz - przechodzisz po wszystkich metodach i sprawdzasz ich kod aby zobaczyć czy doszły nowe kody? przeglądasz change notes? a co jeżeli ich nie ma?

a co jeżeli takich bibliotek masz 10? wszystkie change notes czytasz?

czy może twój error handler robi "default" i tyle? :D

2

No to w ogóle nie ma co dyskutować bo robiąc wszystko porządnie, z dbałością i zrozumieniem nie ma 99.9% problemów programistycznych. Można wyrzucić do kosza wszystkie wzorce i praktyki i kodzić w pascalu na goto.
Łatwo jest mieć 100% wiedzy o własnym kodzie, spróbuj to utrzymać w zespole kilkuosobowym. A samemu nic większego nie dokonasz lub zajmie ci to wieki.

1
obscurity napisał(a):

No to w ogóle nie ma co dyskutować bo robiąc wszystko porządnie, z dbałością i zrozumieniem nie ma 99.9% problemów programistycznych. Można wyrzucić do kosza wszystkie wzorce i praktyki i kodzić w pascalu na goto.

A no można — polecam spróbować.

Łatwo jest mieć 100% wiedzy o własnym kodzie, spróbuj to utrzymać w zespole kilkuosobowym. A samemu nic większego nie dokonasz lub zajmie ci to wieki.

Dobrym przykładem jest SDL — 300 tycięcy linijek kodu, duży zespół, kod w czystym C. I ludzie nie jęczą, że wyjątków nie ma, że GC nie ma, że RAII nie ma, deleporka idzie błyskawicznie.

0

@furious programming:

@obscurity wspomniał też, że dawniej za programowanie brali się ludzie wykształceni, a dziś każdy może klepać kod. No i tu jest pies pogrzebany — skoro kod może pisać ktoś z niską wiedzą, to trzeba mu technologią pomagać, a reszta poradzi sobie, bo rozumie jak to wszystko działa.

To są brednie :P

Do kernela, do chromium i innych przeglądarek, androida, whatever zazwyczaj nie commitują ci ludzie którzy wczoraj skończyli bootcamp, a doświadczeni inżynierowie, zazwyczaj z formalną edukacją

i to oni popełniają te błędy

0

Wiem, że to generalizacja, ale zauważ, że w tym kierunku to idzie — obniża się próg wejścia, mniej na głowie, więcej po stronie technologii. Zresztą nie wiem kto commituje do kernela czy Chromium, bo nie mam statystyk. :P

0
WeiXiao napisał(a):

@Satanistyczny Awatar:

Jeżeli funkcja w wersji biblioteki 0.1 zwraca kody (-2, -1, 0)

i napiszesz pod to kod, to gdy wyjdzie wersja biblioteki 0.2 która zwraca (-3, -2, -1, 0)

to kompilator nie wywali ci błędu kompilacji że nie masz pokrytego brancha dla -3 w swoim kodzie.

W dodatku pewnie będziesz musiał mieć jakiegoś defaulta ze względu na typ int.

Istotnie, sa języki w których w czasie kompilacji się tego nie wychwyci. Aczkolwiek są rozwiązania poza kompilatorem, którymi można to sprawdzać. Są też teoretycznie możliwe do skonstruowania języki, które mogą zaimplementować mechanizmy umożliwiające weryfikację tego co jest zwracane podobną jak dla wyjatków, kwestia podejścia do konstrukcji typów. Nie widzę przeszkód zasadniczo by nie dało sie tego zrobić od strony algorytmicznej. Są języki pozbawione mechanizmu rzucania wyjatków które eksperymentują z błędami jako osbnymi typami.

0
WeiXiao napisał(a):

Jeżeli funkcja w wersji biblioteki 0.1 zwraca kody (-2, -1, 0)

i napiszesz pod to kod, to gdy wyjdzie wersja biblioteki 0.2 która zwraca (-3, -2, -1, 0)

to kompilator nie wywali ci błędu kompilacji że nie masz pokrytego brancha dla -3 w swoim kodzie.

A kompilator wywali błąd, że dany typ wyjatku nie jest pokryty w catch? Wyglądami to na szukanie problemu na siłę.

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