![]() |
DLL/Lib: Parameter Validierung
Hallo zusammen,
mal ein etwas kontroverses Thema: Ich schreibe eine Library (in C; also keine Exceptions) mit einigen Funktionen, die exportiert werden (also als API für Entwickler; nicht als User-Interface). Wie gehe ich vor?
Zu 1.: Pro:
Zu 2.: Pro:
Momentan fahre ich mit Runtime-Checks für die exportierten Funktionen und verwende Assertions nur intern. Funktioniert für mich so eigentlich, aber auf der anderen Seite machen sich die Checks zur Laufzeit bei Zeitkritischen Operationen teilweise doch bemerkbar. Außerdem wird der Code hierdurch unnötig komplex und wenn man den Gedanken mal weiterspinnt: Eine weitere Library verwendet intern meine Library, validiert aber in ihrem Public Interface auch erst noch Parameter, etc. dann werden sich die redundanten Checks irgendwann ziemlich häufen. Eine weitere Überlegung zu diesem Thema sind Zeiger. Prüft man die explizit auf
Delphi-Quellcode:
/
NULL
Delphi-Quellcode:
? Oder lässt man es gleich sein, da ein Null-Pointer nicht schlimmer ist als irgendein anderer ungültiger Zeiger (auf den man eh nicht gescheit testen kann)?
nil
Mich würde mal interessieren, wie ihr da vorgeht. Insbesondere bei professioneller (nicht Endanwender-Software). Viele Grüße Zacherl |
AW: Library: Parameter Validierung
Das hängt doch auch stark von der Art und Weise ab wie deine Sachen konsumiert werden. Über REST? Mit Dingen wie "nil" und Assertions hört sich das so an als gehe es direkt um eine DLL.
Wenn Geschwindigkeit dir so wichtig ist dann biete doch einfach zwei Varianten an: Eine mit Validierung, eine ohne. So etwas sieht man ja sogar in der Delphi-RTL, beispielsweise TBitConverter.From<T> vs. TBitConverter.UnsafeFrom<T> ( ![]() Dann kann jeder nehmen was er für richtig hält und sich selbst braucht man auch keine Vorwürfe machen dass man die Eingaben nicht vernünftig geprüft hätte. |
AW: Library: Parameter Validierung
Zitat:
Zitat:
|
AW: DLL/Lib: Parameter Validierung
Kannst du denn ein abstraktes Beispiel geben? Mir fehlt dazu die Vorstellungskraft. Holt man sich bei deiner APIs denn auch Bezeichner wie z.B. Handles ab und übergibt die später anderen Routinen?
|
AW: DLL/Lib: Parameter Validierung
Zitat:
Maximal wird intern zusätzlich nochmal ein Fehlerflag (ErrorCode/-Text) gespeichert, welchen man nachträglich nochmal prüfen kann, so ala GetLastError. Dafür sind grade Assertions da, dass man sie deakivieren kann, damit es schneller geht. Aber wenn man sie deaktiviert, dann hat man auch mit Fehlerverhalten zu rechnen, wenn die Eingaben doch ma nicht stimmen. Also ist es IMHO sinnlos und kontraproduktiv, wenn zusätzlich noch eine zweite Eingangsprüfung vorhanden wäre. (3) Und meistens nehme ich inzwischen Exceptions, zur Fehlerbehandlung, anstatt einem Fehlercode-Result. Dazu definiere ich mir meistens mindestens eine eigene Exceptionklasse, je Library. Diese Klassen können dann auch Zusatzinfos enthalten, siehe ErrorCode in EOSError oder EInOutError. Zitat:
Gerade bei Komponenten sollte man nie Fehler dierekt anzeigen, also bleiben da nur * Exceptions * Boolean-Result und interner Statusspeicher ala GetLastError * oder Fehlercodes Und Fehlercodes, also quasi MagicNumbers findet ich etwas blöd, darum habe ich mich für Exceptions entschieden. Sind die Exceptions aber lokalisierbar (ResourceStrings oder sonstwie), dann sollte die Exceptionklasse auch statische Fehlerinfos beinhalten (z.B. immer englischer Text oder Fehlercode). |
AW: DLL/Lib: Parameter Validierung
Zitat:
Wobei das auch nicht wirklich einen Unterschied macht, da die Fragestellung z.B. auch auf eine einfache Wurzel-Funktion anwendbar ist. Sagen wir ich habe einen Algorithmus, der die Wurzel einer Zahl berechnet und der auch für negative Zahlen ein (sinnloses) Ergebnis liefert. Prüfe ich jetzt per Assertion auf Zahlen < 0 oder implementiere ich einen "richtigen" Check, der ggfls. einen Error-Code zurückgibt? Oder kombiniere ich sogar beide Varianten? Im Falle der Assertion kann es sein, dass der Programmierer meine Funktion immer nur mit positiven Zahlen füttert, der Endanwender aber etwas Negatives übergibt und somit ein unerwartetes Ergebnis bekommen würde (wenn der Entwickler nicht manuell die Benutzereingabe vorher validiert, bevor er sie meiner Funktion übergibt). Mit Runtime-Checks würde die - zur Entwicklungszeit nicht getestete - Eingabe dazu führen, dass eine Fehlermeldung angezeigt wird (vorrausgesetzt der Entwickler prüft den Error-Code). Zitat:
Zitat:
Zitat:
|
AW: DLL/Lib: Parameter Validierung
Was ich immer noch nicht richtig zuordnen kann ist deine Sorge um Return values die der Nutzer vielleicht nicht auswertet und glaubt ein richtiges Ergebnis zu haben. Und glauben die Library sei fehlerhaft und "nicht gut". So etwas ist mir persönlich völlig fremd.
Ich würde, beispielsweise bei einem negativen Handle einen ordentlich dokumentierten Fehlercode zurückgeben. Wer eine Routine nimmt, ohne Doku zu lesen glaubt zu wissen was sie tut und sich dann freut dass es immerhin kompiliert - Damit hätte ich kein Mitleid. Wer dann undefinierte Werte in Ausgabeparametern hat und damit weiterarbeitet ist klar selber schuld. |
AW: DLL/Lib: Parameter Validierung
Exceptions gibt es im C auch, nur kennt C natürlich die Delphi-Exceptionklassen nicht und kann da dann nichtmal den im unbekannten Message-Text auswerten.
Nja, du kannst natürlich auch LongBool-Results nehmen (in C++ ist der BOOL gern 32 Bit groß), definierst dir eine Reihe Fehlercodes, nach dem Muster der WinAPI, wie z.B. HRESULT, und nutzt fleißig ![]() FACILITY ist ein Code für deine Komponente/Library.
Delphi-Quellcode:
oder
{------------------------------}
{ OLE Error Codes } {------------------------------} (* The return value of OLE APIs and methods is an HRESULT. This is not a handle to anything, but is merely a 32-bit value with several fields encoded in the value. The parts of an HRESULT are shown below. HRESULTs are 32 bit values layed out as follows: 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 +-+-+-+-+-+---------------------+-------------------------------+ |S|R|C|N|r| Facility | Code | +-+-+-+-+-+---------------------+-------------------------------+ where S - Severity - indicates success/fail 0 - Success 1 - Fail (COERROR) R - reserved portion of the facility code, corresponds to NT's second severity bit. C - reserved portion of the facility code, corresponds to NT's C field. N - reserved portion of the facility code. Used to indicate a mapped NT status value. r - reserved portion of the facility code. Reserved for internal use. Used to indicate HRESULT values that are not status values, but are instead message ids for display strings. Facility - is the facility code Code - is the facility's status code *) const SEVERITY_SUCCESS = 0; SEVERITY_ERROR = 1; function Succeeded(Status: HRESULT): BOOL; inline; function Failed(Status: HRESULT): BOOL; inline; function IsError(Status: HRESULT): BOOL; inline; function HResultCode(hr: HRESULT): Integer; inline; function HResultFacility(hr: HRESULT): Integer; inline; function HResultSeverity(hr: HRESULT): Integer; inline; function MakeResult(sev, fac, code: Integer): HResult; inline; const { HRESULT value definitions } { Codes $4000-$40ff are reserved for OLE } S_OK = $00000000; S_FALSE = $00000001; NOERROR = 0; E_UNEXPECTED = HRESULT($8000FFFF); E_NOTIMPL = HRESULT($80004001); E_OUTOFMEMORY = HRESULT($8007000E); E_INVALIDARG = HRESULT($80070057); E_NOINTERFACE = HRESULT($80004002); ...
Delphi-Quellcode:
{ DOS and OS/2 Compatible Error Code definitions returned by the Win32 Base
API functions. } { Translated from WINERROR.H } { Error code definitions for the Win32 API functions } (* Values are 32 bit values layed out as follows: 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 +---+-+-+-----------------------+-------------------------------+ |Sev|C|R| Facility | Code | +---+-+-+-----------------------+-------------------------------+ where Sev - is the severity code 00 - Success 01 - Informational 10 - Warning 11 - Error C - is the Customer code flag R - is a reserved bit Facility - is the facility code Code - is the facility's status code *) { Define the facility codes } const FACILITY_WINDOWS = 8; {$EXTERNALSYM FACILITY_WINDOWS} FACILITY_STORAGE = 3; {$EXTERNALSYM FACILITY_STORAGE} FACILITY_RPC = 1; {$EXTERNALSYM FACILITY_RPC} FACILITY_SSPI = 9; {$EXTERNALSYM FACILITY_SSPI} FACILITY_WIN32 = 7; {$EXTERNALSYM FACILITY_WIN32} FACILITY_CONTROL = 10; {$EXTERNALSYM FACILITY_CONTROL} FACILITY_NULL = 0; {$EXTERNALSYM FACILITY_NULL} FACILITY_INTERNET = 12; {$EXTERNALSYM FACILITY_INTERNET} FACILITY_ITF = 4; {$EXTERNALSYM FACILITY_ITF} FACILITY_DISPATCH = 2; {$EXTERNALSYM FACILITY_DISPATCH} FACILITY_CERT = 11; {$EXTERNALSYM FACILITY_CERT} { Define the severity codes } ERROR_SUCCESS = 0; NO_ERROR = 0; ERROR_INVALID_FUNCTION = 1; ERROR_FILE_NOT_FOUND = 2; ERROR_PATH_NOT_FOUND = 3; ERROR_TOO_MANY_OPEN_FILES = 4; ... |
AW: DLL/Lib: Parameter Validierung
Zitat:
Zitat:
Zitat:
Zitat:
|
Alle Zeitangaben in WEZ +1. Es ist jetzt 11:11 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz