Xin hat geschrieben:Code: Alles auswählen
union IntegerPointer
{
void * MemoryHandling;
int * IntegerArray;
};
union IntegerPointer intP;
intP.MemoryHandling = malloc( ARRAYSIZE * sizeof( int ) );
intP.IntegerArray[0] = 1;
Das ist für mich eine Krücke. Eine Krücke um etwas zu umgehen, was Teil der Sprache ist und das mir den genau den gleichen Effekt liefert empfinde ich als schlicht und einfach "überflüssig".
Xin hat geschrieben:Mit dem Cast darf man integer_array auch mal nach (char *) konvertieren und auf einen String zeigen lassen. union IntegerPointer lässt das nicht zu.
Deine lösung ist keinen deut Typsicherer. Ich kann zwar nicht IntegerPointer casten, aber ich kann sehr wohl IntegerPointer.IntegerArray casten, was genau das gleiche ist wie ein normaler Zeiger. Ich sehe hier keinen Vorteil
Xin hat geschrieben:C++ Lösung: Klasse erstellen, bestehend aus eigenen Longs, die sich als char-Array zurückgeben lassen.
Die implementierung möchte ich sehn, die ohne Casts auskommt. Wie willst du z.B. ein long und ein short zusammenhängend als Array Speichern? Entweder du Castest einen Zeiger auf dein char- array zu einem int, oder du nimmst nen Zeiger auf deine ints und castest diese nach char*. Nur weil du es in eine Klasse Kapselst, ist das Problem noch lange nicht weg.
Xin hat geschrieben:C Lösung: Funktionen verwenden, die klare Aussagen treffen, was sie tun. Hier könnte man sich wieder mit Unions behelfen, aber hier würde ich Casting akzeptieren, wenn es nur in den passenden Funktionen stattfinden würde
Code: Alles auswählen
WriteUnsignedint( char ** buffer, unsigned int l )
{
*((unsigned int *) *buffer) = l;
*buffer += sizeof( long );
}
WriteUnsignedShort( char ** buffer, unsigned short s )
{
*((unsigned int *) *buffer) = s;
*buffer += sizeof( short );
}
...
unsigned char buffer[BUFSIZE];
unsigned char ** bufferPos = &buffer;.
WriteUnsignedLong( bufferPos, packed_length );
WriteUnsignedShort( bufferPos, header_length );
Ich sehe hier keinen Unterschied zu meiner Version, es sind genausoviele Casts drinnen, wie bei mir, nur sind sie schon wieder in Funktionen versteckt. Ich brauche nicht für eine Funktion, die mir mal shcnell nen Paketheader zusammenbauen soll 2-3 unterfunktionen! Das ist alles Laufzeit, denn C89 hat keine inline- funktionen, das bedeutet im endeffekt (im assembly- pseudocode)
Code: Alles auswählen
; auf der aufruferseite:
push l
push buffer
call WriteUnsignedint
; auf der funktionsseite
push ebp
mov ebp, esp
pusha
...
popa
pop ebp
ret
; und wieder zurück:
pop eax
pop eax
Das ganze 3 mal, und wenn das in einer Schleife geschieht geht kostbare Laufzeit verloren, und das für eine nicht-Lösung.
Xin hat geschrieben:Grober Designfehler...
Code: Alles auswählen
struct Base
{
bool is_A;
virtual void boringFunction() = 0;
Base(bool _is_A)
: is_A(_is_A)
{}
virtual void handler() = 0;
};
struct DerivedA : public Base // <- gehe ich mal von aus...
{
DerivedA()
: Base(true)
{}
virtual void boringFunction()
{}
void wickedFunction()
{}
void handler()
{
wickedFunction();
}
}
struct DerivedB : public Base
{
DerivedB()
: Base(false)
{}
virtual void boringFunction()
{}
void coolFunction()
{}
void handler()
{
coolFunction();
}
}
Pseodocode, oder einfache Beispiele drücken meist nicht das aus, was gemeint ist. In "Real World" Situationen gibt es nur sehr sehr selten perfekte Designs, oft sind verschiedene Design Patterns zusammengemischt und sind nur mit Kompromissen zueinander Kompatibel.
In diesem Fall ist "handler" keine globale Funktion, sondern eine callback Funktion von einer "Controller" Klasse, DerivedA und DerivedB sind Nachrichten, und werden von einer Klasse gesendet, die außer einem Funktionszeiger von der "Controller" Klasse nie etwas gesehen hat. Mag sein dass das design nicht Perfekt ist. Es ist aber ein guter Kompromiss, ich habe lange dran gefeilt (kannst Kerli fragen, wie ich ihn bei ICQ zugespammt habe
![Wink ;-)](./images/smilies/icon_e_wink.gif)
).
Das ganze ist ein Feature, dass man "Vererbung" nennt, und dazu gehören diese Casts noch dazu. Diese Technik wird auch in Bibliotheken verwendet, die nicht unter den 10 schlechtesten Libs sind, und zwar wxWidgets. Da gibts zum Beispiel die
wxEvent Klasse, die die Basisklasse für viele andere Klassen ist.
Xin hat geschrieben:Casten ist immer nur ein Armutszeugnis für einen Entwickler. In Beispiel 2 ist casten eine Vereinfachung, Lösung 1 würde hier auch greifen. Lösung 1 ist im letztendlich das gleiche wie Casten, aber es ist vorher klar, dass man nicht überall hinkonvertieren kann, sondern nur zu festgelegte Typen.
Casten in C++ ist ein Zeichen ist so gut wie überhaupt nicht mehr notwendig. Es gibt Fälle mit Templates, da komme ich nicht um Casten rum, um Redundanz zu vermeiden. Bevor ein Cast verwendet wird, sollte man lieber mal ein paar Tage in sich gehen und überlegen, was es für Alternativen gibt.
Ich gebe dir Recht in dem Punkt, dass Casten in den meisten Fällen zu vermeiden ist. Ich gebe dir auch recht, dass casten ein
Anzeichen eines Designfehlers ist, und auch dass man
Generell casten vermeiden sollte.
Nur Fakt ist: In echten Anwendungen, mit echten Bibltiotheken, echten Programmierern mit verschiedenen Programmierstilen ist es oft nicht zu vermeiden.
Auch finde ich es etwas ungerecht vom "Armutszeugnis" zu sprechen und Casten grundsätzlich zu verteufeln. Und zwar nicht nur aus dem Grund dass es tausende (hunderttausende?) Programmierer gibt, und Millionen von Codezeilen in denen Casts vorkommen. Eine Ansammlung von Bits sind Daten, die Interpretiert werden. Manchmal ist es notwendig und sinnvoll diese Daten anders zu interpretieren.
Ich meine, man
darf einfach nicht generell Sprachfeatures verteufeln. Natürlich soll man Casts vermeiden, natürlich soll man keine goto's anwenden und auch so wenig wie möglich mit dem Präprozessor machen. Aber all diese sprachfeatures haben ihre Nischen, und müssen manchmal verwendet werden.