Ungültige Funktionsargumente behandeln, ja oder nein?
-
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.
-
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?Weil long long mindestens(!) 64 Bit hat. Wurde mit C++11 in den Standard aufgenommen.
-
Cyres schrieb:
Weil long long mindestens(!) 64 Bit hat. Wurde mit C++11 in den Standard aufgenommen.
Ah mindestens.
Demnach ist es also schnuppe, ob unsigned long longoderuint_least64_t.
-
Gugelmoser schrieb:
Cyres schrieb:
Weil long long mindestens(!) 64 Bit hat. Wurde mit C++11 in den Standard aufgenommen.
Ah mindestens.
Demnach ist es also schnuppe, ob unsigned long longoderuint_least64_t.Trotzdem ist es deutlich besser, direkt zu schreiben, worum es einem bei einem Skalar geht.
Wenn manint_least64_tschreibt, weiß der Leser sofort, was für Anforderungen der Datentyp zu entsprechen hat.
-
Sone schrieb:
[Trotzdem ist es deutlich besser, direkt zu schreiben, worum es einem bei einem Skalar geht.
Wenn manint_least64_tschreibt, weiß der Leser sofort, was für Anforderungen der Datentyp zu entsprechen hat.Deswegen wollte ich pumuckl fragen, wieso er beim Parameter den C++11 Typen nimmt, beim Rückgabetyp jedoch nicht.

-
Gugelmoser schrieb:
Sone schrieb:
[Trotzdem ist es deutlich besser, direkt zu schreiben, worum es einem bei einem Skalar geht.
Wenn manint_least64_tschreibt, weiß der Leser sofort, was für Anforderungen der Datentyp zu entsprechen hat.Deswegen wollte ich pumuckl fragen, wieso er beim Parameter den C++11 Typen nimmt, beim Rückgabetyp jedoch nicht.

Ahhh....

-
Cyres schrieb:
Weil long long mindestens(!) 64 Bit hat. Wurde mit C++11 in den Standard aufgenommen.
Das überrascht mich jetzt.
Ich kann's aber nicht finden. Wo steht das?Danke,
FW
-
Furble Wurble schrieb:
Cyres schrieb:
Weil long long mindestens(!) 64 Bit hat. Wurde mit C++11 in den Standard aufgenommen.
Das überrascht mich jetzt.
Ich kann's aber nicht finden. Wo steht das?Danke,
FWwww.gidf.de

2 Sekunden: http://en.wikipedia.org/wiki/C%2B%2B11#Type_long_long_int
-
Cyres schrieb:
Furble Wurble schrieb:
Cyres schrieb:
Weil long long mindestens(!) 64 Bit hat. Wurde mit C++11 in den Standard aufgenommen.
Das überrascht mich jetzt.
Ich kann's aber nicht finden. Wo steht das?Danke,
FWwww.gidf.de

2 Sekunden: http://en.wikipedia.org/wiki/C%2B%2B11#Type_long_long_intLass mich meine Frage umformulieren: "Wo steht das im Standard?"
-
Furble Wurble schrieb:
Das überrascht mich jetzt.
Ich kann's aber nicht finden. Wo steht das?Gute Frage, ich kann es auch nicht finden. Das einzige, das ich finde, ist:
There are five standard signed integer types : “signed char”, “short int”, “int”, “long int”, and “long
long int”. In this list, each type provides at least as much storage as those preceding it in the list.CHAR_BIT Angaben zu den einzelnen Typen finde ich auch keine.
-
Die Wertebereiche stehen im C99-Standard, auf den sich C++11 bezieht.
-
Bashar schrieb:
Die Wertebereiche stehen im C99-Standard, auf den sich C++11 bezieht.
Ja - jetzt hab ich's auch.

18.3.3 Climits
besagt das in climits das gleiche wie in limits.h steht.Und in limits.h (c99/5.2.4.2 Numerical limits) stehen 64 bittige Bereiche für LLONG_MAX, LLONG_MIN, ULLONG_MAX.
Hmmm...fast ein bißchen schade, dass es nicht mehr so allgemein gehalten ist...