Dnešní článek by měl být v tomto seriálku poslední, který se bude věnovat teorii. Nebo minimálně bych si chtěl s teorií dát chvíli pauzu. Přesto si tento díl neodpustím, protože z diskuse pod předchozími díly vyplynulo, že mnoho čtenářů napadlo moje pozice prostě proto, že jsem vycházel z nevyslovených předpokladů a to je moje chyba, kterou uznávám.
V zásadě platí, že napsat správně fungující program lze v kterémkoli jazyce, počínaje Assemblerem a konče jazyky s vysokou úrovní abstrakce. V praxi jsou ovšem v programech mnohé chyby a pokud se tomu chceme vyvarovat, musíme mít možnost ty chyby nějakým způsobem odhalit. Vzhledem k tomu, že vyšší jazyky nabízejí možnost rozdělit program do jednotlivých stavebních bloků, lze i rozhodování o tom, zda program funguje správně, dělat postupně a k výsledku dojít díky analýze těchto stavebních bloků a vztahů mezi nimi.
Abychom ale mohli zkoumat tyto bloky izolovaně, musíme zajistit, že nejsou na sobě vzájemně závislé a pokud ano, pak jenom v jednom směru. To znamená, že procedura (v C++ říkejme raději funkce), která volá jinou funkci, se musí spolehnout na to, že volaná funkce pracuje správně, ale nemusí se starat o svoje okolí a o to, co dělá funkce nadřazená a co dělají všechny ostatní funkce, které jsou volány v nějakém jiném místě programu.
Čistě funkcionální jazyky (typu Haskell nebo Clean) si zakládají na tom, že dodržují referenční integritu, což znamená, že každá funkce pro daný vstup vrátí vždy tu samou hodnotu na výstupu a to bez ohledu na to, v jakém kontextu je funkce volána a co se děje kolem. V praxi to znamená, že výpočet návratové hodnoty funkce bere v potaz výhradně parametry této funkce a nepoužívá žádné hodnoty, které může vyčíst jinde (globální proměnné, standardní vstup, soubor apod.). V jazycích typu C, C++ ale i v jiných imperativních jazycích toto obecně zaručit nelze, ale je možné ověřit, jestli daná funkce referenční integritu dodržuje.
Další koncept, který pomáhá v rozhodování o tom, zda program pracuje správně, je možnost redukce popisu implementace funkce na vytvoření návratové hodnoty. Pokud fukce nedělá nic jiného, než to, že spočítá výstupní hodnotu a vrátí ji, jedná se o funkci bez vedlejších efektů.
Příkladem funkce zachovávající princip referenční integrity je funkce factorial() (která počítá faktoriál nezáporného celého čísla, n!), která může v C++ vypadat třeba následovně:
unsigned factorial(unsigned input) { unsigned result = 1; for (unsigned r = input; r > 1; r--) { result *= r; } return result; }
Jak můžeme snadno zjistit, tato funkce počítá výsledek skutečně pouze na základě hodnoty vstupního parametru a nemá žádné vedlejší efekty. Otázka ale zní: počítá tato funkce výslednou hodnotu vždy správně?
Otázku lze rozdělit do dvou podotázek:
1. Počítá funkce factorial() správně výsledek pro každou hodnotu ze svého definičního oboru?
2. Je zajištěno, že vstupní hodnota funkce factorial() patří do definičního oboru této funkce?
Definiční obor pro faktoriál je obecně množina nezáporných celých čísel a oborem hodnot je podmnožina množiny všech nezáporných celých čísel, takže v prvé řadě bychom se měli ptát, zda algoritmus použitý v naší funkci počítá správně faktoriál za předpokladu, že typ unsigned reprezentuje všechna nezáporná celá čísla. To lze poměrně snadno ověřit.
Typ unsigned zajišťuje, že vstupní hodnota patří do podmnožiny nezáporných celých čísel. Problémem je ale skutečnost, že typ unsigned nereprezentuje množinu všech nezáporných celých čísel. Vzhledem k tomu, že typ unsigned je shora omezený (viz UINT_MAX) že faktoriál je strmě rostoucí funkce, nemůžeme zaručit, že pro všechny možné vstupní hodnoty v rozsahu typu unsigned dokážeme správně vypočítat výstupní hodnotu, protože může dojít k přetečení rozsahu tohoto typu.
Jedno z možných řešení je použít jiný výstupní typ. Existují knihovny pro počítání s (teoreticky) libovolně velkými čísly, které tento problém mohou řešit. Skutečnost je ale taková, že obyčejně nepotřebujeme a ani nechceme, aby naše funkce počítala výsledek pro hodnoty omezené nějakým obecným typem (například unsigned), ale požadujeme, aby funkce počítala správně pro námi určenou množinu vstupních hodnot. Potřebujeme tedy zajistit, že na vstupu dostaneme některou z povolených hodnot. Tím se dostáváme k dalšímu konceptu, který se jmenuje design by contract. S tímto pojmem přišel Bertrand Meyer, autor programovacího jazyka Eiffel. V našem případě chceme:
1. Určit povolený rozsah vstupních hodnot
2. Dát funkci možnost si vstupní hodnotu pohlídat
Pokud nám stačí počítání faktoriálu pro hodnoty menší nebo rovny 10, můžeme zůstat na 32bitových a vyšších platformách u typu unsigned. Nejjednodušší způsob kontroly nám v C++ skýtá makro assert, který způsobí, že v případě nepřípustné hodnoty program skončí s chybou:
unsigned factorial(unsigned input) { assert(input <= 10); unsigned result = 1; for (unsigned r = input; r > 1; r--) { result *= r; } return result;
}
Když se v krátkosti vrátím k funkci filter() z minulých dílů, můžeme o ní říci, že v principu dodržuje principy referenční integrity, nemá vedlejší efekty a povolené vstupní hodnoty jsou poměrně dobře ošetřeny deklarací typů parametrů. Tato tvrzení jsou samozřejmě podmíněná a v průběhu seriálku se k nim ještě vrátím.
V dalších dílech bych chtěl ukázat implementaci principů design by contract pomocí typového systému, nástrojů typu GNU Nana a knihovny Loki, podrobněji pohovořit o referenční integritě a programování bez vedlejších efektů a věnovat se podrobněji možnostem statické analýzy kódu v C++. Nevylučuji, že se časem vynoří další zajímavá témata.
Krása, aneb Pasti a propasti jazyka C++ ;)
Add integrita funkcí, abstrakce : je sice moc pěkné když všude člověk čte poučky o tom, jak je nutno si dát pozor na vedlejší efekty funkcí, nezávyslost oběktů a podobné věci, zvláště je-li u toho uvedena jako příklad funkce, která jen spočítá parametry a vrátí výsledek.
Jenže praxe je to IMHO trochu jinde. Pokud člověk píše trochu složitější program, používá nějaké knihovny a API na jehož základě implementuje funkce může jít integrita a nezávyslost velice rychle do háje - aniž by si čehokoliv všiml. Nedej bože, aby se v nějaké knihovně objevila chybička... To je potom papačka.
Myslím si že, toto je taky jedna z velkých nevýhod jazyků založených na vysoké úrovni abstrakce. V C/C++ se dá ještě ledacos zachránit, popř. udělat jinak (a lépe ;-) ), ale na vyších úrovnich abstrakce už to asi tak jednoduché nebude....
[1] Díky za odpověď, já věřím tomu, že knihovny dokážou udělat docela paseku. Viděl jsem pravidelně segfaultovat i program v Pythonu, což by teoreticky nemělo jít, ale když jde o nějaký binding do C, není to problém. U C++ jsme na to už narazili v diskusi vzhledem k STL. Sutter a Alexandrescu například doporučují používat jednu prověřenou verzi STL, což je na jednu stranu logické, na druhou stranu mi to ale přijde dost extrémní.
Mým záměrem není z C++ udělat čistě funkcionální jazyk, ale spíš prozkoumat možnosti statické analýzy obecně a zvlášť vzhledem k C++, protože to je podle mého názoru "nejnižší" jazyk, který statickou analýzu ve větším rozsahu umožňuje a jeho flexibilita je úžasná, to napětí mezi těmi "školometskými principy" a tím, jak je C++ umožňuje porušit, je velká výzva. Začal jsem s tím, co jsem znal a měl promyšleno, ale od prvního dílu jsem nastudoval poměrně dost materiálů a dost o celé věci přemýšlel.
Rád bych vzal nějaký OSS projekt středního rozsahu a na něm ty principy vyzkoušel. Ale to bude ještě chvíli trvat.
[3]
Add pýthon) Upřímě nevím, jak je interně implementovaný Python ale dost pochybuju o tom, že by nemohl skončit segfaultem (i když zvláštní by bylo, kdyby skolaboval samotný skript a interpreter běžel dál..)
Já jen chtěl říct, že ty školské poučky je nutno brát trochu s rezervou. pokud se opravdu nejedná o nějaké extrémě primitivní funkce (které žel bohu bývají v takových případech uváděny za příklad), tak žádná funkce ani oběkt nebývá většinou ostrovem sam pro sebe.... A to se bohužel netýká jen C/C++.
[5] Já nevím, jestli si teď rozumíme, ale když zkolabuje nějaký pythonovský skript, znamená to, že virtuální stroj detekuje chybu a vyhodí výjimku, např. KeyError, když se snaží skript přistupovat k prvku slovníku pomocí neexistujícího klíče. Když ten skript výjimku neodchytí, klasický handler vypíše chybovou hlášku a sekvenci volání ze zásobníku a interpret skončí přes exit. Z hlediska skriptu je to průšvih, ale ta nejnižší vrstva je v pohodě. Když ale napíšu rozšiřujcí modul v C, interpret nad ním nemá takovou kontrolu jako nad bytecode pythonovského skriptu, protože ten rozšiřující modul si může v zásadě řádit podle libosti jako klasický céčkový program.
Co se týče těch izolovaných funkcí, existují určité mechanismy, jak dodržet referenční integritu a další principy, ikdyž jsou občas docela krkolomné. Například jazyk Clean má funkci Start (jakási obdoba main v C) a pokud chceme, aby jazyk byl interaktivní, musíme mu vyplnit volitelný parametr typu World, se kterým ta funkce pak pracuje při výpočtech.
[5] O tom, že realita je jiná, to žádná. Ale nemohlo by to být zapříčiněno hlavně tím, že se v praxi na ty školské poučky kašle?
Uvedl bych jako příklad databáze, kde je to moc krásně vidět. Snad nikdo nepochybuje, že dodržování normalizačních pravidel je velice důležité. A přesto to mnozí prostě a jednoduše ignorují. Samozřejmě si to více či méně dobře zdůvodní. A samozřejmě, že pak jsou z toho větší či menší průšvihy.
[6]
add python) Teď už ano. Když dojde k chybě při vykonávání skriptu, tak to beru. Já měl spíš na mysli chybu, k níž by došlo trochu níže (bez ohledu na implementační jazyk) :)
add funkce) Hmm.... dost pochybuji o efektivitě takovýchto principů. Na úrovni daného jazyka snad, ale když to vezmete z globálního hlediska, tak by se asi ukázalo, že je daný program stejně silně provázaný se svým okolím, což může potencionálně (za jistých okolností) zase vést k problémům, včetně (alespoň v principu) porušení diskutované integrity.
Vezměte si, že samotný program (řekl bych, že jakýkoliv spustitelný kód) se dá z hlediska operačního systému považovat za určitou abstrakci, ne-li rovnou za funkci. No a jak ta je provázaná (používá knihovny, které mohou být dále provázány se zbytkem systému, může číst a zapisovat do souborů, nebo systémových proměnných, atp...). Tomu se dle mého názoru dá jen těžko zabránit...
[7]
Ano máte do jisté míry pravdu. Já ale spíš upozorňuji na jednostrannost těch pouček, které neberou v potaz to, že daný program neběží z ničeho a ve vzduchoprázdnu.... :)
[4]: No já nevím, třeba jen jako profík v jednom oboru reaguju na druhého profíka v jiném oboru stejným způsobem, jako on ve svém oboru reaguje na jiné? :-) BTW, mezi anglickým "add" a latinským "ad" je asi stejný rozdíl jako třeba mezi =~ v Perlu a ~= v Lua, nebo = v Pascalu a = v C.
[9] Tady Tě taky musím podpořit. Úroveň psaného textu je jistý indikátor, který není radno podceňovat. Ale řekl bych, že ten rozdíl je asi jako mezi fakturou a frakturou (jedno písmenko, kouzlo nezamýšleného), termínem a temitem či mezi žebrákem a Točníkem (pro znalce Cimrmana).
[9, 11]
Ano, a vy nemáte nic lepšího na práci, než mi nahradit kompilátor udělat řádnou lexikální a syntaktickou kontrolu a na konec případnou chybu řádně zviditelnit? Tak to vás čeká spousta práce, protože já jsem dysortografik a navíc jsem začal používat Operu, kde se kontrola pravopisu neprovádí automaticky - takže na to čas od času zapomínám. Přeji příjemnou zábavu ;)
Jinak přiznávám, že jsem si tu zkratku popletl. No a? Vám se nikdy nic podobného nestalo? nikdo není dokonalý - tedy já určitě ne. Už jsem se s tím jaksi pro tento okamžik smířil. A vy? :)
PS. Kdybychom psaly pravopisný cvičení, nebo slohovou práci, neřeknu ani slovo, ale řekl bych, že o tom to tu snad není. A ještě než začnete psát něco o úrovni chybujících, uvědomte si, že podobnými narážkami říkáte též něco o své vlastní úrovni ;)
Nevím co přesně dělá to Vaše makro assert, ale pokud je to totéž co v Céčku, nebylo by lepší jej nahradit něčím, co hodí výjimku (když už mluvíme o C++)? Je sice hezké, že factorial() nepřipustí chybný vstup, ale zase je nehezké, aby se kvůli tomu zbortilo řízení jedoucího vlaku...
[3] ani stabilni verze STL nepomuze, pokud clovek pravidelne updatuje kompilator samotny.
Podle mne osobne nejlevnejsi cesta k bezpecnemu programovani je Test Driven Development. Unit testy zabezpeci ze jednotlive funkce na nizke urovni (mysleno hlavne abstrakce) funguji presne dle specifikace testu, metodika TDD zase nuti k relativne dobre modularnosti a efektivite (v kontextu abstrakce) API, takze vetsina veci v aplikaci se pak deje prave na te nizke urovni ktera je kryta unit testy.
Porad je tam trochu mista pro velmi komplexni chyby ktere se projevi az po poskladani aplikace do celku a nejdou jednoduse odchytit unit testem, ale jednak jsou tyto pripady mene caste a jednak muze clovek natolik verit jednotlivym funkcim, nakolik ma kvalitni unit testy, co ho bud prinuti pridat dalsi unit testy aby vice veril podezrelym funkcim, nebo se muze soustredit na hledani chyby na vyssi urovni v celkovem propojeni aplikace a nemyslet na jednotlive male funkce.
Taky je tento pristup vhodny pro agilni vyvoj.
Jako malou nevyhodu ze sve zkusenosti zatim vnimam souboj TDD s QA, t.j. v ramci TDD jsou potreba unit testy k veci, ale pokud mozno neredundantni, aby v ramci agilniho vyvoje kdyz se radikalne refaktoruje API funkci, tak byla potreba zmenit jenom minimalni mnozina testu, naopak kvalitni QA vyzaduje od testu nejenom 100% code coverage, ale i redundantni testy ktere dukladne otestuji funkce jestli opravdu pocitaji to co maji. Ale neni to tak spatne, kdyz treba funkce ma byt dostatecne robustni, tak treba testovani extremnich vstupu je soucasti jiz zakladni unit testu, takze tech kvuli QA navic uz neni tak moc a pri refaktoringu uprava testu na nove API nakonec vetsinou zabere jen malou cast prace.
[13]
Jazyk C++ se pokud vím nikdy nestavěl do role objektového, ale typového jazyka. Je ale zajímavé, že se v něm dají využít principy OOP (a jiné podobné techniky). Koliku jazyků je podobně flexibilních? No, já bych řekl, že Lisp asi moc ne. A když už jsme u něj, Lisp mě taky moc nebere, to když už, tak radši TCL... :)
[19] (OT, flame) To že Vás Lisp nebere není důvod si myslet, že není flexibilní pro většinu mně známých významů tohoto slova. Tedy pokud jste tu větu nemyslel tak, že Common Lisp není podobně, ale mnohem víc flexibilní. Pokud máte v oblasti OOP (klíčová slova: CLOS - součást standardu v Common Lispu, MOP - de-facto standardní rozšíření) protipříklady, dejte vědět, rád si rozšířím obzor.
Když už flamuju, tak i k článku - pokud mně zajímá faktoriál čísel do deseti, tak je zcela funkcionální a čistý postup lookup v tabulce vytvořené během kompilace. O tom to nebylo, já vím. Jenže i to je součást flexibility jazyka - umí jazyk bez dalších obezliček pracovat s celými čísly bez ohledu na jejich velikost*? Pokud deklaruji typ proměnné (už si to nevybavuji - můžu definovat v C ekvivalent typu (integer 0 10)? - assertion je jedna věc, definice typu jiná), umí na požádání hlídat jeho přetečení? Autorova činnost mi přijde jako záslužná, leč při vší úctě k C++ s použitím nevhodného nástroje.
* s omezeními danými velikostí paměti a podobně - a s případnou vhodnou chybou při jejich překročeních
[20]
Hmm... jeden v souvislosti s Lispem mě napadá. Např. V knihovně Loki ( http://loki-lib.sourceforge.net/ ) lze najít šablonové třídy, které implementují typové seznamy. S jejich pomocí a trochou práce by bylo možno pro použití v C++ kódu implementovat seznamy jaké má Lisp, bez rozšiřování jazyka samotného, pouze za využití prostředků které nabýzí. Tedy se ptám - umožňuje něco podobného i Lisp?
PS. Je otázkou, co považujete za flame...
[22]
To je pravda. Nicméně, to jsou jeho uživatelé, ne jeho tvůrci, či jazyk sám....
Pokud vím, tak sám Straustroup někde ve své knize "C++ programovací jazyk" říká, že C++ je založeno na datových typech a strukturách a ne objektech. Jenže (alespoň z mých zkušeností a soudě podle toho jak je podobný jiným jazykům) k tomu musí asi každý dojít sám...
Jinými slovy, podle mě to vychází z toho, že C++ nebylo pochopeno, nebo z falešného pocitu, že když C++ není plně objektový jazyk, tak je méně cenný. Nebo prostě z obavy, že by náhodou mohl mýt pravdu někdo jiný...
[20]
- C ani C++ s vetsimi cisly nez je long long pracovat bez obezlicek neumi, sice to v C++ jde zabalit do objektu, ale hoodne velkou ciselnou konstantu nedas :-), stejne jako se budou muset prepsat vsechny metody pro iostream, konverze atd., docela dost prace...
- integer 0 10 a podobne deklarace se musi v C++ obchazet pomoci objektu a pretezovani operatoru (operatory - to je takove celkem zbytecne syntakticke cukratko), preteceni opet nutno imperativne napsat do kodu k onomu objektu. Dtto pro pole - runtime checking na prekroceni mezi ve standardu nenajdes.
[21]
- skutecne jsou ty seznamy ekvivalentni s Lispovymi? Ja se tedy Lispu nechci nijak zastavat, jen podotykam, ze i samotny kod je v Lispu zapisovan do seznamu - tj. lze napriklad s kodem ruzne manipulovat (skolni priklad-symbolicka derivace vyrazu), zavolat eval atd. Nevim nevim, ale to zadna knihovna nad kompilovanym jazykem asi neda :-)
Jestli chcete jit od techto nizkopodlaznich jazyku nekam "vyse", zkuste Lisp (Scheme), Python ci Lua, popripade i ten JavaScript ujde.
"naopak kvalitni QA vyzaduje od testu nejenom 100% code coverage, ale i redundantni testy ktere dukladne otestuji funkce jestli opravdu pocitaji to co maji."
Alternativou je ještě Knuthův přístup - "Beware of bugs in the above code; I have only proved it correct, not tried it". :-)
[25] ano to myslim naprosto vazne. JavaScript (aniz bych se ho chtel nejak zvlast zastavat) je jeden z nejvice nepochopenych programovacich jazyku dneska. Asi nejvic mu uskodila spatna implementace v browserech, takyprogramatori vyvijejici metodou copy&paste bez hlubsiho porozumeni problemu a taky literatura, kterou snad psali lide, co JavaScriptu neporozumeli (no par vyjimek je, ale dost malo). Ale jde o vysokourovnovy jazyk s funkcionalnimi rysy, plnou podporou funkci (funkce je datovy typ se vsim vsudy), uzavery atd. Navic s celkem nenapadnou syntaxi, ktere se lidi tak neleknou, narozdil od Lispu (ale ten za to nemuze, prave diky jeho syntaxi je to tak mocny jazyk :-)
[15] "Moje" makro assert() je to klasické z jazyka C, byl to jenom jednoduchý příklad. Ale samozřejmě není problém nadefinovat si makro vlastní, které vyhodí třeba právě tu výjimku. Jestli je lepší nechat program natvrdo sletět, nebo se raději snažit z toho nějak vybruslit, je určitě otázka konkrétní aplikace. Pamatuji si na předlouhý thread v konferenci cz.comp.linux před lety na toto téma. Byl to docela slušný flamewar.
[16] Kompilátor samozřejmě má vliv také, ale to už je vcelku extrémní příklad, podle mě. TDD je zajímavý z několika důvodů. Třeba tím, že přímo vybízí k tomu, aby programátor dodržoval v článku uvedené principy nebo tím, že často nabourává politiku přístupových práv v některých jazycích (Java). V dokumentu, který je, žel, k dispozici pouze po zaplacení předplatného (já jsem si ho zaplatil a myslím, že nebudu litovat, protože v knihovně jsou k dispozici zajímavé materiály právě z hlediska statické analýzy a podobných disciplín) tvrdí skupina, v níž je i Bertrand Meyer, že ke generování jednotkových testů lze využít právě kontrakty uvedené v kódu (použitý nástroj je EiffelStudio: http://portal.acm.org/citation.cfm?doid=1287624.1287685
[20] Vytvoření tabulky faktoriálů při kompilaci je jistě teoreticky možné, ale je to dobrý nápad? Pokud tu funkci volám často a input je omezen shora poměrně malým číslem, pak možná ano (ale i tak lze tu tabulku budovat dynamicky za běhu, ikdyž v tom případě porušíme uvedené principy), ale pokud tomu tak není, bude asi lepší líný přístup. C takový typ definovat neumožňuje, jinak by ta legrace s assertem byla koneckonců zbytečná, ale v C++ by to implementovat šlo. Podle mě to úsilí při použití C++ není zbytečné - jednak v C++ existuje obrovské množství programů a další pořád přibývají a jednak se s podobnými potížemi jako v C++ setkáme i v jiných imperativních nebo hybridních jazycích. Třeba taková Java mi přijde v něčem i horší než C++, protože nutí programátory používat třídy i tam, kde se moc nehodí a svádí je tím k porušování referenční integrity a ke kódu plnému vedlejších efektů.
Děkuji všem za reakce.
[24]
To totiž nedělá knihovna, ale překladač. Na této úrovni je to už trochu něco mezi nebem a zemí ( :D ). Všimněte si, že tyto seznamy typů (typelist) jsou schopny (a to je hlavní důvod proč byli v Loki vytvořeny) vygenerovat v době překladu celou a kompletní hierarchii tříd. Tyhle seznamy manipulují přímo s jednotlivými datovými typy (ne s jejich hodnotami). Toho využívá kupříkladu abstraktní továrna na objekty (tamtéž).
Domnívám se, že pokud bych postavil operace s konkrétními hodnotami položek seznamu jako typy, pak by to šlo. Upřímně, ten nápad nepochází z mí hlavy, ale od Alexandrescua, a s takovými věcmi jako je funkce eval, bych se asi už nepopral, ale principiálně by to podle mého názoru fungovat mohlo. (Je už otázkou zda by to k něčemu bylo užitečné...)
Nechci nikam výše :-) . Když už, tak jsem si oblíbil TCL (a to pouze na jednoúčelové skripty - asi tak jak jiní využívají BASH), a díky panu Tišnovskému, začínám pomalu koketovat s jazykem LUA, ale zase v kontextu programů psaných v C++ (tedy žádné bindingi a pod.). C++ je pro mě prostě "ten pravý" :-D
[28]
- nemyslim si ze by rozbite STL diky novejsi verzi kompilatoru byl az tak ojedinely stav, specialne kdyz ta verze STL je starsiho data.
- u TDD nejde vyuzit kontrakty uvedene v kodu ke generovani testu z principu (pokud myslite primo Unit testy): u TDD nejdriv vznika test, pak kod, takze v dobe kdyz je kod(+kontrakty) napsany, uz neni co generovat. To by mozna slo spis naopak, z unit testu vytahnout kontrakty. Ale cele to nedava moc smysl, protoze unit testy samotne muzou pokryt i veskery prostor ktery ma k dispozici staticka analyza, jedina nevyhoda je v tom ze potrebuji prelozit a spustit, staticka analyza muze probihat bez prekladu. (ale musi byt o dost rychlejsi nez build + unit test run, co nemusi byt az tak trivialni pri slozitosti syntaxe C++) Vzhledem k tomu ze u TDD se doporucuje mit dobu buildu + behu testu pod 2sec ... to klade na tu statickou analyzu hodne extremni pozadavky.
Leda ze by jste myslel generovani dodatecnych QA testu (z pohledu TDD duplicitnich), to jiz urcitou logiku ma, ale podle mne to kvalitni lidskou ruku urcite nenahradi, a nejspis ani nedoplni, nebo jen ve stastnych pripadech nahodou. Ale nemam s tim zadnou praktickou zkusenost, soudim tak jen z obecne zkusenosti s elektronikou a SW, kde plati ze nic nefunguje tak jak ma a co se NEmuze pokazit, to se pokazi v nejmene vhodne chvili. O tom co se muze pokazit nema smysl ani diskutovat, to je pokazene non stop. :)
[30] Já jsem nepsal o tom, že z kontraktů vzejde TDD, to je samozřejmě úplně jiný přístup k vývoji SW. Nemyslím si, že by bylo nutné spouštět analyzátor tak často, aby bylo nutné řešit, zda bude trvat 2 sekundy nebo půl minuty. Kromě toho i syntaktickou analýzu lze dělat inkrementálně, ale jak říkám, vývoj typu napíšu jednu metodu, otestuju, přidám řádek, otestuju, je trochu jiný přístup.
Mimochodem, ten dokument, který jsem vzpomínal, je i volně přístupný, jak jsem zjistil. Takže pokud by měl někdo zájem, může se podívat sem: http://dev.eiffel.com/CddBranch
[31]
"..., vývoj typu napíšu jednu metodu, otestuju, přidám řádek, otestuju, je trochu jiný přístup."
Nevím, je otázkou na kolik takový přístup je užitečný... To by taky vývoj jedné třídy mohl trvat půl roku a než by jste byl hotový, tak by jste možná zapoměl proč tu třídu vlastně píšete. :-)