Ungültige Funktionsargumente behandeln, ja oder nein?
-
Gugelmoser schrieb:
b) Würdet ihr den Parameter zu
int nabändern und eine ungültiges ArgumentJa, und zwar in Anlehnung an die Faustregel, daß Programme, die so gestaltet sind, daß sie durch den „dümmsten“ anzunehmenden Benutzer zum Absturz gebracht werden können, schlecht geschrieben sind.
http://de.wikipedia.org/wiki/Dümmster_anzunehmender_User
Gugelmoser schrieb:
. Also ich mache das nicht, weil ich finde, wenn der Benutzer zu blöd ist die Schnittstellen-Definition zu lesen, dann hat er eben Pech.
Hier wäre auch die Frage, was denn der Maßstab sein soll.
Ein Hochschulprofessor könnte da wahrscheinlich noch ganz andere Geschütze
auffahren
-
Mechanics schrieb:
Es kommt drauf an...
Und wie würdest du es bei dem Fakultät-Beispiel machen?
Sone schrieb:
static assertion oder
enable_if.Stimmt, daran hab ich gerade irgendwie nicht gedacht.
static assertionscheint allgemein oft verwendet zu werden, oder?Sone schrieb:
BTW: ist 0! nicht 0? Ich meine, 0 ist 0 oder?

Nein, dann würdest du am Ende ja immer mit 0 multiplizieren und es würde immer 0 herauskommen.
redrew99 schrieb:
Ja, und zwar in Anlehnung an die Faustregel, daß Programme, die so gestaltet sind, daß sie durch den „dümmsten“ anzunehmenden Benutzer zum Absturz gebracht werden können, schlecht geschrieben sind.
Stimmt, das ist auch ein Argument, daran hab ich auch nicht gedacht.
-
Gugelmoser schrieb:
Sone schrieb:
static assertion oder
enable_if.Stimmt, daran hab ich gerade irgendwie nicht gedacht.
static assertionscheint allgemein oft verwendet zu werden, oder?Willst du mich jetzt vera
rschenlbern oder was?
Ich wollt' nur nen Weg vorschlagen, andere Typen die implizit in unsigned konvertiert werden können, auszuschließen. Man muss nicht gleich böse werden.
-
Gugelmoser schrieb:
Sone schrieb:
BTW: ist 0! nicht 0? Ich meine, 0 ist 0 oder?

Nein, dann würdest du am Ende ja immer mit 0 multiplizieren und es würde immer 0 herauskommen.
Nein, was ich meine ist:
Alle Zahlen kleiner als die eigentliche Zahl - da gibt es gar keine. Also 0, oder nicht?

-
Gugelmoser schrieb:
Mechanics schrieb:
Es kommt drauf an...
Und wie würdest du es bei dem Fakultät-Beispiel machen?
Grad bei so "mathematischen" Funktionen, wo es auch (oder vor allem) auf Wertebereiche ankommt, sollte man versuchen, von vornherein so viele falsche Eingaben zu verhindern, wie möglich. Von dem her find ich die Antwort von Sone nicht verkehrt.
-
wtf!?
0! = 1.
-
Sone schrieb:
Gugelmoser schrieb:
Sone schrieb:
static assertion oder
enable_if.Stimmt, daran hab ich gerade irgendwie nicht gedacht.
static assertionscheint allgemein oft verwendet zu werden, oder?Willst du mich jetzt vera
rschenlbern oder was?
Ich wollt' nur nen Weg vorschlagen, andere Typen die implizit in unsigned konvertiert werden können, auszuschließen. Man muss nicht gleich böse werden.Nö, das war eine ernst gemeinte Aussage und Frage.
-
@Sone
Keiner will dich veralbern, wir wollen dich bloss loswerden.
Weil du ein unglaublicher Depp bist.
-
Gugelmoser schrieb:
Sone schrieb:
Gugelmoser schrieb:
Sone schrieb:
static assertion oder
enable_if.Stimmt, daran hab ich gerade irgendwie nicht gedacht.
static assertionscheint allgemein oft verwendet zu werden, oder?Willst du mich jetzt vera
rschenlbern oder was?
Ich wollt' nur nen Weg vorschlagen, andere Typen die implizit in unsigned konvertiert werden können, auszuschließen. Man muss nicht gleich böse werden.Nö, das war eine ernst gemeinte Aussage und Frage.
Na dann...
Wenn der Compiler genügend C++11 Unterstützung hat, nehme ich static_assert. Das ist viel besser zu lesen und erlaubt gleich eine Fehlermeldung die der Compiler ausgeben kann (und kein Kommentar, zu dem man, wenn man nicht in ner IDE kompiliert, mühselig gehen muss).
Aber was hat dein Thema mit static assertions zu tun? Hatte das Gefühl du willst das ins lächerliche ziehen...

