[alg] Exceptions, wanneer te gebruiken?

Pagina: 1
Acties:
  • 216 views sinds 30-01-2008
  • Reageer

Acties:
  • 0 Henk 'm!

  • Michali
  • Registratie: Juli 2002
  • Laatst online: 29-05 22:54
Ik ben erg bezig met het proberen onder de knie te krijgen van de juiste manier (lees: een goede manier) voor het gebruik van exceptions. Nu zit ik een beetje met de vraag wanneer een exception nou goed te gebruiken is en wanneer het overbodig is. En dan heb ik het met name over exceptions die je zelf schrijft en throwed, dus niet zozeer een exception die je zou kunnen verwachten bij het inlezen van een resource oid.

Als een methode false returned om aan te geven dat de gevraagde actie mislukt is. Is het hier altijd beter om een exception te gebruiken, of is het opvangen van de return waarde en aan de hand van die waarde bepalen of er iets gedaan moet worden beter?

Stel dat ik een variabel uit een collectie wil halen. Is het dan beter om van te voren te controleren of hij er ook werkelijk in zit en aan de hand daarvan actie ondernemen, of is het beter om de waarde maar gewoon proberen op te halen en eventueel de exception afvangen als hij niet bestaat.

Is het verder beter om alle exceptions te proberen voorkomen met een controle, of is het ook een goede methode om het actief in te zetten als er ook maar iets gebeurd wat je met een controle zou kunnen afvangen? Of is het beter om er meer tussenin te gaan zitten, waar zou de grens liggen?

En hoe ver kan ik gaan in gespecialiseerde exceptions? Is het geschikt om voor ieder type fout een aparte exception te schrijven, of is het beter om voor een groep gerelateerde fouten (doordat ze bijvoorbeeld in dezelfde laag gethrowed worden) een exception te maken en aan de hand van constanten en een switch de juiste actie te ondernemen?

Wat is de strategie die jullie hier toepassen? Graag ook met een beetje onderbouwing, ik ben namelijk erg benieuwd naar de gedachte er achter.

[ Voor 4% gewijzigd door Michali op 15-12-2004 16:05 ]

Noushka's Magnificent Dream | Unity


Acties:
  • 0 Henk 'm!

  • MLM
  • Registratie: Juli 2004
  • Laatst online: 12-03-2023

MLM

aka Zolo

Persoonlijk vindt ik dat je Exceptions alleen moet gebruiken in .NET aangezien het framework er ook veelvuldig gebruik van maakt.

In principe zijn functies zodanig dat je returnvariable teruggeeft of een functieaanroep geslaagd is, en de in en uitvoer in de parameters zitten. Helaas is dit enigzins utopisch aangezien iedereen andere dingen gebruikt, en lang niet iedereen ook daadwerkelijk returnvalues checkt.

Op dat punt zijn exceptions wel nuttig, want als je ze niet cathed dan crasht je programma :D

Ik denk dus vooral een kwestie van persoonlijke voorkeur

-niks-


Acties:
  • 0 Henk 'm!

  • Plekuz
  • Registratie: September 2002
  • Laatst online: 23-07 21:42

Plekuz

available in strong mint

IMO: Alle problemen die de werking van je programma verstoren via een exception opvangen, zodat foutafhandeling uniform in je programma geregeld wordt.

"There he goes. One of God's own prototypes. Some kind of high powered mutant never even considered for mass production. Too weird to live, and too rare to die."


Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 09-08 12:23

.oisyn

Moderator Devschuur®

Demotivational Speaker

Exceptions gebruik je in uitzonderlijke gevallen, hence de naam "exceptions". Bijvoorbeeld als je een query probeert uit te voeren op een database object wanneer de connectie met de database verloren is. Of als je een file probeert te lezen die op een disk staat die inmiddels uit de computer verwijderd is. Dat soort dingen.

Het handige van exceptions is dat ze door je programma propageren tot op het moment dat je 'm expliciet afhangt, integenstelling tot errorcodes die genegeerd worden als je niets doet. Om het database-query-voorbeeld nog eens aan te halen; als je een query wrapt in een functie, zoals getTopicsFromForum(), dan is het een beetje onzinnig om op dat moment op errors te gaan controleren, aangezien je er op dat niveau niets mee kunt. Degene die je getTopicsFromForum aanroept kan er waarschijnlijk wel wat mee, en die is dan ook verantwoordelijk voor het afvangen van de exception.

Daarnaast is het natuurlijk noodzaak om exception-safe code te schrijven. Een functie zou bijvoorbeeld een resource kunnen aanmaken die een stukje verderop in de functie weer vrijgegeven wordt. Als er een exception tussentijds optreedt wordt de vrijgeef-code niet aangeroepen, en daarom is het noodzaak dat soort dingen in het finally gedeelte van een try block te zetten (of in een destructor in het geval van C++, aangezien die geen finally kent)

Gebruik exceptions echter nooit om normale program-flow te definieren.

[ Voor 3% gewijzigd door .oisyn op 15-12-2004 16:11 ]

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Acties:
  • 0 Henk 'm!

  • pjvandesande
  • Registratie: Maart 2004
  • Laatst online: 14-07 10:25

pjvandesande

GC.Collect(head);

Eigelijk is het heel simple, altijd een exception gooien wanneer er iets voorkomt wat uitzonderlijk is. Ik vind persoonlijk een False returnen of een null niets waard, want dan weet je nog wat er nou precies mis is.

Exceptions hebben een voordeel dat ze een StackTrace hebben en nog meer details die je kunnen helpen met het oplossen van een probleem. Bij het connecten naar een server kun je wel een false returnen als het niet is gelukt, maar dan kun je alleen een MessageBox showen dat het mislukt is. Maar ook de gebruiker wilt weten waarom, een timeout of iets, dit kun je realiseren met Exceptions.

Ik gebruik weer geen exception als een collectie bijvoorbeeld een bepaalde index niet kan vinden, dan gebruik ik meestal weer een negative return waarde van -1.

Exception types, dus je eigen Exception deel je meestal in in groepen.

Acties:
  • 0 Henk 'm!

  • FlowinG
  • Registratie: Maart 2003
  • Laatst online: 09-08 22:00
In een stricte programmeer taal moet je vaak een vooraf gedefineerde waarde retourneren. Bijvoorbeeld een string of een array. Zodra je hier van gaat afwijken door een boolean (false) af te geven met je imho niet echt netjes bezig. Het beste kan je dan null terug geven.
Wat je kan doen is een reference vragen naar een bepaalde parameter in de methode constructor. Zodra er een error is gegeven krijg je daarin de beschrijving van de error fout code.
Dit is dan vooral handig voor het uitvoeren van bepaalde taken.

Als die taken falen door externe omstandigheden, zal ik gewoon voor de foutcode gaan die terug gegeven worden. Maar stel dat er een fout ontstaat door een verkeerd meegegeven parameter, zal ik voor de "throw error" gaan, omdat dit in veel talen voor een runtime error of een log vermelding zorgt (ligt eraan:programma's of webscripting).

[ Voor 2% gewijzigd door FlowinG op 15-12-2004 16:15 . Reden: Leesbaarheid verbeterd ]


Acties:
  • 0 Henk 'm!

  • alienfruit
  • Registratie: Maart 2003
  • Laatst online: 01:10

alienfruit

the alien you never expected

Ach, anders moet je eens kijken na de Indy library voor Delphi, daar gooien een Exception als je de verbinding verbreekt :X Dat lijkgt mij nou niet echt een uitzondering :+

Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 09-08 12:23

.oisyn

Moderator Devschuur®

Demotivational Speaker

questa schreef op woensdag 15 december 2004 @ 16:11:
Exceptions hebben een voordeel dat ze een StackTrace hebben en nog meer details die je kunnen helpen met het oplossen van een probleem
Dat is nogal implementation defined en is niet echt het doel van exceptions. Ik vind dingen als een nullpointer exception of een class cast exception nogal vreemde dingen aangezien het fouten zijn in je programmacode, en niet zozeer uitzonderlijke gevallen waar jij als programmeur geen controle over hebt. Een assert is in die gevallen beter op z'n plaats. Dat er een stacktrace bij zit heb je in een release build vrij weinig aan, dat zijn slechts debug-zaken

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Acties:
  • 0 Henk 'm!

  • Zoijar
  • Registratie: September 2001
  • Niet online

Zoijar

Because he doesn't row...

Dat is een beetje een 'C' manier van denken, altijd return values voor errors...

Een exception is een uitzondering, het geeft dus een uitzonderlijke fout aan. Als je een set hebt, waar je een element kan opvragen, dan heb je twee mogelijkheden. Of je stelt als pre-conditie dat het element aanwezig MOET zijn. In dat geval is het redelijk een exception te gooien wanneer het niet aanwezig is, want dat is raar en schendt je pre-conditie. Of je kan stellen als post-conditie dat ofwel je element wordt geretourneerd, ofwel het element niet bestond en er een fout code wordt geretourneerd. Bij een set waar elementen vaak niet bestaan, bv een cache pool oid, is dat een normale oplossing en is het geen uitzondering (dus geen exception) als het element niet bestaat.

Exceptions zou ik ook niet te veel afvangen, alleen op kritieke punten. Stel je roept een write_file_to_disk() routine aan. Dan kan je ergens bv een disk_not_ready exception afvangen, en dat stukje opnieuw proberen in de routine. Maar als er nou een kritieke fout optreed, bv disk_full exception, dan hoef je die niet in die routine af te vangen. Dan is het prima als dat pas naar voren komt als resultaat van de write_file_to_disk(), die dan netjes report "failed". Vang een exception pas af, als je er ook daadwerkelijk iets mee kan doen, of als je een stukje code kan aborten.

(.oisyn typed iets sneller, maar ben het zoals gewoonlijk eens :) Hoewel een assert ook als std::logic_error gegooid kan worden. Had het laatst nog, na veel processing op een graph, moet op een gegeven moment elke node 0 of 2 children hebben. Als dit niet het geval was gooide ik een std::logic_error. Tot nu toe nooit gezien, maar het zou ooit misschien kunnen gebeuren als m'n bewijs niet klopt...)

[ Voor 16% gewijzigd door Zoijar op 15-12-2004 16:23 ]


Acties:
  • 0 Henk 'm!

  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024

Alarmnummer

-= Tja =-

Michali schreef op woensdag 15 december 2004 @ 16:03:
Ik ben erg bezig met het proberen onder de knie te krijgen van de juiste manier (lees: een goede manier) voor het gebruik van exceptions. Nu zit ik een beetje met de vraag wanneer een exception nou goed te gebruiken is en wanneer het overbodig is. En dan heb ik het met name over exceptions die je zelf schrijft en throwed, dus niet zozeer een exception die je zou kunnen verwachten bij het inlezen van een resource oid.

Als een methode false returned om aan te geven dat de gevraagde actie mislukt is. Is het hier altijd beter om een exception te gebruiken, of is het opvangen van de return waarde en aan de hand van die waarde bepalen of er iets gedaan moet worden beter?
Als bv een file is aangesproken en dit is niet gelukt omdat de arm van de hardeschijf is afgebroken, dan wil je niet werken met een return value omdat je die eventueel zou kunnen negeren (nouja.. een exception zou je ook kunnen catchen en weggooien). En verder zijn exceptions erg handig omdat je dus een 2e flow in je systeem krijgt, namelijk de exception flow. Hierdoor blijf je niet zitten met allerlei nare exception/standaard flow in je applicatie.

Ik gebruik dus exceptions voor alle uitzonderlijke zaken... zaken waarvan je niet echt zult verwachten dat het fout gaat.. namelijk de uitzondering.
Stel dat ik een variabel uit een collectie wil halen. Is het dan beter om van te voren te controleren of hij er ook werkelijk in zit en aan de hand daarvan actie ondernemen, of is het beter om de waarde maar gewoon proberen op te halen en eventueel de exception afvangen als hij niet bestaat.
Ik weet niet helemaal wat je bedoelt met een variabel uit een collectie halen. Maar als ik een lijst heb waarvan ik weet dat een element erin zit.. en hij zit er toch niet in -> exception. Als ik het niet weet? Dan is het normale programma flow (en dus geen exception).
Is het verder beter om alle exceptions te proberen voorkomen met een controle
Dat is onmogelijk. Stel dat jij vraagt aan jouw hd.. is jouw arm al afgebroken? En hij returned false.. en nog geen microseconde erna breekt de arm eraf... Wat dan? :)
En hoe ver kan ik gaan in gespecialiseerde exceptions? Is het geschikt om voor ieder type fout een aparte exception te schrijven, of is het beter om voor een groep gerelateerde fouten (doordat ze bijvoorbeeld in dezelfde laag gethrowed worden) een exception te maken en aan de hand van constanten en een switch de juiste actie te ondernemen?
Voor programmeerfouten maak ik zo nu en dan wel eens een aparte exception aan.. Maar uiteindelijk ga je hem alleen op het aller aller hoogste nivo catchen.. en dan is een stacktrace met goeie messages meer dan voldoende.

