Mogens Hansen wrote:
>> "Jakob Bøhm" <jb@danware.dk> wrote in message
>> news:464846b7$0$13937$edfadb0f@dread15.news.tele.dk...
>
> [8<8<8<]
>> Et stykke lineær kode mister egenskaben at linierne udføres allesammen
>> eller slet ikke.
>
> Der er ingen principiel forskel på fejlhåndtering med exceptions eller retur
> koder.
Bortset fra læselighed og overskuelighed.
> Det er mere et spørgsmål om at separere fejlhåndterings koden fra "normal"
> kode.
>
> I yderste konsekvens kan man transformere fra exception til fejlkode og den
> anden vej:
> try {
> func();
> }
> catch(...) {
> return SOME_ERROR;
> }
>
> og
>
> if(NO_ERRR != func()) {
> throw runtime_error();
> }
>
> Hvis man modtager en fejlkode er det meget almindeligt at funktionen må
> returnere, uden at eksekvere det normale flow. Prøv bare at kig på typisk
> Microsoft COM kode.
Enig. Der findes også COM-klassebiblioteker hvor alle kald til/fra
COM-interfaces automatisk converterer mellem fejlkoder og C++
exceptions. Microsoft's scriptsprog gør dette konsekvent som en del af
deres runtime.
> Tilsvarende er det muligt at fortsætte eksekveringen, selvom der smides en
> exception - det gør vi ofte i det program jeg primært udvikler på, og især i
> brugerfladen.
God ide.
>
>> Al robust kode skal lave paranoide data-commits i tide og utide for "hvad
>> nu hvis jeg pludselig bliver slået ihjel". Der er en høj real-world
>> risiko for datatab.
>
> Det har ikke noget med exceptions a gøre.
Tag følgende funktion (lavet med pointere i stedet for referencer for at
kunne lave en runtime check):
template<T> bool swap(T *pa, T *pb)
{
T x;
if (pa && pb) {
x = *pa;
*pa = *pb;
*pb = x;
return true;
}
return false;
}
Hvis T er en normal type kan man med sikkerhed regne med at funktionen
enten bytter om på a og b eller gør ingenting. Det er let at resonere
med hensyn til opretholdelse af invarianter m.m.
Hvis T er en klasse som kan throwe en exception (f.eks. xalloc) i sin
operator =, så kan man ikke længere antage noget som helst om
opretholdelse af invarianter, da man risikerer at blive efterladt med to
kopier af den ene værdi, men ingen af den anden. For at få en
forudsigelig og verificerbar semantik bliver man nødt til at tilføje en
masse try blokke eller bruge dummy-klasser til at simulere
__finally-blokke. Læremestre udi pure C++-thinking elsker at pille den
slags eksempler fra hinanden enkeltvis og så påstå de har modbevist
problemets eksistens i almindelighed.
>
>
>> Vi har nok alle oplevet at bande over en editor som pludselig terminerede
>> før vi havde savet vores seneste guldkorn.
>
> Helt klart.
> Jeg husker een editor som fangede "general protection fejl" (altså en ikke
> forudset fejl), og pænt spurge om man ville gemme filen alligevel.
> Hvis man sagde ja, så slettede den hele filen!
> Det er blot eet eksempel på at det er svært at komme ud af en ukendt
> tilstand på en fornuftig måde.
> Den kortsigtede løsning var _altid_ at svare nej. Den langsigtede løsning
> var at forbedre stabiliteten.
Forkert langsigtet løsning. Den rette langsigtede løsning ville have
været at en sådan nødsave sikrede sig selv mod at save rent crap, f.eks.
ved at save med et alternativt filnavn som det kendes fra mange
UNIX-editorer og fra Microsoft Word.
>
> [8<8<8<]
>> En anden fejlantagelse i nogle svar er at range-check kun er en
>> debugging-mekanisme som i et "fejlfrit" program kan disables i
>> produktionskoden.
>
> Hvorfor er range-check speciel ?
Det er de ikke, andre checks er lige så gode eksempler.
> Tester du ved indgangen til enhver member-funktion at "this" pointeren er
> gyldig ?
Hvis jeg implementerer klassen i C er svaret normalt ja! I C++ er
svaret nej.
> Hvis ikke: hvorfor ?
C++ vil ofte have derefereret this allerede i det øjeblik kaldet blev
foretaget (gælder især hvis objektet kaldes med punktum i stedet for ->,
eller hvis funktionen er virtuel), så en sådan check vil sjældent fange
noget. Dertil kommer at C++ selv antager at this aldrig er NULL og
derfor kan bortoptimere udtrykket (this == NULL). Endelig kan
nedarvningsforhold få C++ til at lægge små konstanter til NULL før NULL
bliver til this, hvilket gør testen meget sværere.
> Under udvikling (debug build) checker jeg normalt _samtlige_ this-pointere,
> _alle_ pointer operationer inklusiv range check etc. (det er et værktøj som
> gør det).
God ide, er det et kendt værktøj jeg kan tilføje til mine unit-tests?
> Men i release build er det disablet, fordi programmet kører ca. 10 gange
> langsommere.
Det tyder på at testen ikke samarbejder med optimizeren, eller at dit
buildmiljø generelt disabler optimizeren på debug-builds. Især
whole-program-optimization burde kunne eliminere alle de tests som
statisk kan verificeres at være i orden.
>
>
> [8<8<8<]
>> En tredje fejlantagelse er at det ikke er spor slemt at lukke et program
>> ned ved fejl.
>
> Du fusker med citaterne !!!
> Hvor i denne tråd har nogen sagt at det ikke er spor slemt ?
Ingen steder direkte, men mange steder er det blevet skrevet at i denne
eller hin situation bør man aborte/asserte uden nogen angivelse af at
dette er problematisk.
>
>> Det kommer meget an på programmet og må derfor ikke dikteres af generelle
>> klassebiblioteker.
>
> Jeps
> Men generelle biblioteker må gerne stille krav, som kalderen skal overholde
> for at få den ønskede virkning.
Ja, men krav som går ud over selve biblioteksinterfacet (f.eks. at det
er acceptabelt at biblioteket terminerer programmet) kan være ekstremt
problematiske hvis kalderen befinder sig i et miljø uden den egenskab.
>
>> Nogen programmer (f.eks. Webservere) skal bare køre 24x7, og en halvdårlig
>> kørsel er bedre end slet ingen kørsel.
>
> Gælder det altid ?
Nej, men ofte, og kun så længe man kan holde sig indenfor en
applikationsspecifik definition af forskellen på halvdårlig og heldårlig.
> Hvad hvis man udnytter en programmerings fejl i webserveren (eller en af de
> applikation den kører) til at få kontrol over computeren ?
Det ville være en heldårlig kørsel. Min pointe er lige præcis at kode i
en muddle-through process er nødt til at være runtime-paranoid med sine
argumenter og reducere nonsens-værdier (f.eks. out-of-range index) til
ufarlige værdier (f.eks. 0, NULL eller EOF/INVALID_HANDLE_VALUE).
>
> Hvis man kigger på en Webserver som Microsoft IIS, så har den forskellige
> modeller for hvordan man kan eksekvere udvidelser (web applikationer).
> Der er et trade-off mellem performance og pålidelighed.
> Man kan konfigurere IIS således at selve serveren kører i een process og
> hver udvidelse (f.eks. ASP applikation) kører i hver sin process.
> Det betyder at man har operativ-systemet og hardware til at beskytte selve
> serveren og udvidelserne fra hinanden, så en applikation der går amok ikke
> kan slå IIS serveren ned.
>
> Det er netop en løsning, hvor man i tilfælde af uforudsete fejl kan bringe
> programmet (IIS) i en kendt tilstand.
>
> Noget tilsvarende er argumentationen for micro-kernel operativ systemer.
>
>
Enig, det jeg argumenterer for er at bruge den samme filosofi på
programdele med mindre granularitet end processer. En ting er at bringe
hele programdelen i en "kendt tilstand" (læs: tom nultilstand), noget
andet er blot at bringe den konkrete dataværdi i en kendt tilstand men
samtidig bevare de øvrige data. I IIS-programmer bliver tilstanden ofte
lagt over i en SQL Server process hvor de kan overleve et IIS-crash, i
mange andre programmer findes denne luksus ikke.
--
Jakob Bøhm, M.Sc.Eng. * jb@danware.dk * direct tel:+45-45-90-25-33
Danware Data A/S * Bregnerodvej 127 * DK-3460 Birkerod * DENMARK
http://www.netop.com * tel:+45-45-90-25-25 * fax tel:+45-45-90-25-26
Information in this mail is hasty, not binding and may not be right