Ze zprávičky to vypadá, že Linus je proti kontrole přetečení. Což není, Linus je pro to, aby kontrola přetečení byla na první pohled kontrola přetečení:
if (mtu < hlen + sizeof(struct frag_hdr) + 8)
goto fail_toobig;
mtu -= hlen + sizeof(struct frag_hdr);
Ne aby to volalo kouzelnou funkci, která kontroluje přetečení během odečítání hodnoty v ukazateli:
if (overflow_usub(mtu, hlen + sizeof(struct frag_hdr), &mtu) ||
mtu <= 7)
goto fail_toobig;
V tomto miste nema abstrakce co delat. Komu tim prospejete? Svemu egu ze to clovek tak hezky napsal? Ze s Ccka si javu nakonec udelate a napisete specialni framework na odmocniny? Naprosto zbytecne se zvysuje slozitost. Kod ma byt prehledny. Neni-li prehledny ani pro dva ci tri reviewery - prepsat. Stezuje si securitak ze tomu nerozumi? - prepsat.
Jako po jednom code review:
a) prosimte co je to za hovadinu?
b) libi? to je krasny vid.
a) Jo. Koukal jsem do toho 20minut nez jsem pochopil co to dela. A to nedela zadny kalkulace. Hlavne na prvni pohled netusim co to provede se skutecnym zarizenim a jak ovlivni dalsi testy
b) Ale je to hezky
a) Kde tu funkci jeste pouzivas?
b) Ale jenom tady primo v tom zdrojaku
a) Petre si normalni? Tohle vas uci na matfyzu? Nechces radsi delat neco uzitecnyho? Bez ke kryptoanalytikum a bez jim pomoci. Na tohle je "te tu skoda".
Ten puvodni navrh je lepsi v tom, ze ten vyraz tam je jen jednou. Duplicitni kod je vzdy objektivne slozitejsi. V tomto pripade je jasne riziko, ze nekdo upravi vypocet, ale neosetri podminku, ktera tim ztrati smysl, pripadne bude skodit. To nejsou akademicke kecy, ale zkusenost z praxe.
Pak tam muze byt jeste jina vec dobra - z puvodniho kodu je jasne zrejmy zamer autora, ale tam uz zalezi na tom, jak moc je pouzivana ta funkce atd. V tomto konkretnim pripade jsou Linusovy vyhrady nejspis na miste.
Hádky o GOTO probíhaly někdy koncem 70.let. Skončili ihned poté, co jeden student v diplomové práci prokázal, že programovací jazyk nemusí příkaz GOTO vůbec obsahovat, má-li následující tři příkazy:
1. - příkaz pro předčasné ukončení programu (stop, halt a pod..)
2. - příkaz pro předčasné ukončení procedury (return)
3. - příkaz pro předčasné ukončení cyklu (break).
Pokud má jazyk všechny tři příkazy, pak je použití GOTO známkou chybného programovacího stylu! Problém asi je, že se studenti na školách stále učí algoritmy pomocí zastaralých vývojových diagramů, které k použití GOTO svádí.
A pokud někomu vadí strukturovné IF, pak jen proto, že ho neumí.
Tak to je trošku fanatický přístup, protože sémantika goto v některých případech mnohem líp odpovídá řešenému algoritmu než podmíněné break (return je úplně něco jinýho). goto se netřeba bát, je to kupodivu v céčku čitelnější než mnohé smyčky (protože explicitní cíl přenosu řízení programu).
Ono obecne vyskakovani z for cyklu je prasarna. Od toho sou while cykly.
Nejvetsi problem goto je ze se v tom da velmi lehce ztratit. V basicu pak snadno vznikaly nechteny nekonecny smycky, ktery se blbe hledaly. To samy se samozrejme muze stat v C a dalsich a proto pouziti goto neni doporucovany. Od toho sou prave cykly a funkce, ktery delaj kod mnohem cistelnejsi.
NOTE − Use of goto statement is highly discouraged in any programming language because it makes difficult to trace the control flow of a program, making the program hard to understand and hard to modify. Any program that uses a goto can be rewritten to avoid them.
http://www.tutorialspoint.com/cprogramming/c_goto_statement.htm
Šak jo:
1. - příkaz pro předčasné ukončení programu (stop, halt a pod..)
V jádře nemůžeme použít jednoduše exit() a vypnout celý počítač kvůli nějaké chujovině - ten GOTO je tady používán právě pro předčasné ukončování programu. (Respektive dané části, tak aby zbytek kernelu zůstal běžet.)
Zrovna u error handlingu je to v C celkem běžný pattern/idiom, když je třeba uvolňovat prostředky v opačném pořadí, než v jakém byly alokovány a alokace můžou selhat. Lze tím nahradit hluboký strom if-ů, který by jinak nutně vzniknul. Defakto stejnou funkcionalitu realizuje stack unwind při vyhození výjimky v C++.
Ukázka třeba zde: http://stackoverflow.com/questions/788903/valid-use-of-goto-for-error-management-in-c
Mnoho. Zvláště dříve se operační systémy psaly v Pascalovsky laděných jazycích. A nebo já před 20 lety dělal zápočtovou práci ve Smalltalku, který nabootoval jako operační systém a kromě pár stránek asm tam byl pouze Smalltalk.
Mimochodem kernel Windows je programován v C a C++, a světě div se, to Céčko má výjimky! A to Céčko má __try a __finally. Windows kernel výjimky masivně používá dokonce všetně driverů hw, a neošetřená výjimka, která prohučí bez ošetření se zove modrá obrazovka smrti, jistě jste o ní slyšel.
On opravdu neexistuje jenom Linux, je také spousta jiných kernelů, mainstreamových a nemainstreamových. A také spousta programovacích jazyků. A těch jazyků, ve kterých se dá efektivně napsat kernel je celá spousta.
Zpět k tématu: Osobně si také jako mnozí zde v diskusi myslím, že původní varianta je lepší, než ta, kterou prosazuje Linus. A z praxe bych považoval Linusovu variantu za chyby přitahující, protože předpokládá, že každý kdo to změní, inteligentně změní obě části.
Miloslav Ponkrác
OS byly vždy v ASM + nověji C/C++ a ve Smalltalku OS nelze vytvořit, těch "pár stránek asm" je 5-10 MB nativního binárního kódu ke kterému nejsou ve Smalltalku, Pascalu a jiných vyšších jazycích potřebné jazykové prostředky, leda snad že by to používalo již existující binárky z BIOSu.
finally mají jenom u Microsoftu, normálně finally v C/C++ není.
1) Pascal je na tom ohledně goto a try-catch-finally úplně stejně jako céčko
2) které céčko prosím umí __try a __finally? Používáme aktivně C89 i C99, myslím, že ty normy docela znám, ale nějak to tam nevidím
3) zatím asi nejlepší handling v čistém C používá setjmp/longjmp, tj. vlastně "globální" GOTO
__try a __finally jsou rozšíření C od MS, podobně jako má GCC mnohá rozšíření (třeba __attribute__)
setjmp a longjmp jsou mnohem hůř čitelné než goto. Tedy přesněji oboje může být stejně nečitelné, ale setjmp/longjmp nelze používat dostatečně čitelně, jako je používáno goto v Linuxu. Navíc jsou o dost dražší.
try - finally se chová tak, že blok finally se zavolá ve chvíli, kdy se scope toho try - finally opouští, ať již tím, že se scope dokončil, byl zavolán return (break, …) nebo vyskočila výjimka. V jazyce bez výjimek tam pořád bude to zachytávání returnů, což je přesně to, na co se v Linuxu používá goto.
Jádro Singularity je v C++. Ale operační systémy napsané v managovaných jazycích existují, třeba JX (v Javě) a Squeak (ve Smalltalku). Pokud by se to rozšířilo i na ovladače (což je ten kód, na který Linus nadával), tak mnoho mikrokernelů podporuje ovladače v celkem libovolném jazyce, včetně zmiňované Singularity.
Ad Jádro Singularity je v C++ - není.
Counting lines of code, over 90% of the Singularity kernel is written in Sing# [dialekt C#]. ... Singularity includes small pockets of assembly language code in the same places it would be used in a kernel written in C or C++, for example, the thread context switch, interrupt vectors, etc. Approximately 6% of the Singularity kernel is written in C++, consisting primarily of the kernel debugger and low-level system initialization code.
http://research.microsoft.com/pubs/69431/osr2007_rethinkingsoftwarestack.pdf
Ne v normalnich jazycich je na to scope ;-)
http://dlang.org/statement.html#ScopeGuardStatement
Tys ten odkaz necet vid? Je to videt, protoze linus se tam rociluje i nad tim, ze to pouziva nestadardni rozsireni kompilatoru a je to teda zavisly na tom, jestli to umi nebo neumi. A navic "nikdo nevi" co to vlastne dela (respektive, se to v zadnym pripade neda zjistit ze zdrojaku jadra)
A jinak ad zpravicka, pokud dobre ctu, tak ten kod nazval spis hromadou sracek nez blbosti.
Ale on takhle vyšiluje už dlouho a všichni to ví. Alespoň je vidět, že se na kód opravdu někdo semtam koukne, než ho pustí dál. V podstatě je v tomto případě úplně jedno, jestli je lepší první, nebo druhá varianta. Je to o tom, že "u nás se to dělá takhle" a pokud to někdo nerespektuje a chce do toho vnášet nějaký svoje "já to chci dělat takhle", ať si jde za Poetteringem, ten ho jistě přivítá s otevřenou náručí a nechá ho nějakej kousek SW zbastlit.
Tak on se vyjadril i k tomu, ze ta konstrukce vyzaduje podporu v kompilatoru, ktera nemusi byt pritomna, tedy je zavisla na urcitem kompilatoru.
and this is the new "improved" code that uses fancy stuff that wants
magical built-in compiler support and has silly wrapper functions for
when it doesn't exist:
Give me *one* reason why it was written in that idiotic way
with two different conditionals, and a shiny new nonstandard function
that wants particular compiler support to generate even half-way sane
code, and even then generates worse code?
Je to dost pokrytecké zdůvodnění, když Linux stejně nejde zkompilovat s něčím jiným než GCC (dokonce ani s Clangem), protože už teď používá spoustu nestandardních rozšíření
Mě připadá že Linus ale i další programátoři maj nějakou poruchu, že nedokážou komunikovat normálně lidskou řečí. Nechápu tu módu úplně zbytečně zvýrazňovat slova s *jasným* významem. Přece když napíšu nikdy, tak to každý chápe jako nikdy a není to potřeba zvýrazňovat jak pro postižený lidi. Nebo jako *všem* znamená více lidem než všem a *nikdy* je míň často než nikdy?
Slusny clovek by to napsal takhle:
http://catcode.com/comments/2015/cf20151101.html
Kdybych už chtěl něco napsat přehledně a samodokumentačně, napsal bych to to já osobně nějak takto (za type si dosaďte nějaký konkrétní typ, neznám okolí kódu):
const type len_to_decrease = hlen + sizeof(struct frag_hdr);
const type reserve = 8;
if (mtu < len_to_decrease + reserve)
goto fail_toobig;
mtu -= len_to_decrease;
Tohle pochopí i idiot při prvním podívání, navíc je to optimalizované kompilátorem na nejvyšší míru.
Nicméně mně přijde ten původní kód i druhý kód čitelný. V tom druhém kódu bych se ale sakra ošíval, protože je nutné, aby dva výrazy byly při případné změně změněny najednou a stejně – a větší příspěvek k neudržovatelnosti a výrobě co nejzákeřnějších chyb snad ani neexstuje.
Ale já jsem jenom praktik, nejsem Linus.
Miloslav Ponkrác
Ten první kód je dost nepřehledný, není jasné, co se kam přiřazuje (všiml sis toho, že mtu je ve volání té funkce dvakrát?), a je hlavně špatně. C nedefinuje, kdy se identifikátor převede na hodnotu, takže změna mtu a zároveň jeho testování ve stejném ifu má nedefinovaný výsledek.
I will moderate the comment thread below this post with enthusiasm. I won’t hesitate to delete comments that are hurtful, abusive, or otherwise objectionable to me.
To je něco jako ta uražená feministická kravka, co nedávno páchala manifestační developerskou sebevraždu? :-D