Maar voor sommige componenten maak ik wel (soms) classes exceptions aan die daar van kunnen extenden. Het is een beetje afhankelijk van de situatie. Ik wrap dan wel vaak lagere exceptions in hogere exceptions (en stuur de hogere laag al vast wat bij op basis van de exceptions in de lagere laag: resources vrijgeven, semaforen unlocken etc).

[ Voor 6% gewijzigd door Alarmnummer op 15-12-2004 16:21 ]


Acties:
  • 0 Henk 'm!

  • _Thanatos_
  • Registratie: Januari 2001
  • Laatst online: 07-08 10:54

_Thanatos_

Ja, en kaal

Ik zeg altijd dat exceptions handig zijn als je op een hoog niveau wilt afvangen wat er op een heel laag niveau (ergens diep in je code) mis gaat. Zoals je weet, is de eigenschap van een exception dat alle verdere uitvoerbare code wordt overgeslagen en onmiddellijk naar het eerstvolgens finally/except/catch block gesprongen wordt. Met die wetenschap moet je denk ik grotendeels zelf bepalen wat je een exception vindt, en wat gewoon een false return waarde is. Vertaal een exception anders: "uitzondering". Een resultaat in je code die eigenlijk niet zou moeten gebeuren, maar als het toch gebeurt, weet je er in ieder geval van.

Ik zou types van exception in niet al teveel detail gaan definieren, omdat het alleen handig is, als je er ook daadwerkelijk afvang-code voor hebt. Aparte typen exceptions kun je apart afvangen en je kunt ze eventueel eigen specifieke properties geven. Voor de eindgebruiker boeit het niets of er nou een (Delphi voorbeeldje) EGraphicError, een EJPEGError of zelfs een EJPEGInvalidHeaderError opgegooit werd. Het laatste geval in mijns inziens overbodig, omdat een EJPEGError een errorcode kan hebben die de specifieke fout aanduidt. Dus niet té ver uitsplitsen zou ik zeggen.

Wat betreft het afvangen van exceptions: ik weet niet hoe iedere taal het doet, maar in Delphi is er een "standaard catch" om elke message heen. Dus als er in het klikken op een knop een exception opgegooit wordt, die niet afgevangen wordt, dan knalt niet het hele programma eruit, maar wordt de "standaard catch" getriggerd (het simpelweg weergeven van de (engelse) message van de exception) en verdere uitvoer afgebroken, maar alleen de message, niet het hele programma of nog meer...

日本!🎌


Acties:
  • 0 Henk 'm!

  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024

Alarmnummer

-= Tja =-

_Thanatos_ schreef op woensdag 15 december 2004 @ 16:17:

Ik zou types van exception in niet al teveel detail gaan definieren, omdat het alleen handig is, als je er ook daadwerkelijk afvang-code voor hebt. Aparte typen exceptions kun je apart afvangen en je kunt ze eventueel eigen specifieke properties geven. Voor de eindgebruiker boeit het niets of er nou een (Delphi voorbeeldje) EGraphicError, een EJPEGError of zelfs een EJPEGInvalidHeaderError opgegooit werd. Het laatste geval in mijns inziens overbodig, omdat een EJPEGError een errorcode kan hebben die de specifieke fout aanduidt. Dus niet té ver uitsplitsen zou ik zeggen.
Het is wel vrij praktisch dat je foutmeldingen gaat afvangen op een bepaald abstractie nivo, ze van informatie gaat voorzien dat op dat abstractie nivo interessant is en de cause exception gaat meegeven.

Je krijgt dan:
Kon file bla.txt niet opslaan, caused by:
Error flushing outputstream, caused by:
error at io chip: 0xAFF3DDE

Ipv alleen die:
error at io chip: 0xAFF3DDE
Wat betreft het afvangen van exceptions: ik weet niet hoe iedere taal het doet, maar in Delphi is er een "standaard catch" om elke message heen. Dus als er in het klikken op een knop een exception opgegooit wordt, die niet afgevangen wordt, dan knalt niet het hele programma eruit, maar wordt de "standaard catch" getriggerd (het simpelweg weergeven van de (engelse) message van de exception) en verdere uitvoer afgebroken, maar alleen de message, niet het hele programma of nog meer...
IN java kan je sinds jdk1.5 gebruik maken van de thread exception handler. Iedere thread kan je een exception handler geven (ik geloof ook de event dispatching thread).

Acties:
  • 0 Henk 'm!

  • whoami
  • Registratie: December 2000
  • Laatst online: 09-08 22:00
MLM schreef op woensdag 15 december 2004 @ 16:10:
Persoonlijk vindt ik dat je Exceptions alleen moet gebruiken in .NET aangezien het framework er ook veelvuldig gebruik van maakt.
Wat is dat nu voor onzin?
Waarom zou je geen excepties in andere talen mogen gebruiken?

IMO:
Excepties gebruik je enkel om echt 'uitzonderlijke' gevallen aan te duiden, situaties die normaal gezien niet mogen vorkomen; daarom heet het ook een exceptie.
Het gooien van excepties is traag, dus ga er niet teveel mee strooien.
Bv: als je wilt checken of een user wel een naam heeft ingevult waar hij dat moest doen, en hij heeft die naam niet ingevuld, dan ga je daar geen exceptie voor gebruiekN.

(wat oisyn dus al lang geleden gezegd heeft. :z * whoami == traag )

[ Voor 6% gewijzigd door whoami op 15-12-2004 16:35 ]

https://fgheysels.github.io/


Acties:
  • 0 Henk 'm!

  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024

Alarmnummer

-= Tja =-

MLM schreef op woensdag 15 december 2004 @ 16:10:
Persoonlijk vindt ik dat je Exceptions alleen moet gebruiken in .NET aangezien het framework er ook veelvuldig gebruik van maakt.
Koel.. ik wist wel dat die exception handling bij delhi, java, c++ allemaal voor geen meter deugde ;)
In principe zijn functies zodanig dat je returnvariable teruggeeft of een functieaanroep geslaagd is, en de in en uitvoer in de parameters zitten. Helaas is dit enigzins utopisch aangezien iedereen andere dingen gebruikt, en lang niet iedereen ook daadwerkelijk returnvalues checkt.
Ik ben het helemaal met je eens.. en als een functie ook nog een normale return waarde heeft, dan sturen we zeker gewoon een object terug? Of laat jij ook alle functies een string of int returnen? En als het bereik van de functie een overlap heeft met de error waarde, dan ach.. dan zoeken ze het maar uit.
Op dat punt zijn exceptions wel nuttig, want als je ze niet cathed dan crasht je programma :D
Die thread die een exception uberhaubt niet afvangt is idd ten dode opgeslagen. Threads en exceptions sucken gewoon hard ;)
Dat is oud nieuws ;)

[ Voor 8% gewijzigd door Alarmnummer op 15-12-2004 16:42 ]


Acties:
  • 0 Henk 'm!

  • _Thanatos_
  • Registratie: Januari 2001
  • Laatst online: 07-08 10:54

_Thanatos_

Ja, en kaal

Het is wel vrij praktisch dat je foutmeldingen gaat afvangen op een bepaald abstractie nivo, ze van informatie gaat voorzien dat op dat abstractie nivo interessant is en de cause exception gaat meegeven.

Je krijgt dan:
Kon file bla.txt niet opslaan, caused by:
Error flushing outputstream, caused by:
error at io chip: 0xAFF3DDE

Ipv alleen die:
error at io chip: 0xAFF3DDE
Een soort stack trace, bedoel je dat? Dat is alleen in managed omgevingen op een nette en betrouwbare manier mogelijk, zoals in Java, .NET en evt in scripttalen. Maar wat je zegt, het is wel degelijk nuttig om meer info te geven, maar teveel info kan ook onzinnig zijn, zoals in het voorbeeld dat ik gaf met de jpeg error.
IN java kan je sinds jdk1.5 gebruik maken van de thread exception handler. Iedere thread kan je een exception handler geven (ik geloof ook de event dispatching thread).
Hmm, ik ken java niet, maar kun je niet gewoon een try..catch..finally statement in een thread hangen? In Delphi kan het iig wel, en als je het niet doet er er treedt een exception op, sja, dan "stopt" je thread gewoon :)

日本!🎌


Acties:
  • 0 Henk 'm!

  • Eelis
  • Registratie: Januari 2003
  • Laatst online: 21-02-2015
.

[ Voor 101% gewijzigd door Eelis op 18-02-2015 19:08 ]


Acties:
  • 0 Henk 'm!

  • FendtVario
  • Registratie: Januari 2002
  • Laatst online: 12-05 22:30

FendtVario

The leader drives Vario!

_Thanatos_ schreef op woensdag 15 december 2004 @ 16:17:
Wat betreft het afvangen van exceptions: ik weet niet hoe iedere taal het doet, maar in Delphi is er een "standaard catch" om elke message heen. Dus als er in het klikken op een knop een exception opgegooit wordt, die niet afgevangen wordt, dan knalt niet het hele programma eruit, maar wordt de "standaard catch" getriggerd (het simpelweg weergeven van de (engelse) message van de exception) en verdere uitvoer afgebroken, maar alleen de message, niet het hele programma of nog meer...
In Delphi kun je gebruik maken van het TApplication.OnException event. Hierin kun je alle niet afgevangen excepties opvangen en er als nog voor zorgen dat niet direct je applicatie wordt afgesloten.

www.fendt.com | Nikon D7100 | PS5


Acties:
  • 0 Henk 'm!

  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024

Alarmnummer

-= Tja =-

_Thanatos_ schreef op woensdag 15 december 2004 @ 16:46:
[...]

Een soort stack trace, bedoel je dat?
Nee.. het is een abstractie trace.Je maakt voor ieder abstractie nivo nieuwe foutmeldingen (strings) die veel info geven over de fout op dat abstractie nivo. De foutmelding op het hoogste abstractie nivo laat je aan de gebruiker zien..maar de andere foutmeldingen zijn erg handig als je de trace afloopt.

Ik chain trouwens ook exceptions.. erg informatief.
Dat is alleen in managed omgevingen op een nette en betrouwbare manier mogelijk, zoals in Java, .NET en evt in scripttalen.
Je kunt van Delphi zeker en vast en zeker ook wel bij c++ een stacktrace ophalen (dus een trace van de stackframes). Dat heeft niet specifiek te maken met managed/unmanaged. Ik heb het zelfs ingebouwd in mijn Prolog compiler.
Hmm, ik ken java niet, maar kun je niet gewoon een try..catch..finally statement in een thread hangen?
Als jij in java beschikking hebt over de sourcecode van een thread dan ja.. Heb je geen beschikking over de source van die thread.. dan nee.. Je kunt dan niet een exception handler toevoegen (tenzij je jdk1.5 gebruikt.. daar kan het weer wel).

