switch + goto (bzw. utf8)



  • drakon schrieb:

    Also ich verstehe den Sinn auch nicht ganz. Vielleicht hast du ja einfach ein blödes Beispiel genommen.
    Ich würde hier einfach die foo's und bar's trennen oder zumindest umdrehen.
    Sprich z.B bar mit den Parametern aufrufen und dann dort drin die Untescheidung mit switch/case machen.

    ja, kann schon sein, dass das bsp. an sich bissl doof war, aber ich habs jz einfach mit ner zusätzlichen schleife gelöst - wird der compiler schon wegoptimieren können ;o) und wenn nicht, dann ist das eben so^^
    das mit dem nicht inlinen können klang für mich schlimmer ;o)

    die möglichkeit, doppelten code zu nehmen hatte ich relativ schnell wieder verworfen - mir war dann auch wieder eingefallen, wieso ich die schonmal als doof eingeschätzt hatte 😉

    bb



  • @unskilled --> Deine Rückfragen
    Also: In früheren Zeiten war die Benutzung von 'goto' weitverbreitet. Die ersten BASIC-Compiler kannten nicht viel mehr zur Ablaufkontrolle. Hiermit wurde oft sog. Spaghetti-Code erzeugt mit wilden Sprüngen kreuz und quer. Solche Programme waren unübersichtlich und im Fehlerfall schwer nachvollziehbar gewesen. Deshalb gab es in den 1980er Jahren eine Kampagne gegen das 'goto'.Für gewöhnliche Iterationen nimmt man heute besser while-Schleifen.



  • berniebutt schrieb:

    @unskilled --> Deine Rückfragen
    Also: In früheren Zeiten war die Benutzung von 'goto' weitverbreitet. Die ersten BASIC-Compiler kannten nicht viel mehr zur Ablaufkontrolle. Hiermit wurde oft sog. Spaghetti-Code erzeugt mit wilden Sprüngen kreuz und quer. Solche Programme waren unübersichtlich und im Fehlerfall schwer nachvollziehbar gewesen. Deshalb gab es in den 1980er Jahren eine Kampagne gegen das 'goto'.Für gewöhnliche Iterationen nimmt man heute besser while-Schleifen.

    Die Basic-Compiler waren damals üblicherweise Basic-Interpreter.
    Und Aussagen wie "tue etwas nie" oder "tue etwas stets" sind irgendwie mit Vorsicht zu genießen. Es gibt durchaus ein paar Fälle, wo goto sinnvoll ist und die Übersichtlichkeit steigert. Zum Verlassen von tief geschachtelten Schleifen z.B. (sofern die Sprache nicht sowas wie exit LABEL hat, aber das ist schließlich auch nur ein besseres goto).
    Bei dem Teil von unskilled würde ich goto allerdings vermutlich auch nicht benutzen. Vielleicht kann man da etwas mit Funktionsüberladung basteln, dass etwas wartbarer ist.



  • berniebutt schrieb:

    Deshalb gab es in den 1980er Jahren eine Kampagne gegen das 'goto'.

    Kurzfassung:
    1968: Go To Statement Considered Harmful http://www.cs.utexas.edu/users/EWD/ewd02xx/EWD215.PDF
    über
    1978: With quält die Welt mit Modula II http://de.wikipedia.org/wiki/Modula-2
    bis
    1991: Qbasic verdängt goto-erzwingende Basic-Dialekte http://de.wikipedia.org/wiki/QBasic



  • Ich hatte dieses Problem neulich auch öfters (beim Umbau eines alten Codes).
    Da es eben öfters auftrat, habe ich mich für ein zweites Switch entschieden und dieses Verfahren an allen Stellen angewandt.
    Sieht insgesamt feiner aus, als es hier im ersten Moment erscheint.

    int x;
    int xtype;
    
    x = 2 /*0..4*/;
    
    switch (x)
    {
      case 1: foo1(); xtype = 0; break;
      case 2: foo2(); xtype = 1; break;
      case 3: foo3(); xtype = 2; break;
      case 4: foo4(); xtype = 3; break;
    }
    
    switch (xtype)
    {
      case 3:
        bar(a);
      case 2:
        bar(b);
      case 1:
        bar(c);
    }
    

    Eigentlich würde ich auch einen extra Funktionsaufruf bevorzugen, aber in diesem Fall kam es mir darauf an, dass keine neuen Funktionen entstehen, sondern alles in genau dem Rahmen abgehandelt wird, in dem es original vorlag.

    Prinzipiell könnte 'xtype' sogar x selbst sein, aber ich wollte die Möglichkeit einer Änderung des x im ersten switch offen halten.

    Wird eine richtige Enumerierung eingesetzt, sieht's noch sauberer aus.



  • unskilled schrieb:

    meine erste frage sollte offensichtlich sein: gibt es einen besseren weg, der euch einfällt?

    switch ist eines der überflüssigsten Sprachkonstrukte in C++. Fast immer ist es sinnvoller was anderes zu nehmen.

    #include <iostream>
    
    int main () {
        int x(4); // 0..4
    
        switch(x) {
        case 1:
            break;
        case 2:
            goto one_left;
        case 3:
            goto two_left;
        case 4:
            goto three_left;
    
        three_left:
            std::cout << "three_left\n";
        two_left:
            std::cout << "two_left\n";
        one_left:
            std::cout << "one_left\n";
        }
        std::cout << "end of switch\n\n";
    
        if (1 < x) {
            if (2 < x) {
                if (3 < x) {
                    std::cout << "three_left\n";
                }
                std::cout << "two_left\n";
            }
            std::cout << "one_left\n";
        }
        std::cout << "end of switch\n";
    }
    


  • ~john hier vergleichst du aber äpfel mit birnen...

    if (1 < x) {
            if (2 < x) {
                if (3 < x) {
                    std::cout << "three_left\n";
                }
                std::cout << "two_left\n";
            }
            std::cout << "one_left\n";
        }
        std::cout << "end of switch\n";
    

    vs.

    switch(x)
    {
      case 3:    std::cout << "three_left\n";
      case 2:    std::cout << "two_left\n";
      case 1:    std::cout << "one_left\n";
    }
    std::cout << "end of switch\n";
    

    noch dazu müsste es in deinem code if(x == 3) heißen und nicht if(x > 3) .

    bb



  • unskilled schrieb:

    ~john hier vergleichst du aber äpfel mit birnen...

    if (1 < x) {
            if (2 < x) {
                if (3 < x) {
                    std::cout << "three_left\n";
                }
                std::cout << "two_left\n";
            }
            std::cout << "one_left\n";
        }
        std::cout << "end of switch\n";
    

    vs.

    switch(x)
    {
      case 3:    std::cout << "three_left\n";
      case 2:    std::cout << "two_left\n";
      case 1:    std::cout << "one_left\n";
    }
    std::cout << "end of switch\n";
    

    noch dazu müsste es in deinem code if(x == 3) heißen und nicht if(x > 3) .

    Geht viel schneller und einfacher.

    #include <iostream>
    using namespace std;
    
    int main(){
    	int x;
    	cin>>x;
    	cout<<&x["\35\24\13"]["three_left\ntwo_left\none_left\nend of switch\n"];
    }
    


  • unskilled schrieb:

    meine erste frage sollte offensichtlich sein: gibt es einen besseren weg, der euch einfällt?

    Frage zu speziell. In welchem Kontext passiert das? Sind die Funktionen überhaupt sinnvoll? Ich kann nur über Verbesserungen nchdenken, wenn ich weiß, was da geschehen soll.



  • Da kann ich volkard zustimmen. Das Beispiel sagt nicht was du wirklich machst. Wahrscheinlich kann man es ganz anders machen, aber dazu müsste man erst mal wissen was es macht.



  • @Alle hier
    Die Diskussion bringt nichts mehr. Wir und der Fragesteller kommen da doch zur Frage "Wie entwerfe ich ein übersichtlich strukturiertes, leicht nachvollziehbares und wartungsfreundliches Programm?" Das ist dann ein vollkommen anderes Thema! Hier ging es um den Gebrauch von 'goto'. Und diesen sollte man wirklich nur dann einsetzen, wenn es anders unübersichtlicher bleibt. Auch ich setze gelegentlich deswegen 'goto' ein, was bei verschachtelten Schleifen sogar Sinn macht. Das lässt sich aber nur aus der konkreten Aufgabenstellung klären, die hier unbekannt ist.



  • ging um Zeichenkonvertierung: utf8 -> ucs4.
    So siehts jetzt aus:

    template <typename InputIterator>
    static ucs4 get_ucs4_from_utf8(InputIterator &iter, InputIterator end)
    {
    	unsigned char tmp = unsigned char(*iter++);
    	const unsigned char length = get_length(tmp);
    
    	switch(length)
    	{
    	case 1:
    		return ucs4(tmp);
    	case 2:
    		tmp = null_first_three_bits(tmp);
    		break;
    	case 3:
    		tmp = null_first_four_bits(tmp);
    		break;
    	case 4:
    		tmp = null_first_five_bits(tmp);
    		break;
    	}
    
    	ucs4 to_add = make_byte(tmp, --length);
    
    	for(; length >= 0; --length)
    	{
    		if(iter == end)
    			throw std::runtime_error("wrong utf8 encoding");
    		to_add = add(to_add, *iter++, length);
    	}
    
    	return to_add;
    }
    

    die Schleife wollte ich halt eigtl mit in das switch bauen, aber habs jetzt so gemacht, dass make_byte eben das erste byte(erster parameter) (um 2. parameter * 6) nach links shiftet und add den zweiten parameter um length*6 nach links shiftet und dann den ersten Parameter mit dem geshifteten addiert(bzw. nur nen or ausführt, weil dort ja eh noch 0en sind).
    wenn ich noch ma drüber nachdenke, könnte man auch wirklich nen zweites switch bauen... hmm...

    bb



  • volkard schrieb:

    Geht viel schneller und einfacher.

    Natürlich muß man entsprechend Code einfügen und dann klappt das, was Du hier vorschlägst nicht mehr.



  • unskilled schrieb:

    ~john hier vergleichst du aber äpfel mit birnen...

    Du liest nicht aufmerksam, das ist alles. Es gibt exakt das gleiche Ergebnis heraus! Und natürlich muß man anstelle der sinnfreien Ausgaben entsprechende Codeblöcke einfügen.



  • while ist auch komplett sinnlos, oder?

    else ist auch total sinnlos

    ...



  • berniebutt schrieb:

    Hier ging es um den Gebrauch von 'goto'. Und diesen sollte man wirklich nur dann einsetzen, wenn es anders unübersichtlicher bleibt. Auch ich setze gelegentlich deswegen 'goto' ein, was bei verschachtelten Schleifen sogar Sinn macht.

    Aber die verschachtelten Schleifen machen keinen Sinn, womit das goto wieder stirbt. 🕶



  • was machen null_first_three_bits und so? den code müßte ich erst entoptimieren.



  • der ist 0 optimiert, sondern erst mal nur so geschrieben, dass er läuft und verständlich ist...

    was machen null_first_three_bits und so?

    3mal darfst du raten ;o)

    es setzt die ersten 3 bit auf 0

    static unsigned char null_first_three_bits(unsigned char value)
    {// 0001 1111
    	return value & 0x1F;
    }
    

    Der Rest analog dazu... (ich find nur ein tmp & 0x1F so extrem nichtssagend deshalb verfrachte ich so was immer in extra fkt - wenn man binäre schreiben könnte und nicht nur hexadezimal wär das scho cool...)
    ucs4 ist nen typedef auf unsigned long

    das wars eigtl, oder?

    bb



  • unskilled schrieb:

    es setzt die ersten 3 bit auf 0

    static unsigned char null_first_three_bits(unsigned char value)
    {// 0001 1111
    	return value & 0x1F;
    }
    

    Der Rest analog dazu... (ich find nur ein tmp & 0x1F so extrem nichtssagend deshalb verfrachte ich so was immer in extra fkt

    Aha.
    Also wirds zu

    template <typename InputIterator>
    static ucs4 get_ucs4_from_utf8(InputIterator &iter, InputIterator end)
    {
    	unsigned char tmp = unsigned char(*iter++);
    	const unsigned char length = get_length(tmp);
    
    	if (length==0)
    		return ucs4(tmp);
    
    	tmp = tmp & (0xef>>length);
    
    	ucs4 to_add = make_byte(tmp, --length);
    
    	for (; length >= 0; --length)
    	{
    		if (iter == end)
    			throw std::runtime_error("wrong utf8 encoding");
    		to_add = add(to_add, *iter++, length);
    	}
    
    	return to_add;
    }
    

    (ungetestet)



  • ich hasse ja eigtl so nen bit-gefrickel in meinem quellcode...^^

    aber die idee hier
    tmp = tmp & (0xef>>length);
    ist klasse - danke! : >

    die schleife am ende würdest du lassen?!
    oder durch nen switch ersetzen?
    oder was ganz anderes?

    bb


Anmelden zum Antworten