-
Wie sollte der Author einer mathematischen Funktion dem Nutzer vorschreiben, welche Wertebereiche okay sind?
Man könnte allerhöchstens mit einem (Runtime) assert prüfen ob n groß genug ist, um mit fak den Wertebereich von unsigned zu sprengen. Aber das würde wohl auch lästig langsame Debugbuilds produzieren.
Andere Typen deeten ist ja wohl auch ne dumme Idee. Wieso sollte man die Funktion so aufrufen müssen?
fak(unsigned(33))Nene ...
-
Wesentlich schlimmer als negative Zahlen ist ein fehlender Test, ob ein Überlauf stattfindet. Man sieht sofort, dass fak(-1) irgendwie suboptimal ist. Das ist ein einfacher Fehler, um den man sich nicht kümmern muss, weil er sofort auffällt. Aber ob fak(125) funktioniert? Das geht eventuell durch das Review durch, ohne dass das jemand bemerkt.
//edit: Ethon_ war schneller

-
Ethon_ schrieb:
Andere Typen deeten ist ja wohl auch ne dumme Idee.

Jo, wie gesagt: einfach range checks, vielleicht ne assertion. Ne assertion wäre wohl das Beste.unsigned long long fak_impl(uint8_t n) { if( n == 0 || n == 1 ) return 1; return n * fak(n-1); } unsigned long long fak(uint8_t n) { assert( n < 26 ); return fak_impl(n); }
-
Sone schrieb:
Ethon_ schrieb:
Andere Typen deeten ist ja wohl auch ne dumme Idee.

Jo, wie gesagt: einfach range checks, vielleicht ne assertion. Ne assertion wäre wohl das Beste.unsigned long long fak_impl(uint8_t n) { if( n == 0 || n == 1 ) return 1; return n * fak(n-1); } unsigned long long fak(uint8_t n) { assert( n < 26 ); return fak_impl(n); }fak(258) ist ok?
Das muss noch geübt werden