[ Voor 3% gewijzigd door Alarmnummer op 15-12-2004 16:52 ]


Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 09-08 12:23

.oisyn

Moderator Devschuur®

Demotivational Speaker

Eelis schreef op woensdag 15 december 2004 @ 16:47:
Een iterator gooit in Python (bij conventie) een exception als hij tegen het eind van een sequence aanloopt, en in Python is dat dus echt de normale manier om een sequence te doorlopen: net zolang doorgaan tot er zo'n exception optreedt.
Mja, ik vind dat dus complete mis-use van exceptions. Waarom is het "tegen het eind van de iterator aanlopen" een uitzonderlijk geval? Waarom kun je daar niet gewoon zelf op checken? Design-flaw, als je het mij vraagt.

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Acties:
  • 0 Henk 'm!

  • _Thanatos_
  • Registratie: Januari 2001
  • Laatst online: 07-08 10:54

_Thanatos_

Ja, en kaal

FendtVario schreef op woensdag 15 december 2004 @ 16:51:
[...]


In Delphi kun je gebruik maken van het TApplication.OnException event. Hierin kun je alle niet afgevangen excepties opvangen en er als nog voor zorgen dat niet direct je applicatie wordt afgesloten.
Dat bedoel ik dus, dat is wat er in het except-block (catch block in java/c++) wordt aangeroepen als er een niet-afgehandelde exception optreedt. OnException wordt vaak gebruikt om het exception-dialoogje te customizen. Er zijn zelf systemen (waarvan ik er een in een commerciele app heb gebruikt) die van dit mechanisme gebruikmaken en een complete stacktrace en disassembly in elkaar hacken :)

日本!🎌


Acties:
  • 0 Henk 'm!

  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 02-08 19:14
In moderne C++ zijn exceptions conceptueel behoorlijk gebruikelijk, het is niet voor niets dat een bekend boek Exceptional C++ heet. Dat wil niet zeggen dat je ze vaak gooit, alleen dat er veel plekken zijn waar het zou kunnen. De hele taal is erop ontworpen: C++ is gebaseerd op het RAII mechanisme, files aflsuiten e.d. gebeurt automatisch door destructors, en die worden oa in reactie op exceptions aangeroepen.

Man hopes. Genius creates. Ralph Waldo Emerson
Never worry about theory as long as the machinery does what it's supposed to do. R. A. Heinlein


Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 09-08 12:23

.oisyn

Moderator Devschuur®

Demotivational Speaker

sidenote: toch mis ik een finally feature in c++, alles in destructors wrappen is ook niet alles.

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Acties:
  • 0 Henk 'm!

  • whoami
  • Registratie: December 2000
  • Laatst online: 09-08 22:00
.oisyn schreef op woensdag 15 december 2004 @ 17:23:
sidenote: toch mis ik een finally feature in c++, alles in destructors wrappen is ook niet alles.
Finally is wel handig, idd, maar het heeft imo niet echt met excepties te maken.
Het is gewoon een blok code die altijd wordt uitgevoerd, als er een exceptie opgetreden is in het bijhorende try block of niet.
Je kan ook perfect een try / finally hebben zonder catch. (Maar dit gebruik je natuurlijk enkel als de code in je try blok geen excepties kan veroorzaken.

https://fgheysels.github.io/


Acties:
  • 0 Henk 'm!

  • Jrz
  • Registratie: Mei 2000
  • Laatst online: 09:41

Jrz

––––––––––––

MLM schreef op woensdag 15 december 2004 @ 16:10:
Persoonlijk vindt ik dat je Exceptions alleen moet gebruiken in .NET aangezien het framework er ook veelvuldig gebruik van maakt.
Ik dacht juist dat .NET geen checked exceptions had..

Ennnnnnnnnn laat losssssssss.... https://github.com/jrz/container-shell (instant container met chroot op current directory)


Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 09-08 12:23

.oisyn

Moderator Devschuur®

Demotivational Speaker

whoami schreef op woensdag 15 december 2004 @ 17:27:
[...]


Finally is wel handig, idd, maar het heeft imo niet echt met excepties te maken.
Het is gewoon een blok code die altijd wordt uitgevoerd, als er een exceptie opgetreden is in het bijhorende try block of niet.
Je kan ook perfect een try / finally hebben zonder catch. (Maar dit gebruik je natuurlijk enkel als de code in je try blok geen excepties kan veroorzaken.
dan snap je het nut van finally denk ik niet, finally is er om exception-safe code te schrijven. Juist omdat het altijd wordt aangeroepen, ookal wordt er een exception gegooid, hoort opruimcode in een finally block, zodat het opruimmechanisme nog steeds werkt ondanks een eventuele gegooide exception. C++ kent geen finally, dus dan zit je je opruimcode te wrappen in een destructor van een class, maar dan heb je weer geen toegang tot de locale variabele van de functie (zodat die weer meegegeven moeten worden aan de instantie die je op de stack in die functie zet)

[ Voor 6% gewijzigd door .oisyn op 15-12-2004 17:33 ]

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Acties:
  • 0 Henk 'm!

  • Johannes
  • Registratie: Juni 2000
  • Laatst online: 09-07 21:03
.oisyn schreef op woensdag 15 december 2004 @ 16:52:
[...]


Mja, ik vind dat dus complete mis-use van exceptions. Waarom is het "tegen het eind van de iterator aanlopen" een uitzonderlijk geval? Waarom kun je daar niet gewoon zelf op checken? Design-flaw, als je het mij vraagt.
Volgens mij wijzen de vragen die je stelt niet op een design-flaw. Waarom zou je exceptions volgens jou alleen in uitzonderlijke gevallen moeten gebruiken? Pas als dat duidelijk is kun je beoordelen of het een design-flaw is (waar ik het, full disclosure, overigens niet mee eens ben).

Overigens kom je StopIteration als Python-programmeur bijna nooit tegen. Alleen als je zelf expliciet next() uit het iterator-protocol aanroept, dan moet je op StopIteration letten. Dat is iets wat Python in for-loops standaard voor je doet.

Uit volle borst op weg naar nergens / Zonder reden zonder doel
Met m'n zeden en m'n zonden / En mijn angstig voorgevoel
Laat mij mijn kont tegen de krib / Laat mij dit goddeloze lied
Hef jij je handen maar ten hemel / Maar red mij niet


Acties:
  • 0 Henk 'm!

  • FendtVario
  • Registratie: Januari 2002
  • Laatst online: 12-05 22:30

FendtVario

The leader drives Vario!

@_Thanatos_: Ok, dat was niet helemaal duidelijk. Bedoel je met dat systeem misschien toevallig madExcept?

Ik heb dacht ik ook een keer gelezen dat je een hook op een globale exception listener van windows kan zetten, maar daarover kan ik nu niets meer terug vinden.

Terug on topic @Michali: Ik denk dat je geen exceptions moet gebruiken als je een keuze hebt in de te nemen actie na het uitvoeren van een handeling. Voorbeeldje:

je laat een gebruiker zoeken naar een opgeslagen relatie binnen een klanten systeem. Als de relatie niet gevonden wordt gooi je geen exceptie, maar laat je een bericht zien dat de relatie niet gevonden kan worden. Helaas, jammer laat 'm nog maar een keer proberen.

Maar stel nu dat je een taak start die gegevens over deze relatie nodig heeft. Door een foutje kan de relatie niet gevonden worden maar je hebt als gebruiker geen invloed meer op de flow. Dan zou je een exception kunnen gebruiken om de flow te doorbreken en de taak af te breken. (kan even niet een beter voorbeeld verzinnen).

Als ik exceptions opgooi is dat vaak alleen in code bibliotheken voor algemeen gebruik die standaard taken uitvoeren. In de applicatie zelf probeer ik ze alleen op te vangen.

www.fendt.com | Nikon D7100 | PS5


Acties:
  • 0 Henk 'm!

  • Michali
  • Registratie: Juli 2002
  • Laatst online: 29-05 22:54
Alarmnummer schreef op woensdag 15 december 2004 @ 16:17:
Dat is onmogelijk. Stel dat jij vraagt aan jouw hd.. is jouw arm al afgebroken? En hij returned false.. en nog geen microseconde erna breekt de arm eraf... Wat dan? :)
Ja dat weet ik ook wel, maar over dat soort typen exceptions had ik het niet eigenlijk:
En dan heb ik het met name over exceptions die je zelf schrijft en throwed, dus niet zozeer een exception die je zou kunnen verwachten bij het inlezen van een resource oid.
Veel nuttige stof al, even kijken of ik nog ergens vragen over heb.

Ik heb overigens al zelf ook wel een zeer nuttige feature van exceptions ondekt, en dat is als je je applicatie in veel lagen opbouwd. Ik probeer namelijk via een data access laag een object in de database te stoppen via een database abstractie laag. Nu kan het wel eens fout gaan in die laag (door bijvoorbeeld een unique key waarvoor een duplicate waarde wordt gegeven). Dan wil ik de exception die dan wordt gegooid niet direct afvangen in de data access laag, maar stuur ik hem direct door met een nieuwe exception. Zo kan ik in de applicatie laag de error afhandelen met wat dan ook.

Noushka's Magnificent Dream | Unity


Acties:
  • 0 Henk 'm!

  • drm
  • Registratie: Februari 2001
  • Laatst online: 09-06 13:31

drm

f0pc0dert

whoami:
Finally is wel handig, idd, maar het heeft imo niet echt met excepties te maken.
't Heeft alles met excepties te maken. Juist het sluiten van een file handle (bijvoorbeeld) in een finally verzekert je van het feit dat je niet met loshangende draadjes zit in je systeem, ondanks het feit dat er iets "exceptioneels" is gebeurt. Anders gezegd: als een method een exceptie opgooit, betekent dat gewoon dat hij tegen je zegt: "Ik weet 't niet meer, zoek jij het maar uit". Als de aanroepende methode het dan ook niet meer weet, maar nog wel even wat dingen op moet lossen voordat de exceptie doorgegooid wordt (wat tenslotte default behaviour is, hence de verplichting een throws in je method signature op te nemen als je excepties niet expliciet afvangt), kan je dat alleen oplossen door een finally te gebruiken. Zonder exceptions had je die hele finally niet nodig gehad, want dan werd de exceptie ook niet verder "naar boven" opgeworpen.

Music is the pleasure the human mind experiences from counting without being aware that it is counting
~ Gottfried Leibniz


Acties:
  • 0 Henk 'm!

  • drm
  • Registratie: Februari 2001
  • Laatst online: 09-06 13:31

drm

f0pc0dert

Michali:
Dan wil ik de exception die dan wordt gegooid niet direct afvangen in de data access laag, maar stuur ik hem direct door met een nieuwe exception. Zo kan ik in de applicatie laag de error afhandelen met wat dan ook.
Dat "doorgooien" met nieuwe informatie is wat Alarmnummer volgens mij ook bedoelde met zijn "abstractie-trace"

Music is the pleasure the human mind experiences from counting without being aware that it is counting
~ Gottfried Leibniz


Acties:
  • 0 Henk 'm!

  • whoami
  • Registratie: December 2000
  • Laatst online: 09-08 22:00
.oisyn schreef op woensdag 15 december 2004 @ 17:31:
[...]


dan snap je het nut van finally denk ik niet, finally is er om exception-safe code te schrijven. Juist omdat het altijd wordt aangeroepen, ookal wordt er een exception gegooid, hoort opruimcode in een finally block, zodat het opruimmechanisme nog steeds werkt ondanks een eventuele gegooide exception. C++ kent geen finally, dus dan zit je je opruimcode te wrappen in een destructor van een class, maar dan heb je weer geen toegang tot de locale variabele van de functie (zodat die weer meegegeven moeten worden aan de instantie die je op de stack in die functie zet)
Hum ja, je hebt gelijk.
Als je geen exceptie hebt, heb je idd geen behoefte aan die finally. :z
* whoami zit te suffen.

https://fgheysels.github.io/


Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 09-08 12:23

.oisyn

Moderator Devschuur®

Demotivational Speaker

Johannes schreef op woensdag 15 december 2004 @ 17:34:
Volgens mij wijzen de vragen die je stelt niet op een design-flaw. Waarom zou je exceptions volgens jou alleen in uitzonderlijke gevallen moeten gebruiken?
Op die vraag heb ik allang antwoord gegeven, en ik denk dat dat ook de concensus is van deze draad. Daarbij zeg ik dat ik vind dat ze het in Python voor het verkeerde doeleinde gebruiken, evenals in Java in het geval van bijv. het aanroepen van een functie op een null object. Het zijn program errors, dingen die je in de debugfase zou moeten detecteren. Vandaar dat ik zei dat een assert beter voldoet, in release zou het van mij keihard mogen crashen

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Acties:
  • 0 Henk 'm!

  • Michali
  • Registratie: Juli 2002
  • Laatst online: 29-05 22:54
Ok even kijken of ik het goed begrijp. Je kunt dus exceptions goed toepassen als:
  • er meerdere zaken fout kunnen gaan in hetzelfde stukje code en je speciefieke informatie wilt meegeven per fout.
  • je een fout een of meerdere lagen hoger wilt kunnen afhandelen zonder je druk te maken waar.
Ik moet weg nu. Ik zal straks even kijken of ik nog meer duidelijke punten kan vinden. Wordt nog iets uitgebreid dus.

Noushka's Magnificent Dream | Unity


Acties:
  • 0 Henk 'm!

  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024

Alarmnummer

-= Tja =-

Michali schreef op woensdag 15 december 2004 @ 17:56:
Ok even kijken of ik het goed begrijp. Je kunt dus exceptions goed toepassen als:
Je kunt exceptions toepassen als er iets gebeurt dat een uitzondering is en waar je je normale programma flow niet mee wilt verzieken. Bij een exception wordt namelijk de exception flow geactiveerd...

Acties:
  • 0 Henk 'm!

  • drm
  • Registratie: Februari 2001
  • Laatst online: 09-06 13:31

drm

f0pc0dert

.oisyn:
Vandaar dat ik zei dat een assert beter voldoet, in release zou het van mij keihard mogen crashen
Klopt, daar zijn ze het bij Java inderdaad ook al wel een tijdje mee eens.

Music is the pleasure the human mind experiences from counting without being aware that it is counting
~ Gottfried Leibniz


Acties:
  • 0 Henk 'm!

Verwijderd

.oisyn schreef op woensdag 15 december 2004 @ 17:50:
Op die vraag heb ik allang antwoord gegeven, en ik denk dat dat ook de concensus is van deze draad. Daarbij zeg ik dat ik vind dat ze het in Python voor het verkeerde doeleinde gebruiken, evenals in Java in het geval van bijv. het aanroepen van een functie op een null object. Het zijn program errors, dingen die je in de debugfase zou moeten detecteren. Vandaar dat ik zei dat een assert beter voldoet, in release zou het van mij keihard mogen crashen
Ik vind dit een beetje tendentieus, een iterator is niets anders dan een andere manier van tegen indexing aankijken, dus je zou dan ook moeten stellen dat out-of-bounds exceptions foute boel zijn. Het is volgens mij meer een kwestie van filosofie, enerzijds is exceptions alleen voor runtime errors werpen conceptueel "cleaner", maar anderzijds kun je betere veiligheid/dataintegriteit garanderen als je onder elke foutconditie een exception werpt ("horrible death" komt veel minder vaak voor bij stack unwinding + destructie dan bij een brute _exit(1) of een jmp 0x0).

Acties:
  • 0 Henk 'm!

  • joepP
  • Registratie: Juni 1999
  • Niet online
whoami schreef op woensdag 15 december 2004 @ 17:41:
Hum ja, je hebt gelijk.
Als je geen exceptie hebt, heb je idd geen behoefte aan die finally. :z
* whoami zit te suffen.
Niet mee eens. Als je een procedure hebt die je op meerdere plaatsen zou willen verlaten is finally wel degelijk handig. Zeer handig zelfs.

Acties:
  • 0 Henk 'm!

  • farlane
  • Registratie: Maart 2000
  • Laatst online: 31-07 21:30
FlowinG schreef op woensdag 15 december 2004 @ 16:13:
In een stricte programmeer taal moet je vaak een vooraf gedefineerde waarde retourneren. Bijvoorbeeld een string of een array. Zodra je hier van gaat afwijken door een boolean (false) af te geven met je imho niet echt netjes bezig. Het beste kan je dan null terug geven.
Kolder.

Somniferous whisperings of scarlet fields. Sleep calling me and in my dreams i wander. My reality is abandoned (I traverse afar). Not a care if I never everwake.


Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 09-08 12:23

.oisyn

Moderator Devschuur®

Demotivational Speaker

Verwijderd schreef op woensdag 15 december 2004 @ 18:25:
dus je zou dan ook moeten stellen dat out-of-bounds exceptions foute boel zijn.
Dat is idd wat ik eerder in deze draad al gezegd heb ja ;)
.oisyn in "[alg] Exceptions, wanneer te gebruiken?"

Het zijn programmer errors, daar horen (imho) geen exceptions voor gebruikt te worden.

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Acties:
  • 0 Henk 'm!

  • Soultaker
  • Registratie: September 2000
  • Laatst online: 00:54
.oisyn schreef op woensdag 15 december 2004 @ 16:52:
Mja, ik vind dat dus complete mis-use van exceptions. Waarom is het "tegen het eind van de iterator aanlopen" een uitzonderlijk geval? Waarom kun je daar niet gewoon zelf op checken? Design-flaw, als je het mij vraagt.
Je kunt niet zomaar een null-pointer returnen, zoals je in C zou doen, omdat collecties in Python niet getypeerd zijn (wat gebruikelijk is in een dynamisch getypeerde taal) en Python bovendien geen pointers kent. None (het Python-equivalent van een null-pointer) is dus gewoon een geldige return value van de next-methode (als er in je collectie dus een None-waarde zit).

Je kunt wel alternatieven verzinnen, zoals het retourneren van een tuple met een boolean (die succes aan geeft) en een waarde, of dat next altijd een 1-tuple retourneert als er een waarde is en None bij het einde, maar dat maakt het gebruik helemaal niet makkelijker. Code als deze werkt dan simpelweg niet:
Python:
1
(value,) = iterator.next()

Want als iterator.next() None returned kan de tupel niet 'uitgepakt' worden en dan krijg je daar ook een (andere) exception van. Je moet dan expliciet zoiets doen:
Python:
1
2
3
result = iterator.next()
if result <> None:
   (value,) = result

Ook niet echt fijn in het gebruik.

Tenslotte, en misschien wel het belangrijkste, hoor je als normale gebruiker die StopIteration-exception nooit (meer) te gebruiken. Het is een implementatie-detail dat wel te observeren is (en dat, bij gebrek aan een standaard, wel onderdeel is van de standaardimplementatie) maar veel meer ook niet.

Als 'gebruiker' van een iterator gebruik je in 99,9% van de gevallen gewoon een for-lus ("for x in iterator:"); ik kan me niet herinneren in Python ooit iets anders gedaan te hebben. Als 'aanbeider' van de iterator hoor je tegewoordig een generator te gebruiken; dat is gewoon een functie die van een speciale constructie gebruik maakt om waarden op te leveren (een constructie die ik niet in andere talen ben tegengekomen):
Python:
1
2
3
def kwadraten(begin, eind):
  for n in range(begin, eind):
    yield n*n

Deze generator levert de kwadraten van de getallen tussen 'begin' en 'eind'; je gebruikt 'm bijvoorbeeld zo: (en dat print 4, 9, 16, 25, want 6 valt buiten de range)
code:
1
2
for m in kwadraten(2, 6):
  print m

Python code hoort zo te zijn: geen exception meer te zien. Alleen als je per se op implementatie (denk: virtual machine) nivo met iterators bezig wil gaan, hoef je te weten dat zo'n generator een object retourneerd met een next() methode die een exception kan gooien.

Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 09-08 12:23

.oisyn

Moderator Devschuur®

Demotivational Speaker

Soultaker schreef op woensdag 15 december 2004 @ 18:50:
Je kunt niet zomaar een null-pointer returnen, zoals je in C zou doen, omdat collecties in Python niet getypeerd zijn (wat gebruikelijk is in een dynamisch getypeerde taal) en Python bovendien geen pointers kent. None (het Python-equivalent van een null-pointer) is dus gewoon een geldige return value van de next-methode (als er in je collectie dus een None-waarde zit).
Ik denk dat je m'n punt niet helemaal begrijpt. Je gebruikt een stukje code op een verkeerde manier, wat helaas alleen @ runtime gedetecteerd kan worden. Een exception lijkt mij echter niet de goede keuze, maar je zou ook gewoon een error kunnen tonen en exiten. Zoals een assert doet dus. Tevens is een assert niet van buiten-af af te vangen, wat in feite ook niet zou mogen. Als je een functie met verkeerde parameters aanroept dan kun je wel een IllegalArgumentException kunnen afvangen en daarop kunnen reageren, maar wat je eigenlijk moet doen is gewoon zorgen dat je de goede parameters geeft.

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Acties:
  • 0 Henk 'm!

  • Soultaker
  • Registratie: September 2000
  • Laatst online: 00:54
Ik begrijp je punt en ben het zeker met je eens. Je reageerde concreet op een stelling die iets zei in de trant van "zo werken we in Python met exceptions" en ik wilde vooral beargumenteren dat die truc met de iterator meer een hack is om het op laag nivo werkend te krijgen en niet zozeer een aangewezen methode om in Python met exceptions om te gaan. Zo horen exceptions in Python (maar ook in andere talen) dus niet gebruikt te worden. (Eigenlijk reageerde ik dus meer op Eelis dan op jou.)

Overigens is het indexen van een array buiten zijn bounds in C/C++ undefined behaviour, dus dan mag de compiler best een array-out-of-bounds exception gooien. Bij het debuggen zou dat best eens handig kunnen zijn. Verder ga je er vanuit dat een programma correct is in de zin dat het deterministisch is en geen gebruik maakt van undefined behaviour, dus is het feit dat het programma een fout bevat in die zin zeker wel exceptioneel (en niet in de betekenis dat het weinig voorkomt ;)). Het is zeker niet de bedoeling om undefined behaviour aan te roepen.

In een 'normaal' programma ga je die out-of-bounds exceptions natuurlijk niet zitten catchen. Dan kun je beter je programma fixen. Op een platform dat garandeert dat die exceptions gegooid worden (wat alleen wenselijk is als dat niet tenkoste gaat van de performance, zoals in Java en .NET, waar array access toch bijna altijd gechecked moet worden) kun je ze eventueel ook nog wel eens functioneel gebruiken in een programma, maar dat is hoogstwaarschijnlijk onnodig en getuigt nogal van slechte stijl.

[ Voor 3% gewijzigd door Soultaker op 15-12-2004 19:05 ]


Acties:
  • 0 Henk 'm!

  • _Thanatos_
  • Registratie: Januari 2001
  • Laatst online: 07-08 10:54

_Thanatos_

Ja, en kaal

FendtVario schreef op woensdag 15 december 2004 @ 17:36:
@_Thanatos_: Ok, dat was niet helemaal duidelijk. Bedoel je met dat systeem misschien toevallig madExcept?
Ja die bedoelde ik, kon er ff niet op komen... erg fijn pakket is dat :)

日本!🎌


Acties:
  • 0 Henk 'm!

  • MLM
  • Registratie: Juli 2004
  • Laatst online: 12-03-2023

