Casten mit Carsten

Algorithmen, Sprachunabhängige Diskussionen zu Konzepten, Programmiersprachen-Design
Benutzeravatar
Xin
nur zu Besuch hier
Beiträge: 8859
Registriert: Fr Jul 04, 2008 11:10 pm
Wohnort: /home/xin
Kontaktdaten:

Re: Ausdrücke in C

Beitrag von Xin » Mi Jul 23, 2008 7:20 pm

Dirty Oerti hat geschrieben:
Xin hat geschrieben: Wozu gibt es in C++ new?
Was ist aber, wenn du C schreiben MUSST ?
Dann steht eine bessere Lösung direkt unter dem Satz, den Du gequotet hast ^^
Merke: Wer Ordnung hellt ist nicht zwangsläufig eine Leuchte.

Ich beantworte keine generellen Programmierfragen per PN oder Mail. Dafür ist das Forum da.

Benutzeravatar
Dirty Oerti
Beiträge: 2229
Registriert: Di Jul 08, 2008 5:05 pm
Wohnort: Thurndorf / Würzburg

Re: C:Ausdrücke

Beitrag von Dirty Oerti » Mi Jul 23, 2008 7:28 pm

Hups^^
Habe ich übersehen. :)
Bei Fragen einfach an daniel[ät]proggen[Punkt]org
Ich helfe gerne! :)
----------
Wenn du ein Licht am Ende des Tunnels siehst, freu dich nicht zu früh! Es könnte ein Zug sein, der auf dich zukommt!
----
It said: "Install Win95 or better ..." So I installed Linux.

Benutzeravatar
fat-lobyte
Beiträge: 1398
Registriert: Sa Jul 05, 2008 12:23 pm
Wohnort: ::1
Kontaktdaten:

Re: Ausdrücke in C

Beitrag von fat-lobyte » Mi Jul 23, 2008 8:21 pm

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 ;-) ).
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.
Haters gonna hate, potatoes gonna potate.

Benutzeravatar
Dirty Oerti
Beiträge: 2229
Registriert: Di Jul 08, 2008 5:05 pm
Wohnort: Thurndorf / Würzburg

Re: C:Ausdrücke

Beitrag von Dirty Oerti » Mi Jul 23, 2008 8:29 pm

Mal ne Frage zwischenrein:

Was genau ist an Casts schlecht?
Bei Fragen einfach an daniel[ät]proggen[Punkt]org
Ich helfe gerne! :)
----------
Wenn du ein Licht am Ende des Tunnels siehst, freu dich nicht zu früh! Es könnte ein Zug sein, der auf dich zukommt!
----
It said: "Install Win95 or better ..." So I installed Linux.

Benutzeravatar
Xin
nur zu Besuch hier
Beiträge: 8859
Registriert: Fr Jul 04, 2008 11:10 pm
Wohnort: /home/xin
Kontaktdaten:

Re: Ausdrücke in C

Beitrag von Xin » Mi Jul 23, 2008 8:50 pm

fat-lobyte hat geschrieben:
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".
Hehehe, dann dürften Klassen für Dich auch eine Krücke sein.Der Effekt ist eben nicht der selbe, weil...
fat-lobyte hat geschrieben: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
Wenn Du nicht casten kannst, kannst Du auch IntegerPointer.IntegerArray nicht casten. Wenn eine Typumwandlung wie (typ) oder static_cast< typ >() verboten ist, ist da nix zu casten.
Wenn Du Verbote bewußt umgehst, darfst Du auch gleich *( (int *) 0 ) = 0; reinpacken. Das führt bewußt zum Absturz, aber wenn man sich nicht an Regeln hält und da das gültiger Code ist, ist das ja nicht das Problem des Entwicklers.
fat-lobyte hat geschrieben:
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.
Du kannst wieder mit Unions arbeiten, du kannst aber auch die Chars zurecht setzen. Zweiteres kostet mehr Zeit, aber ist typsicher. Ersteres ist eine klare Einschränkung, was in dem Fall erlaubt ist und was nicht und kostet keine Rechenzeit.
fat-lobyte hat geschrieben:Ich sehe hier keinen Unterschied zu meiner Version, es sind genausoviele Casts drinnen, wie bei mir, nur sind sie schon wieder in Funktionen versteckt.
Verwendest Du sie mehrfach, musst Du weniger casten. Wieder ist es möglich mit Unions klarzustellen, was von wo nach wo geht und im Code zu beschreiben, was gewünschtes, erlaubtes Verhalten ist und was nicht.
fat-lobyte hat geschrieben: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 ganze 3 mal, und wenn das in einer Schleife geschieht geht kostbare Laufzeit verloren, und das für eine nicht-Lösung.
Dann pack die Funktion in ein Makro und fertig.
fat-lobyte hat geschrieben:
Xin hat geschrieben:Grober Designfehler...

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 ;-) ).

Code: Alles auswählen

struct Base
{
  virtual DerivedA * AsDerivedA() { return NULL; }
  virtual DerivedB * AsDerivedB() { return NULL; }
};

struct DerivedA : public Base
{
  DerivedA * AsDerivedA() { return this; }
};

struct DerivedB : public Base
{
  DerivedB * AsDerivedB() { return this; }
};
fat-lobyte hat geschrieben: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.
Es gibt Gründe zum Casten, aber in der Regel daher, da die Sprache keine Alternative anbietet oder weil ein anderer Programmierer Mist gebaut hat.
fat-lobyte hat geschrieben:Nur Fakt ist: In echten Anwendungen, mit echten Bibltiotheken, echten Programmierern mit verschiedenen Programmierstilen ist es oft nicht zu vermeiden.
Was allerdings meist auch an Designfehlern liegt.
fat-lobyte hat geschrieben: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.
Jow, aber ein Typ sollte sagen, welche Interpretationen gewünscht sind und welche nicht. Union macht das. Casts setzen sich über alles hinweg.
fat-lobyte hat geschrieben: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.
Ich verteufle casten nicht. Aber es hat bei mir den Stellenwert eines Gotos: man sollte es möglichst nicht einsetzen.

Ich habe einige Zeit überlegt, ob ich Casts bei mir zulassen soll und mich dazu durchgerungen, sie zuzulassen, aber ich könnte mir gut vorstellen, einen Schalter einzubauen, der erst angegeben werden muss, um casts zu erlauben.
Merke: Wer Ordnung hellt ist nicht zwangsläufig eine Leuchte.

Ich beantworte keine generellen Programmierfragen per PN oder Mail. Dafür ist das Forum da.

Benutzeravatar
fat-lobyte
Beiträge: 1398
Registriert: Sa Jul 05, 2008 12:23 pm
Wohnort: ::1
Kontaktdaten:

Re: C:Ausdrücke

Beitrag von fat-lobyte » Mi Jul 23, 2008 8:57 pm

Dirty Oerti hat geschrieben:Mal ne Frage zwischenrein:

Was genau ist an Casts schlecht?
Du interpretierst ein Datum (einzahl von Daten) als etwas, was es gar nicht ist. Wieso hast du es z.B. nicht gleich als solchen Typ deklariert, der er wirklich ist?
So zumindest die Grundregel. Ich finde es gibt ausnahmen zu dieser Regel. Xin findet dass es diese nicht gibt.
Haters gonna hate, potatoes gonna potate.

Benutzeravatar
Dirty Oerti
Beiträge: 2229
Registriert: Di Jul 08, 2008 5:05 pm
Wohnort: Thurndorf / Würzburg

Re: C:Ausdrücke

Beitrag von Dirty Oerti » Mi Jul 23, 2008 9:16 pm

Hm...die Definition von Cast kenn ich.
Aber was ist das schlechte dran?

Das ich etwas umdefiniere?

Hm..was ist schlecht daran, den Wertebereich zu verkleinern?

