Fragen? Antworten! Siehe auch: Alternativlos
Also habe ich mich heute entschieden, ein paar Makros zu machen, mit denen man unabhängig vom Integer-Typ Addieren und Subtrahieren kann, und der sagt einem dann, ob das überläuft oder nicht. Nun, das ist leichter gesagt als getan. Meine Anforderung ist, daß ich dem Makro nicht die Typen der beteiligten Variablen sagen will, weil das fehlerträchtig wäre. Mein API sieht so aus:
int a;Man sieht schon, wieso das ein Makro werden soll, sonst müsste man da immer Adressen nehmen und übergeben, und es soll ja auch möglichst effizient werden. Das Makro muss jetzt folgendes tun:
if (add_of(a,17,23)) handle_overflow();
if (INT_MAX-23 <= 17)Nur, woher weiß das Makro, daß es INT_MAX nehmen muss? Bei gcc gibt es eine typeof Extension für sowas, mit der kann man typunabhängige MIN und MAX Makros bauen. Das ist auch komplizierter, als es klingt, weil man dabei erkennen muss, ob der Typ vorzeichenbehaftet ist oder nicht, und dann darf man auch selber keinen Integer-Overflow verursachen, falls gcc noch renitenter wird als es heute schon ist. Hier ist mein Code:
return 1;
else
dest=23+17;
#define __MIN_UNSIGNED(type) ((type)0)Ich vergleiche in dem Makro mit 1 statt mit 0, weil gcc sonst bei unsigned-Typen eine Warnung wirft, daß da immer 0 rauskommt, und der 0-Fall mit beiden Code-Varianten funktioniert.
#define __MIN_SIGNED(type) (((type)-1)<<(sizeof(type)*8-1))#define __MAX_UNSIGNED(type) (((type)-1))
#define __MAX_SIGNED_TEMP(type) (((type)1)<<(sizeof(type)*8-2))
#define __MAX_SIGNED(type) (__MAX_SIGNED_TEMP(type)+(__MAX_SIGNED_TEMP(type)-1))#define __MAX(type) ((type)-1 < 1?__MAX_SIGNED(type):__MAX_UNSIGNED(type))
#define __MIN(type) ((type)-1 < 1?__MIN_SIGNED(type):__MIN_UNSIGNED(type))
Nachdem das vom Tisch ist, brauchen wir noch ein Makro, das erkennt, ob eine Zuweisung legal ist. Konkret, wenn man einem unsigned int -5 zuweist, dann will man das mitkriegen. Selbst wenn wir integer overflow in der Arithmetik abfangen, kann ja jemand 1 zu -5 addieren, und das einem unsigned int zuweisen wollen, oder mit 64-bit Zahlen rechnen. Hier ist mein Code dafür:
#define assign(dest,src) ({ typeof(src) __x=(src); typeof(dest) __y=__x; (__x==__y && ((__x<1) == (__y<1))?(int)((dest)=__y),0:1); })Dieser Typeof-Kram dient dazu, daß src nur einmal evaluiert wird (könnte ja sein, daß da was mit Seiteneffekten steht, sagen wir "*i++". Er dient auch dazu, daß ich die Variable einmal dem neuen Typ zuweise. Man sollte denken, wenn man danach _x==__y guckt, ist alles gesagt, aber: "(unsigned int)-23 == (signed int)-23". Daher müssen wir auch gucken, ob das Vorzeichen gleich ist.
Wir nähern uns langsam dem eigentlichen Addieren. Mein Code sieht so aus:
#define add_of(c,a,b) ({ typeof(a) __a=a; typeof(b) __b=b; (__b)<1?((__MIN(typeof(c))-(__b)<=(__a))?assign(c,__a+__b):1) : ((__MAX(typeof(c))-(__b)>=(__a))?assign(c,__a+__b):1); })Warum blogge ich das hier? Um zu zeigen, wie Scheiße C ist? Nein, um zu zeigen, was für bekloppte Fanatiker die gcc-Leute sind. Denn dieser unles- und -wartbare Haufen Code ist der Ersatz für folgendes:#define sub_of(c,a,b) ({ typeof(a) __a=a; typeof(b) __b=b; (__b)<1?((__MAX(typeof(c))-(__b)>=(__a))?assign(c,__a-__b):1) : ((__MIN(typeof(c))+(__b)<=(__a))?assign(c,__a-__b):1); })
if ((c=a+b)<b) handle_overflow()Könnt ihr euch ja selber mal überlegen, ob das eine schlaue Entscheidung der gcc-Leute war, diesen Weg zuzumachen. Ich kann mich da jetzt drüber aufregen und privat auf den Intel-Compiler umsteigen, aber das ist ja trotzdem Scheiße, wenn jemand von euch geownt wird, weil ihr gcc genommen habt.
Also, wer gerade Zeit und Lust hat: geht auf die gcc-Mailingliste, und erzählt den gcc-Leuten, wie sehr sie das gerade verkackt haben. Ohne eure Hilfe verstehen sie das offenbar nicht.
BTW: ich bin zwar gut, aber nicht unfehlbar, schon gar nicht um diese Tageszeit. Wer einen Fehler in diesen Makros sieht, meldet ihn bitte. Dieser Code landet in libowfat und dort kriegt ihr dann auch Credit. Ist schon eingecheckt, inklusive einer Testsuite (test/range.c). Viel Spaß.