MLM

aka Zolo

Nogal wat commentaar :P
Zal even wat verduidelijken
Wat is dat nu voor onzin?
Waarom zou je geen excepties in andere talen mogen gebruiken?
Dat mag wel, alleen .NET wil nog wel eens excepties gooien.
Koel.. ik wist wel dat die exception handling bij delhi, java, c++ allemaal voor geen meter deugde
Dat zeg ik niet, maar dat doet het wel :D
Ik dacht juist dat .NET geen checked exceptions had..
zie System.Exception class, alle exceptions die je in managed code kan afvangen zijn afgeleid van deze class, dus die heb je wel.

Ik zou zeggen programmeer eens wat in C# of managed C++, je zult zien dat exceptions best veelvoorkomend zijn. (ieg meer als in C++, ik heb daar heel lang in geprogt en nooit een exceptionhandler gemaakt (blame me :o), maar in C# waar ik sinds kort in prog, heb ik ongeveer overal wel één of meer "actieve" exceptionhandler)

-niks-


Acties:
  • 0 Henk 'm!

  • whoami
  • Registratie: December 2000
  • Laatst online: 09-08 22:00
MLM schreef op woensdag 15 december 2004 @ 19:43:
Nogal wat commentaar :P
Zal even wat verduidelijken

[...]

Dat mag wel, alleen .NET wil nog wel eens excepties gooien.

[...]

Dat zeg ik niet, maar dat doet het wel :D
Ik snap totaal nietwat je bedoeld.
.NET gooit wel eens excepties... Ja, als er iets foutloopt.
Maar dat doet de VCL ook.
En ik snap niet waarom je zegt dat excepties in Delphi, C++, etc... niet goed zijn..
zie System.Exception class, alle exceptions die je in managed code kan afvangen zijn afgeleid van deze class, dus die heb je wel.
Dat heeft niets met checked exceptions te maken.
'Checked Exceptions' heb je in Java; checked exceptions zijn exceptions die je moet afvangen. In de method signature is aangegeven welke excepties die method kan throwen en die moet je afvangen, anders compiled het boeltje niet.
Ik zou zeggen programmeer eens wat in C# of managed C++, je zult zien dat exceptions best veelvoorkomend zijn.
Ok, zal ik doen. O-) :z

[ Voor 12% gewijzigd door whoami op 15-12-2004 20:34 ]

https://fgheysels.github.io/


Acties:
  • 0 Henk 'm!

  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024

Alarmnummer

-= Tja =-

MLM schreef op woensdag 15 december 2004 @ 19:43:
Nogal wat commentaar :P
Zal even wat verduidelijken

Dat mag wel, alleen .NET wil nog wel eens excepties gooien.

Dat zeg ik niet, maar dat doet het wel :D
En ik vind dat je maar een flink stuk uit je nek loopt te lullen.

Wat voor betere alternatieven weet jij dan? Of weet je geen alternatief? Vind jij systemen zonder exceptions ook al goed genoeg? Komt het
a) doordat zo slim bent dat jij zonder kunt
b) te ontwetend bent om te realiseren wat exceptions voor toegevoegde waarde hebben.

[ Voor 31% gewijzigd door Alarmnummer op 15-12-2004 20:55 ]


Acties:
  • 0 Henk 'm!

Verwijderd

.oisyn schreef op woensdag 15 december 2004 @ 18:50:
Dat is idd wat ik eerder in deze draad al gezegd heb ja ;)
.oisyn in "[alg] Exceptions, wanneer te gebruiken?"

Het zijn programmer errors, daar horen (imho) geen exceptions voor gebruikt te worden.
Ik las niks over out-of-bounds, maar anyway, ik bedoelde het gebruik van exceptions als "2e lijns verdediging" tegen dataverlies ed. Als een programmeur een out-of-bounds index of een nullpointer niet afvangt voor hij geadresseerd wordt, dan heb je een buffer overflow of segfaulting code zonder exceptions; met exceptions doet de applicatie nog steeds niet wat er van verwacht wordt, maar de kans op dataverlies is veel kleiner omdat alle objecten op de stack fatsoenlijk opgeruimd worden voor zover dat mogelijk is en daarnaast is er ook nog eens veel minder kans op veiligheidslekken omdat out-of-bounds schrijven onmogelijk wordt. Uit dat oogpunt is een uncaught exception dus zelfs beter dan een failed assertion, want assert() doet in het algemeen niet meer dan een brute _exit() en dat is lang niet zo lief voor de data op de stack.

Ik pleit er dus niet voor dat een programmeur die exceptions gaat gebruiken als een alternatieve manier van program flow, maar op het moment dat er iets mis gaat dat een programmeur vergeet af te vangen, zijn dit soort exceptions een zegen (en dat gebeurt vrijwel altijd in grote projecten).

Acties:
  • 0 Henk 'm!

  • Johannes
  • Registratie: Juni 2000
  • Laatst online: 09-07 21:03
.oisyn schreef op woensdag 15 december 2004 @ 17:50:
[...]


Op die vraag heb ik allang antwoord gegeven, en ik denk dat dat ook de concensus is van deze draad.
Tja, die consensus begrijp ik dan ook niet. Ook op de Portland Pattern
Repository (http://c2.com/cgi/wiki?ExceptionPatterns) adviseren een aantal programmeer/XP-goden om excepties zoveel mogelijk te vermijden. Ik ga er van uit dat zij en jij als ervaren programmeurs wel iets slims te zeggen hebben, dus probeer ik te begrijpen waarom je dit zegt.

Laat ik, om de discussie wat concreter te maken, een stukje code die ik recentelijk heb geschreven pakken (complete module):

Python:
1
2
3
4
5
def verifyLogin(self, username, password):
    try:
        self.client.userid = self.db.user.lookup(username)
    except KeyError:
        raise exceptions.LoginError, self._('Invalid login')
deze functie wordt op twee verschillende plekken aangeroepen. De volgende code
is er één van.
Python:
1
2
3
4
5
6
try:
    self.get_action_class('login')(self).verifyLogin(username, password)
except LoginError, err:
    self.make_user_anonymous()
    self.response_code = 403
    raise Unauthorised, err
Nu is mijn vraag: hoe zou jij dit herschrijven (en dan gaat het me alleen om
LoginError, niet om de andere excepties)? En natuurlijk: waarom vind jij jouw
versie beter? Overigens zou ik ook graag versies van anderen zien.

Uit volle borst op weg naar nergens / Zonder reden zonder doel
Met m'n zeden en m'n zonden / En mijn angstig voorgevoel
Laat mij mijn kont tegen de krib / Laat mij dit goddeloze lied
Hef jij je handen maar ten hemel / Maar red mij niet


  • Eelis
  • Registratie: Januari 2003
  • Laatst online: 21-02-2015
.

[ Voor 115% gewijzigd door Eelis op 18-02-2015 19:09 ]


  • Johannes
  • Registratie: Juni 2000
  • Laatst online: 09-07 21:03
Eelis schreef op donderdag 16 december 2004 @ 00:24:
[...]

Hmm, ik heb Python geleerd uit O'Reilly's Python in a Nutshell, en de auteur daarvan schreef dat het wel degelijk een stukje Python-"filosofie" was dat exceptions veel vaker en zeker niet alleen voor uitzonderlijke gevallen gebruikt werden. Volgens hem was het iterator geval juist een sprekend voorbeeld hiervan. Maar goed, misschien is hij dan niet representatief voor de Python community :).
Alex Martelli is zeker wel representatief voor de Python community. Hij is zelfs een van de drie python-list/comp.lang.python "bots": de martellibot, de timbot (Tim Peters) en effbot (Fredrik Lundh). Ik denk niet dat je de Python community op GoT moet zoeken. ;)

[ Voor 3% gewijzigd door Johannes op 16-12-2004 00:41 ]

Uit volle borst op weg naar nergens / Zonder reden zonder doel
Met m'n zeden en m'n zonden / En mijn angstig voorgevoel
Laat mij mijn kont tegen de krib / Laat mij dit goddeloze lied
Hef jij je handen maar ten hemel / Maar red mij niet


  • Soultaker
  • Registratie: September 2000
  • Laatst online: 00:54
Ik kom meer uit de statisch getypeerde programmeerwereld, dus misschien is mijn gebruik van Python anders dan dat van de gemiddelde Python-programmeur. Ik zou code zoals die van Johannes dan ook nooit zo schrijven:
Johannes schreef op woensdag 15 december 2004 @ 21:53:
Laat ik, om de discussie wat concreter te maken, een stukje code die ik recentelijk heb geschreven pakken:
Python:
1
2
3
4
5
def verifyLogin(self, username, password):
    try:
        self.client.userid = self.db.user.lookup(username)
    except KeyError:
        raise exceptions.LoginError, self._('Invalid login')
deze functie wordt op twee verschillende plekken aangeroepen. De volgende code
is er één van.
Python:
1
2
3
4
5
6
try:
    self.get_action_class('login')(self).verifyLogin(username, password)
except LoginError, err:
    self.make_user_anonymous()
    self.response_code = 403
    raise Unauthorised, err
Nu is mijn vraag: hoe zou jij dit herschrijven (en dan gaat het me alleen om
LoginError, niet om de andere excepties)? En natuurlijk: waarom vind jij jouw
versie beter? Overigens zou ik ook graag versies van anderen zien.
Ik zou dit niet doen, omdat dit geen exceptionele situatie is. De KeyError van de dictionary vind ik gerechtvaardigd, om een vergelijkbare reden als de iterator: het gebruik van de index-operator wordt simpelweg te complex als er een tupel geretourneerd zou worden. Ik vind bovendien dat er geen 'impliciete' waarde moet zijn voor een niet-gedefineerde index, dus de exception is wat mij betreft correct: het indexeren op een ongeldige key is van dezelfde orde als het indexeren van een array buiten zijn grenzen.

Ik zou dan zelf ook geen exception gooien, maar gewoon een status returnen.
Python:
1
2
3
4
5
6
def verifyLogin(self, username, password):
    if username in self.db.user:
        self.client.userid = self.db.user[username]
        return True
    else:
        return False

Het is wat lastig om de aanroep ook om te schrijven, omdat ik de context niet ken. Ik zou waarschijnlijk ook later geen exception raisen, maar alleen de status instellen. Het is wat mij betreft in ieder geval onlogisch om enerzijds een exception te gooien, maar anderzijds de status code aan te passen. Ofwel je handelt een exception af, ofwel je raist 'm en blijft verder overal vanaf. Als je dan toch een exception gooit, laat dan de caller beslissen om de gebruiker anoniem te maken en de status code in te stellen.

Als het doel van de hoofdmethode is om de gebruiker en status code in te stellen, doe dat dan, en raise geen exception. Als een user niet ingelogd is, is het immers normaal om de status code in te stellen en te retourneren. Daar is niets abnormaals aan en ik zou absoluut niet weten waarom de caller daar wat speciaal zou moeten doen.

Denk eraan dat je met een exception de caller dwingt om een actie te ondernemen, om te voorkomen dat het hele programma in de soep loopt. Dat moet je alleen doen als daar een dringende reden voor is; een reden die de caller niet redelijkerwijs zelf kan waarnemen en die relevant is omdat de caller niet verder kan werken zonder het (exceptionele) probleem op te lossen. Als een (goede) programmeur een return value negeert, dan zal 'ie dat niet zonder reden doen: dan interesseert het hem blijkbaar niet wat het resultaat is, op uitzonderingen na. Dwing de programmeur dus niet om onderscheid te maken in gevallen die misschien wel niet relevant voor hem zijn, zoals in dit geval de vraag of de gebruiker wel of niet geauthenticeerd is.

Een tweede belangrijke vraag die je je moet stellen, is of het zinnig is dat een exception gepropageerd wordt. Als een exception alleen op praktische wijze kan worden afgehandeld door de directe caller (en dus niet lager in de call chain) dan is het waarschijnlijk niet zinnig om een exception te gebruiken.

[ Voor 17% gewijzigd door Soultaker op 16-12-2004 02:22 ]


  • MSalters
  • Registratie: Juni 2001
  • Laatst online: 02-08 19:14
.oisyn schreef op woensdag 15 december 2004 @ 17:31:
C++ kent geen finally, dus dan zit je je opruimcode te wrappen in een destructor van een class, maar dan heb je weer geen toegang tot de locale variabele van de functie (zodat die weer meegegeven moeten worden aan de instantie die je op de stack in die functie zet)
Dat is opzettelijk. Als je in een finally, bij het opruimen interactie tussen variabelen nodig hebt, dan wijst dat meestal op een slecht OO design. Een object moet zelf verantwoordelijk zijn voor het zichzelf opruimen.

Overigens kun je in C++ een ScopeGuard class maken, die praktisch gezien als een finally block werkt. Een extra voordeel is dat je niet de lange en indented try { ... } finally { ... } constuctie nodig hebt.

Man hopes. Genius creates. Ralph Waldo Emerson
Never worry about theory as long as the machinery does what it's supposed to do. R. A. Heinlein


  • Johannes
  • Registratie: Juni 2000
  • Laatst online: 09-07 21:03
Soultaker schreef op donderdag 16 december 2004 @ 02:15:
Ik kom meer uit de statisch getypeerde programmeerwereld, dus misschien is mijn gebruik van Python anders dan dat van de gemiddelde Python-programmeur. Ik zou code zoals die van Johannes dan ook nooit zo schrijven:
Kan ik op zich begrijpen. Ik postte juist deze code omdat ik op dit stuk zelf ook zat te twijfelen of ik wel excepties zat te gebruiken.
Ik zou dan zelf ook geen exception gooien, maar gewoon een status returnen.
Python:
1
2
3
4
5
6
def verifyLogin(self, username, password):
    if username in self.db.user:
        self.client.userid = self.db.user[username]
        return True
    else:
        return False
Triviale zaken eerst: self.db.user is geen dictionary, maar een object van klasse Class (ja, de terminologie wil nog wel eens verrassend wezen). Deze heeft geen __contains__ method dus 'username in self.db.user' werkt niet. Misschien wel aardig om eens te implementeren.

Het wordt dus (note het gebruik van het Guard Clause pattern, trouwens):
Python:
1
2
3
4
5
6
def verifyLogin(self, username, password):
    try:
        self.client.userid = self.db.user.lookup(username)
    except KeyError:
        return False
    return True

Maar dan mis ik nog mijn self._('Login error'). Deze error gaat uiteindelijk naar de user van de web interface toe. Nou zou je kunnen denken: laat dat over aan de caller van verifyLogin. Maar er zijn meerdere redenen waarom verifyLogin kan mislukken: 'Login error' of 'You do not have permission to login'. Dus zou je meerdere status-codes moeten meegeven om hiertussen onderscheid te maken. Ook betekent dit dat elke caller de status-codes om moet gaan zetten naar error-messages. Dit is dus geen realistisch alternatief.

Het enige praktische alternatief dat ik kan bedenken gaat ongeveer als volgt:
Python:
1
2
3
4
5
6
7
def verifyLogin(self, username, password):
    try:
        self.client.userid = self.db.user.lookup(username)
    except KeyError:
        self.error_message = self._('Login Error')
        return False
    return True

Dan krijg je als caller:
Python:
1
2
3
4
5
login_action = self.get_action_class('login')(self)
if not login_action.verifyLogin(username, password):
    self.make_user_anonymous()
    self.response_code = 403
    raise Unauthorised, login_action.error_message

wat ik op zich een redelijke oplossing vind, maar het probleem hiermee is dat je zelf een plaats verzint waar de error messages te vinden zijn. Dat is weer iets wat je moet documenteren en dat de gebruiker van je functie moet uitzoeken. Als je documenteert dat je een exceptie kan raisen, dan weet elke redelijke Python-programmeur hoe hij de error-message kan vinden (namelijk met str(err)).
Het is wat lastig om de aanroep ook om te schrijven, omdat ik de context niet ken. Ik zou waarschijnlijk ook later geen exception raisen, maar alleen de status instellen. Het is wat mij betreft in ieder geval onlogisch om enerzijds een exception te gooien, maar anderzijds de status code aan te passen. Ofwel je handelt een exception af, ofwel je raist 'm en blijft verder overal vanaf. Als je dan toch een exception gooit, laat dan de caller beslissen om de gebruiker anoniem te maken en de status code in te stellen.
Ik zou het hier graag mee eens of oneens zijn, maar de rest van de code werkt nu eenmaal met excepties, dus kan ik hier verder weinig doen.

Uit volle borst op weg naar nergens / Zonder reden zonder doel
Met m'n zeden en m'n zonden / En mijn angstig voorgevoel
Laat mij mijn kont tegen de krib / Laat mij dit goddeloze lied
Hef jij je handen maar ten hemel / Maar red mij niet


  • drm
  • Registratie: Februari 2001
  • Laatst online: 09-06 13:31

drm

f0pc0dert

MSalters:
Dat is opzettelijk. Als je in een finally, bij het opruimen interactie tussen variabelen nodig hebt, dan wijst dat meestal op een slecht OO design.
Is dat zo :? 't Kan best zijn dat de spullen die opgeruimd moeten worden gewoon method-lokale (hulp)variabelen waren, toch? Waarom moet 't object zelf daar alle details over weten, als 't in de methodscope gedefinieerd is?

Music is the pleasure the human mind experiences from counting without being aware that it is counting
~ Gottfried Leibniz


  • _Thanatos_
  • Registratie: Januari 2001
  • Laatst online: 07-08 10:54

_Thanatos_

Ja, en kaal

drm schreef op donderdag 16 december 2004 @ 10:53:
[...]
Is dat zo :? 't Kan best zijn dat de spullen die opgeruimd moeten worden gewoon method-lokale (hulp)variabelen waren, toch? Waarom moet 't object zelf daar alle details over weten, als 't in de methodscope gedefinieerd is?
Persies, een heel simpel voorbeeldje hiervan:
Delphi:
1
2
3
4
5
6
7
8
for I := 0 to 9
do List.Add(TObject.Create);
try
  { Doe iets... }
finally
  for I := 0 to List.Count - 1
  do List[I].Free;
 end;

De hulpvar I wordt gewoon in de finally gebruikt om alle objecten in de lijst weer netjes vrij te geven, omdat de lijst dat zelf toevallig niet kan/mag. Tuurlijk kun je zeggen dat je dan een lijst moet hebben die z'n eigen contents vrijgeeft, maar met een lijst van pointers is dat weer niet mogelijk, omdat de lijst dan niet kan weten hoe groot die pointers zijn ;)

Als er nou in bovenstaand blokje een exception optreedt, wordt dat loopje in het finally-block uitgevoerd, maar dat de variabele I daarbij gebruikt wordt, is helemaal niet smerig. De methode is nog niet voorbij, dus de variabele bestaat nog op de stack. Ik zie dus geen reden om em dan niet te gebruiken.

日本!🎌


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 09-08 12:23

.oisyn

Moderator Devschuur®

Demotivational Speaker

Slecht voorbeeld, want daar gebruik je dus typisch een object die de elementen vrijgeeft in z'n destructor.

Ik zat meer te denken in deze richting:

C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
void doTransaction ()
{
    int progress = 0;

    try
    {
        changeSomething ();
        progress++;
        changeSomethingElse ();
        progress++;
        changeSomeMore ();
        progress = 0;
    }
    finally
    {
        switch (progress)
        {
        case 2:
            revertSomethingElse ();
        case 1:
            revertSomething ();
        }
    }
}


Op het moment dat er een exception gegooid wordt in een van de functies is het de bedoeling dat de eerdere veranderingen ongedaan worden gemaakt, om te voorkomen dat het geheel in een inconsistente staat belandt. Een db transaction dus, zeg maar. Een finally is hier goed op z'n plaats, je hebt een aantal atomische veranderingen die je achter elkaar door wilt voeren. C++ kent echter geen finally, dus je zult of de exception moeten afvangen, of die rollback code in een destructor stoppen:

C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
void doTransaction ()
{
    struct RollbackHelper
    {
        int progress;

        RollbackHelper () : progress (0) { }
        ~RollbackHelper ()
        {
            switch (progress)
            {
            case 2:
                revertSomethingElse ();
            case 1:
                revertSomething ();
            }
        }

        void taskCompleted () { progress++; }
        void allTasksDone () { progress = 0; }
    };

    RollbackHelper helper;

    changeSomething ();
    helper.taskCompleted ();
    changeSomethingElse ();
    helper.taskCompleted ();
    changeSomeMore ();
    helper.allTasksDone ();
}


Ik vind het een beetje omslachtig, plus het feit dat je niet bij de lokale variabelen van de functie kan, hoewel dat wel op te lossen is door alle code in het object zelf te zetten en alle locals dus te definieren als members. De finally is echter wel beter leesbaar imho, en gewoonweg makkelijker om mee om te gaan.

[ Voor 93% gewijzigd door .oisyn op 16-12-2004 16:23 ]

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • FendtVario
  • Registratie: Januari 2002
  • Laatst online: 12-05 22:30

FendtVario

The leader drives Vario!

In Delphi is het toch ook niet meer nodig de grootte van een pointer mee te geven als je iets vrijgeeft met FreeMem? Daarnaast heb je Delphi ook nog de TObjectList die weer wel owner is van de items.

[ Voor 40% gewijzigd door FendtVario op 16-12-2004 16:21 . Reden: onzin gepost ]

www.fendt.com | Nikon D7100 | PS5


Verwijderd

.oisyn:

C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
void doTransaction()
{
    try {
        int progress= 0;
        try {
            changeSome();
            ++progress;
            changeSomeMore();
            ++progress;
            finalizeChanges();
            progress= 0;
        }
        catch(...) {
            // simulated finally: executed whenever an exception is thrown,
            // but NOT if there are no exceptions!
            switch(progress) {
                case 2:    revertSomeMore();
                case 1:    revertSome();
            }
            throw;  // rethrow exception
        }
    }
    catch(some_fancy_exception& e) {
        // do some bookkeeping
    }
}

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 09-08 12:23

.oisyn

Moderator Devschuur®

Demotivational Speaker

.oisyn schreef op donderdag 16 december 2004 @ 16:10:
C++ kent echter geen finally, dus je zult of de exception moeten afvangen, of die rollback code in een destructor stoppen:
Met als nadeel dat het dan niet altijd wordt aangeroepen (wat in dit geval niet uitmaakt, maar wel uit zou kunnen maken)

[ Voor 22% gewijzigd door .oisyn op 16-12-2004 17:11 ]

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Verwijderd

.oisyn schreef op donderdag 16 december 2004 @ 17:08:
Met als nadeel dat het dan niet altijd wordt aangeroepen (wat in dit geval niet uitmaakt, maar wel uit zou kunnen maken)
Maar dat is dus tevens de grootste kritiek op finally: doordat finally altijd wordt aangeroepen gebruik je het exception handling mechanisme voor de normale program flow.

(Ik was me bewust van het feit dat je over het afvangen van de exception schreef, maar het is cruciaal de exception daarna opnieuw te throwen, vandaar mijn vorige post.)

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 09-08 12:23

.oisyn

Moderator Devschuur®

Demotivational Speaker

Verwijderd schreef op donderdag 16 december 2004 @ 17:19:
[...]