Code: Alles auswählen

long blub = 3423;
short dummy = (short) blub;
Ist ganz nützlich, wenn man nur den unteren Wertebereich haben möchte.
Bei Fragen einfach an daniel[ät]proggen[Punkt]org
Ich helfe gerne! :)
----------
Wenn du ein Licht am Ende des Tunnels siehst, freu dich nicht zu früh! Es könnte ein Zug sein, der auf dich zukommt!
----
It said: "Install Win95 or better ..." So I installed Linux.

Benutzeravatar
fat-lobyte
Beiträge: 1398
Registriert: Sa Jul 05, 2008 12:23 pm
Wohnort: ::1
Kontaktdaten:

Re: C:Ausdrücke

Beitrag von fat-lobyte » Mi Jul 23, 2008 9:26 pm

wieso hast dus dann nicht gleich als "short" deklariert, wenn du nur den unteren Wertebereich brauchst?
Haters gonna hate, potatoes gonna potate.

Benutzeravatar
Dirty Oerti
Beiträge: 2229
Registriert: Di Jul 08, 2008 5:05 pm
Wohnort: Thurndorf / Würzburg

Re: C:Ausdrücke

Beitrag von Dirty Oerti » Mi Jul 23, 2008 9:44 pm

Beispiel:

Ich habe einen Wert im Wertebereich von long. (Den gibt mir so z.b. der Benutzer ein)
Nun muss ich diesen Wert speichern.
Allerdings muss ich einmal den unteren Teil, ein anderes mal den oberen Teil speichern.
Eventuell muss ich auch viele solcher Werte speichern.(In einem Array)

Code: Alles auswählen

struct der_wert Wertarray[2000]
Und ich hab die Eingabe:

Code: Alles auswählen

long eingabe = /*großer Wert*/;
Wenn ich nun ohne Casts arbeite oder mir das Leben schwer machen will, dann brauch ich folgende struct:

Code: Alles auswählen

struct der_wert
{
   long oben;
   long unten;
}
Dabei könnte ich auch diese hier nehmen:

Code: Alles auswählen

struct der_wert
{
   short oben;
   short unten;
}
Die verbraucht viel weniger Speicher.
Außerdem ist mit einem kurzem Cast und einer Verschiebung alles fertig. Und das dann auch noch schnell.

Während ich bei der Methode ohne Cast erstmal das Unnötige wegbekommen muss.

*Hoffentlich verständlich*

MfG
Daniel
Bei Fragen einfach an daniel[ät]proggen[Punkt]org
Ich helfe gerne! :)
----------
Wenn du ein Licht am Ende des Tunnels siehst, freu dich nicht zu früh! Es könnte ein Zug sein, der auf dich zukommt!
----
It said: "Install Win95 or better ..." So I installed Linux.

Benutzeravatar
Xin
nur zu Besuch hier
Beiträge: 8859
Registriert: Fr Jul 04, 2008 11:10 pm
Wohnort: /home/xin
Kontaktdaten:

Re: C:Ausdrücke

Beitrag von Xin » Mi Jul 23, 2008 10:16 pm

fat-lobyte hat geschrieben:So zumindest die Grundregel. Ich finde es gibt ausnahmen zu dieser Regel. Xin findet dass es diese nicht gibt.
Ich schreib doch schon, dass es Ausnahmen gibt... aber die weisen auf einen Mangel hin. Wenn es nicht im Programmdesign liegt (egal ob in meinem oder dem der Lib, die ich nutze), dann auf einen Mangel Design der Programmiersprache.
Bevor man castet sollte man Alternativen suchen - bei meinem aktuellen Array-Template caste ich viel, weil ich noch auf keine schönere Lösung gekommen bin.
Merke: Wer Ordnung hellt ist nicht zwangsläufig eine Leuchte.

Ich beantworte keine generellen Programmierfragen per PN oder Mail. Dafür ist das Forum da.

Antworten