-
Man muss sich auch fragen, wann man eine Fakulatät braucht.
Fakultäten werden wohl eher nicht mit negativen Argumenten verwendet werden.
Also bleibt der Überlauf. Der ist ein größeres Problem.
Aber ein Range-Check müsste genügen.
-
Gugelmoser schrieb:
wenn ein Benutzer einer Funktion ungültige Argumente übergibt,
Das impliziert für mich folgendes: Mit "Benutzer" ist hier ein Entwickler gemeint, der deine Funktion aufruft, also geht es bei deiner Funktion um eine Bibliotheksfunktion. Es geht hier offenbar nicht um irgendeinen Vollhonk vor der Tastatur, der bei "Alter" irgendeinen Buchstabensalat eintippt. Letzteren müsste der Entwickler, der die Eingabe programmiert, natürlich abtesten.
Im Falle der Fakultätsfunktion würde ich dokumentieren, dass sie nur für Werte von 0..max aufgerufen werden darf. Grund: Der Entwickler entscheidet, mit welchen Werten sie aufgerufen wird. Ruft er sie mit einer Nutzereingabe auf, muss er die Nutzereingabe sowieso überprüfen. Ruft er sie mit einer passenden Konstanten auf (z.B. fak(5)), dann ist von vornherein klar, dass das ein gültiges Argument ist und ein Range-Check wäre ein unnötiger Performanceverlust. Wenn das Argument irgendeine Variable ist, ist es performancetechnisch egal, ob der Entwickler den Range-Check macht oder ob ich ihn mache. MIt der Fehlerbehandlung muss er sich so oder so rumschlagen, und dann soll ers lieber konsistent auf seine Weise machen als sich von mir irgendeine Exception vorschreiben zu lassen, die er fangen muss.
Es gibt Dinge, auf die man gefasst sein muss, das sind z.B. unterbrochene Netzwerkverbindungen und Fehleingaben an der Tastatur. Gegen unfähige Programmierer sollte man sich nicht kostspielig absichern, damit bestraft man nur die fähigen Programmierer. Assertions sind da eine Grauzone, in API-Funktionen kann man sie einbauen und beugt damit ggf. Flüchtigkeitsfehlern vor.
camper schrieb:
Sone schrieb:
unsigned long long fak_impl(uint8_t n) { if( n == 0 || n == 1 ) return 1; return n * fak(n-1); } unsigned long long fak(uint8_t n) { assert( n < 26 ); return fak_impl(n); }fak(258) ist ok?
Die Funktion ist ok. Bei fak(258) sollte ein statischer Checker den Überlauf finden und bemängeln. Wenn ich uint8_t als Parameter festlege impliziert das Werte < 256, Verstöße dagegen kann ich in der Funktion auch nicht abprüfen.
Die 26 im assert ist für mich eher fraglich - kommt aber drauf an, wie groß unsigned long long ist...Spielerei mit statischer LUT:
#include <cstdint> #include <limits> #include <array> #include <cassert> constexpr unsigned long long cfak(uint8_t N) { return N <= 1 ? 1 : N * cfak(N-1); } constexpr bool calculable(uint8_t N) { return N==0 ? true : (cfak(N-1) < std::numeric_limits<unsigned long long>::max()/N); } template <uint8_t N, bool isCalculable = calculable(N)> struct fakTable { typedef typename fakTable<N+1>::type type; }; template <uint8_t N> struct fakTable<N, false> { typedef fakTable<N, false> type; const static uint8_t Nmax = N; const static std::array<unsigned long long,N> values; }; template <uint8_t N> const std::array<unsigned long long,N> fakTable<N, false>::values = []() { std::array<unsigned long long, N> v; for (uint8_t i = 0; i < N; ++i) v[i] = cfak(i); return v; }(); unsigned long long fak(uint8_t n) { typedef typename fakTable<0>::type table; assert(n < table::Nmax); return table::values[n]; } #include <iostream> using std::cout; int main() { cout << fak(1) << '\n'; cout << fak(5) << '\n'; cout << fak(10) << '\n'; cout << fak(11) << '\n'; cout << fak(12) << '\n'; cout << fak(32) << '\n'; }Hab leider keinen Weg gefunden, ein Array mit compilezeit-berechneter Länge und compilezeit-berechneten Einträgen zu erstellen...
Nachtrag: der gcc-4.5.1 (ideone) kann irgendweshalb fakTable<0>::type nicht finden
-
Hier was ideone-Compatibles
#include <cstdint> #include <limits> #include <array> #include <cassert> #include <cstddef> template <typename T, unsigned N> struct cfak { static const T value = T(-1) / N < cfak<T, N-1>::value ? 0 : N * cfak<T, N-1>::value; }; template <typename T> struct cfak<T, 0> { static const T value = 1; }; template <typename T, unsigned N = 0, bool = cfak<T, N>::value == 0> struct max_calculable { static const unsigned value = max_calculable<T, N+1>::value; }; template <typename T, unsigned N> struct max_calculable<T, N, true> { static const unsigned value = N; }; template <std::size_t... i> struct indexes; template <std::size_t N, typename = indexes<>> struct make_indexes; template <std::size_t N, std::size_t... i> struct make_indexes<N, indexes<i...>> { typedef typename make_indexes<N-1, indexes<0, (i+1)...>>::type type; }; template <std::size_t... i> struct make_indexes<0, indexes<i...>> { typedef indexes<i...> type; }; template <typename T, typename = typename make_indexes<max_calculable<T>::value>::type> struct fakTable; template <typename T, std::size_t... i> struct fakTable<T, indexes<i...>> { static const T values[sizeof...(i)]; }; template <typename T, std::size_t... i> const T fakTable<T, indexes<i...>>::values[sizeof...(i)] = { cfak<T, i>::value... }; unsigned long long fak(uint8_t n) { assert(n < max_calculable<unsigned long long>::value); return fakTable<unsigned long long>::values[n]; } #include <iostream> using std::cout; int main() { cout << fak(1) << '\n'; cout << fak(5) << '\n'; cout << fak(10) << '\n'; cout << fak(11) << '\n'; cout << fak(12) << '\n'; cout << fak(21) << '\n'; cout << fak(22) << '\n'; }
-
Die Mehrzahl von 'index' ist 'indices' :p
-
Im Englischen ist beides erlaubt: http://dict.leo.org/ende?lp=ende&lang=de&searchLoc=0&cmpType=relaxed§Hdr=on&spellToler=&search=indexes
-
pumuckl schrieb:
unsigned long long fak(uint8_t n) { typedef typename fakTable<0>::type table; assert(n < table::Nmax); return table::values[n]; }Wieso nicht
uint64_tals Rückgabetyp, pumuckl?
-
Gugelmoser schrieb:
pumuckl schrieb:
unsigned long long fak(uint8_t n) { typedef typename fakTable<0>::type table; assert(n < table::Nmax); return table::values[n]; }Wieso nicht
uint64_tals Rückgabetyp, pumuckl?
Hab ich gar nicht dran gedacht.
Mach aber lieber**uint_least64_t**draus.