Maar dat is dus tevens de grootste kritiek op finally: doordat finally altijd wordt aangeroepen gebruik je het exception handling mechanisme voor de normale program flow.
Wat is daar op tegen dan? Het ging er juist om dat je geen exceptions gebruikte voor normale program-flow, het gebruik van een finally geeft alleen maar aan dat iets altijd uitgevoerd moet worden, ookal wordt er een exception gegooid.
(Ik was me bewust van het feit dat je over het afvangen van de exception schreef, maar het is cruciaal de exception daarna opnieuw te throwen, vandaar mijn vorige post.)
Uiteraard, anders is er voor de buitenwereld niets ergs gebeurd ;)

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • Soultaker
  • Registratie: September 2000
  • Laatst online: 00:54
Johannes schreef op donderdag 16 december 2004 @ 10:45:
Triviale zaken eerst: self.db.user is geen dictionary, maar een object van klasse Class (ja, de terminologie wil nog wel eens verrassend wezen). Deze heeft geen __contains__ method dus 'username in self.db.user' werkt niet. Misschien wel aardig om eens te implementeren.
Dat is een beetje een kip-en-ei-probleem: als ik de code had geschreven, dan was ik al van plan geweest om 'in' te gebruiken en dan zorgde ik er ook voor dat de user data in een datastructuur zat die die operatie ondersteunt. Gewoon een dictionary, waarschijnlijk.
Maar dan mis ik nog mijn self._('Login error'). Deze error gaat uiteindelijk naar de user van de web interface toe. Nou zou je kunnen denken: laat dat over aan de caller van verifyLogin. Maar er zijn meerdere redenen waarom verifyLogin kan mislukken: 'Login error' of 'You do not have permission to login'. Dus zou je meerdere status-codes moeten meegeven om hiertussen onderscheid te maken. Ook betekent dit dat elke caller de status-codes om moet gaan zetten naar error-messages. Dit is dus geen realistisch alternatief.

[..] maar het probleem hiermee is dat je zelf een plaats verzint waar de error messages te vinden zijn. Dat is weer iets wat je moet documenteren en dat de gebruiker van je functie moet uitzoeken. Als je documenteert dat je een exceptie kan raisen, dan weet elke redelijke Python-programmeur hoe hij de error-message kan vinden (namelijk met str(err)).
Je zou ook een fout-object kunnen retourneren (in plaats van een boolean) zodat je onderscheid kunt maken tussen de verschillende fouten die zijn opgetreden en bovendien alle fouten bij elkaar kunt definiëren. Daarmee voorkom je dus de rommel die het geeft om de foutmeldingen en status codes te definiëren waar ze toevallig gedetecteerd worden. Bovendien heb je het voordeel dat je er dan gewoon str() op kunt aanroepen.

Eventueel kun je de actie die je in de caller uitvoert (status code en user instellen) dan ook encapsuleren in het fout-object. Op die manier voorkom je dat je de code in de caller moet dupliceren (zoals je nu naar eigen zeggen doet, omdat verifyLogin op twee plekken wordt aangeroepen, en ik neem aan dat in beide gevallen de LoginError op dezelfde manier afgehandeld wordt). Dat heeft echter meer te maken met het afhandelen van de situatie dan met het wel of niet gebruiken van een exception (als je een exception gooit, zou je dat ook kunnen toepassen).

Kort gezegd denk ik dat een return value hier niet onderdoed is dan een exception. Er zitten ook geen hele grote voordelen aan, eerlijk gezegd, behalve dat je geen onnodige exceptions raist. Persoonlijk vind ik dat in een normaal functionerend programma geen exceptions horen voor te komen; in de praktijk komen die natuurlijk wel voor, maar juist omdat dat die situaties een uitzondering vormen op de verwachte program flow. Het afhandelen van exceptions zou het programma naar mijn mening dus alleen maar robuster moeten maken maar geen invloed moeten hebben op de globale werking ervan. In een 'normaal' programma zou je dus exception handling in theorie dus pas aan het einde kunnen toevoegen of helemaal achterwege laten, al is het niet echt praktisch om in een compleet programma uit te zoeken hoe de exception flow precies in elkaar. (En bovendien zou je in een taal als C++ altijd je best moeten doen om exception safe code te schrijven, ook al ben je nog helemaal niet van plan exceptions actief af te handelen.)
Ik zou het hier graag mee eens of oneens zijn, maar de rest van de code werkt nu eenmaal met excepties, dus kan ik hier verder weinig doen.
Ik zeg ook niet dat je het in deze concrete situatie alllemaal moet aanpassen, maar dat betekent natuurlijk niet dat we de code niet als voorbeeld kunnen nemen om de discussie vorm te geven. Ik wil ook niet beweren dat je huidige aanpak 'fout' is, maar alleen dat ik zelf om stylistische redenen en op basis van ervaring met andere programmeertalen een andere oplossing zou hebben gekozen.

  • Tomatoman
  • Registratie: November 2000
  • Laatst online: 11:57

Tomatoman

Fulltime prutser

Als ik het zo lees, gebruik ik vaker exceptions om de programma-afloop te bepalen dan de meeste anderen. Laat ik er een voorbeeld bij pakken. Stel dat je een functie wilt schrijven die een getal omzet in een string van 5 letters. Als er minder dan 5 letters nodig zijn, wordt de string gevuld met voorloopnullen.

Voor het gemak gebruik ik Delphi-code, maar het probleem is eigenlijk taalonafhankelijk. Je krijgt nu zoiets:
Delphi:
1
2
3
4
5
6
7
8
9
10
11
{ Getal is een Longword, dat is een 32-bit unsigned integer. }
{ Format is een standaardfunctie in Delphi die in dit specifieke geval
  een string van 5 letters retourneert. }
{ Het functieresultaat wordt teruggeven in Result. }
function IntToString5(Getal: Longword): string[5];
begin
  if Getal <= 99999 then
    Result := Format(%.5d, [Getal])  // decimale string met voorloopnullen
  else
    { Tja, wat nu? }
end;
Het probleem zit hem in regel 10: wat doe je als de stringrepresentatie van Getal meer dan 5 letters lang is? Het is een gemakkelijke voorzienbare situatie, dus kun je gemakkelijk een constructie verzinnen om de exception te voorkomen. Je zou bijvoorbeeld de string 'fout!' kunnen teruggeven, of een lege string. Maar een lege string is niet 5 letters lang (programmeertechnisch geen probleem, maar wel inconsequent) en de tekst 'fout!' is ook maar willekeurig gekozen.

Je zou ook een extra parameter kunnen gebruiken om de foutconditie aan te geven:
Delphi:
1
2
3
4
5
6
7
8
9
10
11
12
function IntToString5(Getal: Longword; out Geslaagd: Boolean): string[5];
begin
  if Getal <= 99999 then
  begin
    Result := Format(%.5d, [Getal]);
    Geslaagd := True;
  else
  begin
    Result := ''; 
    Geslaagd := False;
  end;
end;
Nu kun je aan de Parameter Geslaagd zien of er iets is misgegaan. Als Geslaagd false is, is het functieresultaat (de string) geen geldige waarde. Je hebt nu een oplossing verzonnen waarmee je kunt aangeven of de functie een geldige waarde retourneert, maar fraai is het niet. Het heeft als nadeel dat je de functie altijd moet aanroepen met een extra parameter, zelfs als je weet dat je nooit een getal boven de 99999 wilt omzetten in een string. De functieaanroep wordt onnodig ingewikkeld.

Met het gebruik van een exception maak je de code een stuk beter hanteerbaar.
Delphi:
1
2
3
4
5
6
7
function IntToString5(Getal: Longword): string[5];
begin
  if Getal <= 99999 then
    Result := Format(%.5d, [Getal])
  else
    raise Exception.Create('Getal is te groot'); // creëer een exception
end;
Als je nu netjes documenteert dat je een exception om je oren krijgt als Value groter dan 99999 is, is alles opgelost. Een programmeur die zeker weet dat hij nooit functiewaardes groter dan 99999 gebruikt, hoeft niet eens een try..except blok te gebruiken bij de functieaanroep.

In het gegeven voorbeeld geef ik sterk de voorkeur aan het gebruik van een exception, zelfs als het niet strikt noodzakelijk is. De voordelen worden alleen maar groter als de functie ingewikkelder wordt (bijvoorbeeld als hij ook negatieve waardes voor Getal accepteert). Je kunt workarounds verzinnen om het gebruik van de exception te vermijden, maar dat heeft meer nadelen dan voordelen.

Een goede grap mag vrienden kosten.


  • drm
  • Registratie: Februari 2001
  • Laatst online: 09-06 13:31

drm

f0pc0dert

tomatoman: (een heel verhaal)
Dat lijkt mij nou juist een typisch voorbeeld van een prima exception. Feitelijk is dat een stukje parameter checking die wat verder gaat dan het type. In java komt dat ook heel veel voor, hence het bestaan van de standaard IllegalArgumentException. Dit is niet echt wat je noemt "program flow", het is tenslotte een invalid argument wat de functie meekrijgt, wat, normaal gesproken (als de programmeur weet waar hij mee bezig is) niet voor zou mogen komen. Sterker nog; je verhaal pleit zelfs wel voor een assert.

Music is the pleasure the human mind experiences from counting without being aware that it is counting
~ Gottfried Leibniz


Verwijderd

.oisyn schreef op donderdag 16 december 2004 @ 17:31:
Wat is daar op tegen dan? Het ging er juist om dat je geen exceptions gebruikte voor normale program-flow, het gebruik van een finally geeft alleen maar aan dat iets altijd uitgevoerd moet worden, ookal wordt er een exception gegooid.
Dat het de program-flow breekt, net als een goto of een exception die als een "comefrom" gebruikt wordt; je kunt er gemakkelijk spaghetti mee maken:

C++:
1
2
3
4
5
6
7
8
9
10
11
void erase_from_or_all(my_list& l, const value& v) {
    my_list::iterator i= l.begin();
    try {
        for(; i != l.end(); ++i)
            if(*i == v) return;
        i= l.begin();
    }
    finally {
        l.erase(i, l.end());
    }
}

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 09-08 12:23

.oisyn

Moderator Devschuur®

Demotivational Speaker

Dat kan met destructors ook, maar dat neemt nog niet weg dat het ook voor valide dingen bruikbaar is :)

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • Tomatoman
  • Registratie: November 2000
  • Laatst online: 11:57

Tomatoman

Fulltime prutser

drm schreef op donderdag 16 december 2004 @ 17:55:
[...]
Dit is niet echt wat je noemt "program flow", het is tenslotte een invalid argument wat de functie meekrijgt, wat, normaal gesproken (als de programmeur weet waar hij mee bezig is) niet voor zou mogen komen.
Hmm, ja, je hebt gelijk.
.oisyn schreef op woensdag 15 december 2004 @ 18:50:
[...]
Dat is idd wat ik eerder in deze draad al gezegd heb ja ;)
.oisyn in "[alg] Exceptions, wanneer te gebruiken?"

Het zijn programmer errors, daar horen (imho) geen exceptions voor gebruikt te worden.
Daar ben ik het niet mee eens. Laat ik eens code met een bug schrijven:
Delphi:
1
2
3
4
5
6
7
8
9
10
11
var
  Artikel, Bericht: string;
  ArtikelNr: Longword;
begin
  ArtikelNr := 123456;
  Artikel := StrToInt5(ArtikelNr);

  { Doe hier nog een heleboel andere dingen. }

  Bericht := 'Het derde cijfer van het artikelnummer is: ' + Artikel[3];
end;
Dit gaat fout, omdat het artikelnummer groter dan 99999 is. Stel dat StrToInt5 geen exception zou geven als er een te groot getal ingevoerd zou worden, maar in plaats daarvan een lege string zou teruggeven. Dan gaat het programma vrolijk verder en pas op regel 10 krijgt de programmeur tijdens het debuggen een exception, namelijk een access violation. Met Artikel[3] lees je immers het derde karakter uit een lege string. Zie dat maar eens te debuggen, bij een access violation verdenk je StrToInt5 niet zo snel.

