Hi, ich bin gerade dabei mir ein Schema zu überlegen, wie ich Rückgabewerte von Funktionen handhaben soll. Gemeint sind Rückgabewerte, die Aussage über evtl. aufgetretene Fehler geben. Ich habe mir folgende Möglichkeiten überlegt: 1. Rückgabewerte sind fest in den Funktionen hinterlegt, also z.B. 0 für keinen Fehler, 1 für Fehler Nr.1, usw. Das prüfen dieser Fehler kann jetzt auf zwei Arten geschehen: Entweder man prüft auch direkt auf die entsprechenden Rückgabewerte, oder indirekt, indem man zum besseren Verständnis den festen Rückgabewerten per #define aussagekräftige Namen verpasst, und damit dann den Rückgabewert prüft. 2. Die aus Punkt 1 beschriebenen per #define angelegten Fehler-Namen werden auch in den Funktionen verwendet. Das wiederum macht die Funktionen besser lesbar. Allerdings stellt sich dann die Frage, wie es gehandhabt werden soll, wenn z.B. zwei Funktionen einen gleichen Fehlernamen verwenden, obwohl die Funktionen nichts miteinander zu tun haben. Ein zentrales H-File mit allen Fehlern halte ich für unklug, zumal in Betracht gezogen werden soll, dass auch Bibliotheken programmiert werden sollen. Das heisst, bei einer Änderung einer bereits bestehenden Zuweisung müssen alle Bibliotheken neu übersetzt werden. Es sei denn, man kann sicherstellen, dass Bestehendes nie geändert wird. 3. ?????? Wie macht ihr das denn? Ralf
Punkt 2 ist eine verbreitete Vorgehensweise. Die Zuordnung von Fehlernummern und deren Bedeutung wird als unveränderlich angesehen. Wenn eine (neue) Funktion einen bislang unbekannten Fehlercode liefern soll, muss die Fehlernummernliste entsprechend erweitert werden, es darf aber keine "alte" Fehlernummer inhaltlich geändert werden. So machen es Betriebssystem-APIs; gewisse Fehlercodes sind sogar betriebssystemübergreifend standardisiert. Sieh Dir mal errno.h oder error.h Deines Compilers an. Natürlich lassen sich nicht alle möglichen Fehler auf dieses feste Fehlernummernmodell abbilden - in diesem Falle werden oft eigene Fehlercodetabellen für Libraries verwendet. Die Verwendung eines enums für Fehlercodes erscheint mir übrigens recht vorteilhaft, da a) moderne Debugger dann den symbolischen Namen anzeigen können (was mit #defines nicht geht) und b) man sich nicht um die tatsächlichen numerischen Werte Gedanken machen muss, solange man neue Elemente immer "hinten" anhängt.
Es gibt noch eine 3. Methode: Eine Extrafunktion zum Abfragen, welcher Fehler in der letzten Funktion aufgetreten ist. Das hat den Vorteil, dass du in der zu prüfenden Funktion nur einen Rückgabewert für den Fehlerfall verbrätst ("Es ist irgendein Fehler aufgetreten") und die restlichen Rückgabewerte Nutzdaten sind. Welcher Fehler genau aufgetreten ist, kann mit der Extrafunktion geprüft werden.
Die dritte hier emfpohlene Methode bringt eigentlich keinen Vorteil. Was soll es bringen, wenn der Returncode ohnehin für Fehlerinformation verwendet wird, dann nur zwischen Fehler und kein Fehler zu unterscheiden? Ausserdem erfordert er das Ablegen der Fehlerinformation ausserhalb der Funktion - global? - Bäh! So was gibts leider in C und auch Standardbibliotheken machen das, aber das muss man deswegen nicht nachmachen ;) "..., kann mit der Extrafunktion..." Genau, "kann", wird aber nicht... Fehlermeldungen, die ignoriert werden können, sind keine wirksamen Fehlermeldungen. Zum zentralen Fehler-Headerfile. Hat seine Berechtigung und die Wiederverwendung allgemeiner Fehler ist auch nicht schlecht. Bastelst du unterschiedliche Bibliotheken mit eigenen Headers und jeweils eigenen (aber gleichlautenden) Fehler-Defines, kommt es zu Problemen. Da helfen enums oder namespaces (müsste es nach C99 mittlerweile auch in C geben). Ich würde eine "globale" Headerdatei für Fehler anfangen. Vermeide auf jeden Fall die Verwendung von "magischen Zahlen" und arbeite unbedingt mit symbolischen Namen, also enums oder halt defines! Gruß
"errno" als Beispiel für modulare Fehlercodes hinzustellen, ist schon fast kriminell. Besser: Fehlercodes nicht sequentiell numerieren, sondern Bereiche definieren. Global wird nur der Bereich des jeweiligen Moduls definiert, also beispielsweise: <errors-global.h> #define ERR_MODULE_1 0x0100 #define ERR_MODULE_2 0x0300 #define ERR_MODULE_3 0x0200 <module-1.h> #define ERR_MOD1_WASSOLLICHDAMITANFANGEN (ERR_MODULE_1+0x01) usw. C++ Namespaces sind auch ganz nett, lösen das Problem aber auch nicht ganz, weil halt immer noch irgendwer global numerieren muss. Einzig Strings vgl. Java-Classnames sind eindeutig, aber etwas unhandlich.
Wer hat die 3. Methode empfohlen? Ich nicht. Ich habe geschrieben, dass es diese Methode gibt und welchen Vorteil ich darin sehe. Wieso kann man nicht sachlich beim Thema bleiben und muss Leute als kriminelle Ignoranten titulieren?
Ok, sorry, war etwas zu agressiv formuliert. Erwähnt hat es übrigens Rufus. Weil ich weder Unix/Linux noch Windows als Vorbild für Modularisierung und API-Design anraten möchte. Die errnos von Unix sind eine Katastrophe, ähnlich schlimm wie Microsofts Namensgebung von APIs. Ich hatte letzthin mal mit einem Backup-Programm zu tun, das einen Schreibfehler per FTP lauthals auf ein verschmutztes Medium schob. Typischer Fall von etwas zu schmalspurig konzipierter Fehlerhandhabung.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.