Zou StrToInt5 daarentegen een exception geven als het getal te groot is, dan wordt het debuggen veel eenvoudiger. De code geeft direct op de plaats van het probleem een exception en geeft met de message 'Getal is te groot' ook nog eens aan wat er precies aan de hand is.

Voor de gebruiker van het programma maakt het geen verschil, hij hoort het programma pas te krijgen nadat de bug eruit is gehaald. Voor de programmeur die aan het debuggen is, is het verschil echter behoorlijk groot. Daarom vind ik dat exceptions ook bedoeld kunnen zijn voor de programmeur.

Een goede grap mag vrienden kosten.


  • .oisyn
  • Registratie: September 2000
  • Laatst online: 09-08 12:23

.oisyn

Moderator Devschuur®

Demotivational Speaker

Nogmaals, ik zeg ook niet dat je de error moet negeren, ik zeg dat een assert beter op z'n plaats is dan een exception. Je hebt er namelijk controle over, het is je eigen code. Je kunt er dus ook defensief programmeren ipv gewoon maar lukraak die functie aan te roepen en een exception af te vangen als het fout blijkt te gaan.

[ Voor 3% gewijzigd door .oisyn op 16-12-2004 18:30 ]

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


  • Johannes
  • Registratie: Juni 2000
  • Laatst online: 09-07 21:03
Soultaker schreef op donderdag 16 december 2004 @ 17:44:
[...]

Dat is een beetje een kip-en-ei-probleem: als ik de code had geschreven, dan was ik al van plan geweest om 'in' te gebruiken en dan zorgde ik er ook voor dat de user data in een datastructuur zat die die operatie ondersteunt. Gewoon een dictionary, waarschijnlijk.
Nou, in deze context is een dictionary niet echt van toepassing (zie de design documentatie van de hyperdb als het je interesseert), maar ik begrijp je punt.
Je zou ook een fout-object kunnen retourneren (in plaats van een boolean) zodat je onderscheid kunt maken tussen de verschillende fouten die zijn opgetreden en bovendien alle fouten bij elkaar kunt definiëren. Daarmee voorkom je dus de rommel die het geeft om de foutmeldingen en status codes te definiëren waar ze toevallig gedetecteerd worden. Bovendien heb je het voordeel dat je er dan gewoon str() op kunt aanroepen.
Zou je hier misschien een stukje voorbeeld-code van kunnen geven? Ik heb wel een idee, maar misschien helpt wat code voordat ik weer begin te bekritiseren (sorry ;)).
Eventueel kun je de actie die je in de caller uitvoert (status code en user instellen) dan ook encapsuleren in het fout-object. Op die manier voorkom je dat je de code in de caller moet dupliceren (zoals je nu naar eigen zeggen doet, omdat verifyLogin op twee plekken wordt aangeroepen, en ik neem aan dat in beide gevallen de LoginError op dezelfde manier afgehandeld wordt).
Neuh, anders had ik het wel weer in een andere methode (of in verifyLogin) gefactored. De aanroep die ik liet zien handelt HTTP Basic authentication af, de andere aanroep doet authenticatie vanuit cookies. Bij een error bij Basic Authentication moet de response code op 403 gezet worden, bij cookie authentication moet de gebruiker alleen een mooie error te zien krijgen.

Uit volle borst op weg naar nergens / Zonder reden zonder doel
Met m'n zeden en m'n zonden / En mijn angstig voorgevoel
Laat mij mijn kont tegen de krib / Laat mij dit goddeloze lied
Hef jij je handen maar ten hemel / Maar red mij niet


  • Soultaker
  • Registratie: September 2000
  • Laatst online: 00:54
Zou je hier misschien een stukje voorbeeld-code van kunnen geven? Ik heb wel een idee, maar misschien helpt wat code voordat ik weer begin te bekritiseren (sorry ;)).
Ik zat aan zoiets te denken:
Python:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#
# Eerst de fouten die kunnen optreden, met zinnige melding.
#

class NoSuchUser:
    def __init__(self, username):
        self.username = username
    def __str__(self):
        return 'No such user "%s".' % self.username

class IncorrectPassword
    def __init__(self, username):
        self.username = username
    def __str__(self):
        return 'Incorrect password for user "%s".' % self.username

#
# Dan dus de verify-functie, die alleen verifieert.
# (Implementatie is niet zo relevant.)
#

def verifyLogin(self, username, password):
    if username not in self.db.user:
        return NoSuchUser(username)
    if self.db.user[username].password <> password:
        return IncorrectPassword(username)
    self.user = self.db.user[username]
    return None

#
# Caller:
#

error = self.verifyLogin(username, password)
if not error:
    print "Welkom %s!" % self.user.name
else:
    print "Er ging iets mis:", error
Neuh, anders had ik het wel weer in een andere methode (of in verifyLogin) gefactored. De aanroep die ik liet zien handelt HTTP Basic authentication af, de andere aanroep doet authenticatie vanuit cookies. Bij een error bij Basic Authentication moet de response code op 403 gezet worden, bij cookie authentication moet de gebruiker alleen een mooie error te zien krijgen.
Dat klinkt wel zinnig. :)

[ Voor 13% gewijzigd door Soultaker op 16-12-2004 20:55 ]


Acties:
  • 0 Henk 'm!

  • FendtVario
  • Registratie: Januari 2002
  • Laatst online: 12-05 22:30

FendtVario

The leader drives Vario!

.oisyn schreef op donderdag 16 december 2004 @ 18:29:
Nogmaals, ik zeg ook niet dat je de error moet negeren, ik zeg dat een assert beter op z'n plaats is dan een exception. Je hebt er namelijk controle over, het is je eigen code. Je kunt er dus ook defensief programmeren ipv gewoon maar lukraak die functie aan te roepen en een exception af te vangen als het fout blijkt te gaan.
Ben ik niet helemaal met je eens. StrToInt5 is duidelijk een functie die opgenomen wordt in een bibliotheek opgenomen wordt. Ook al is dit je eigen code, je zal misschien niet altijd invloed hebben op de input. En wat als anderen de functie gaan gebruiken? StrToInt gooit toch ook een exception ipv van een Assert? En daarbij, omdat het Delphi is zou je verwachten dat er een EConvertError gebruikt wordt ipv een Assert (al gooit assert ook een exception);

* FendtVario zou ook een StrToInt5Def maken ;)

www.fendt.com | Nikon D7100 | PS5


Acties:
  • 0 Henk 'm!

  • .oisyn
  • Registratie: September 2000
  • Laatst online: 09-08 12:23

.oisyn

Moderator Devschuur®

Demotivational Speaker

FendtVario schreef op vrijdag 17 december 2004 @ 08:23:
Ook al is dit je eigen code, je zal misschien niet altijd invloed hebben op de input.
Maar je kunt de input wél van tevoren controleren, of je returnt naast de int ook gewoon een errorcode (hoewel dat niet in elke taal kan, maar de meeste ondersteunen wel variables passed by reference)

Give a man a game and he'll have fun for a day. Teach a man to make games and he'll never have fun again.


Acties:
  • 0 Henk 'm!

  • FendtVario
  • Registratie: Januari 2002
  • Laatst online: 12-05 22:30

FendtVario

The leader drives Vario!

Ik had nog een brainwave over het gebruik van Exceptions. Dit voorbeeld heb ik gemaakt in Delphi en mijn vraag is of die een juiste manier zou kunnen zijn om exceptions te gebruiken.

In dit voorbeeldje heb ik nog heel oude Interbase tabel gebruikt waarin naam en adresgegevens etc bewaart kunnen worden. Stel nu dat ik een applicatie wil ontwikkelen met een Windows GUI maar de gegevens ook toegankelijk wil maken via internet (bijv. met WebSnap). Omdat je niet 2x hetzelfde wilt proggen heb ik een functie validateForm gemaakt en ervoor gekozen deze in de onBeforePost aan te roepen. Als de input niet geldig is wordt een zelf gedeclareerde EValidationError fout opgegooid.

In de Windows GUI kun je dan een berichtvenster laten zien, in WebSnap kun je bijv. de meldingen boven het in te vullen formulier zetten. Hieronder heb ik de code neergezet.
Delphi:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
procedure TfrmTestException.btnOpslaanClick(Sender: TObject);
begin
  try
    if (IBTable1.State in dsEditModes) then
      IBTable1.Post;

  except
    on EVE: EValidationError do
      ShowValidationMessage(EVE.Message, EVE.Messages);
    on E: Exception do
    begin
      MessageDlg(E.Message, mtError, [mbOk], 0);
    end;

  end; //try except
end;

procedure TfrmTestException.IBTable1BeforePost(DataSet: TDataSet);
var
  tmpMessages: TStringList;
begin
  { controleer de ingevulde waarden }
  if not ValidateForm(tmpMessages) then
    raise EValidationError.Create(
      'Het persoonsformulier is niet juist ingevuld.', tmpMessages);
end;

function TfrmTestException.ValidateForm(var Messages: TStringList): Boolean;

  procedure AddMessage(const Text: string);
  begin
    if not Assigned(Messages) then
      Messages := TStringList.Create;

    Messages.Add(Text);
  end;

begin
  if Trim(IBTable1VOORLETTERS.AsString) = '' then
    AddMessage('Geen voorletters ingevuld.');

  if Trim(IBTable1ACHTERNAAM.AsString) = '' then
    AddMessage('Geen achternaam ingevuld.');

  if Trim(IBTable1TH_STRAAT.AsString) = '' then
    AddMessage('Geen straat ingevuld.');

  { hier alle andere velden controleren de vereist zijn of aan een andere
    voorwaarde moeten voldoen, geknipt voor forum }

  Result := not (Assigned(Messages) and (Messages.Count > 0));
end;

Of is het aanroepen van de ValidateForm in de onClick beter, dus op deze manier (en dan natuurlijk het onBeforePost event niet gebruiken):

Delphi:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
procedure TfrmTestException.btnOpslaan2Click(Sender: TObject);
var
  tmpMessages: TStringList;
begin
  tmpMessages := TStringList.Create;

  if (IBTable1.State in dsEditModes) then
  begin
    //controleer waarden
    if ValidateForm(tmpMessages) then
      IBTable1.Post
    else
      ShowValidationMessage('Het persoonsformulier is niet juist ingevuld.',
        tmpMessages);
  end;
end;


Bij gebruik van de eerste manier is het in WebSnap ook niet meer nodig om de validateForm aan te roepen. Als er onvolledige/verkeerde data is zal de exception omhoog gegooid worden waardoor in alle gevallen de bewerking wordt afgebroken.

www.fendt.com | Nikon D7100 | PS5


Acties:
  • 0 Henk 'm!

  • _Thanatos_
  • Registratie: Januari 2001
  • Laatst online: 07-08 10:54

_Thanatos_

Ja, en kaal

FendtVario schreef op donderdag 16 december 2004 @ 16:11:
In Delphi is het toch ook niet meer nodig de grootte van een pointer mee te geven als je iets vrijgeeft met FreeMem?
Niet helemaal waar, geheugen dat je zelf alloceert met AllocMem (of een dergelijke andere functie uit de RTL van Delphi) kun je inderdaad vrijgeven zonder dat je daarbij de grootte hoeft te weten, omdat de grootte in de 4 bytes vóór de pointer wordt bewaard. Maar als je nou een pointer van een DLL of een gewone API functie krijgt, dan is dat waarschijnlijk niet zo, en oet je wel op de nette manier vrijgeven.

日本!🎌


Acties:
  • 0 Henk 'm!

  • drm
  • Registratie: Februari 2001
  • Laatst online: 09-06 13:31

drm

f0pc0dert

Ik heb trouwens de oplossing voor alle problemen omtrent Exceptions gevonden. Je moet het gewoon zo doen

Music is the pleasure the human mind experiences from counting without being aware that it is counting
~ Gottfried Leibniz

Pagina: 1