String in einem Switch



  • Ist es irgendwie Möglich einen String in einem Switch-Zweig zu fixieren, dass wenn man z.B. "beenden" eingibt das Programm beendet oder "start", "wiederholen"? oder gibt es andere Möglichkeiten?



  • Die zu erwartenden stringbezeichnungen enummerieren und das Ergebnis des Vergleichs mit einer Funktion zurückggeben.

    Beispiel:

    enum STRNUMS {START, ENDE, WEITER, UNKNOWN};
    
    STRNUMS vgl(string x)
    {
        if(x == "start") 
            return START;
    
        return UNKNOWN;
    }
    
    // =========================
    
    switch( vgl(s1))
    {
    case START:
    // ...
    


    1. Man könnte die Strings auch hashen, aber dann muss man sich Gedanken darüber machen, ob es ein Problem ist, wenn was kollidiert.
    2. Oder man benutzt eine map und mappt strings auf funktionen (so mache ich das meistens)
      also zB:
    std::map <std::string, std::function <void()> > smap = {
        {"bla",  [](){ std::cout << "bla"; }},
        {"blub", [](){ std::cout << "blub"; }}
    };
    
    smap["bla"]();
    

    (was man mit makros und ein bisschen template kram auch sogar noch hübsch machen könnte, sodass es genauso aussieht wie switch)

    Aber vielleicht hat ja Arcoth noch eine Lösung, die mich umhaut 😉



  • Ich würde wie merano geschrieben hat Enums verwenden.
    Die wiegen nichts und es ist während der Programmierung klar was gemeint ist.

    Wenn du einen C++11 compiler hast würde ich aber strongly typed enums verwenden.

    enum class Commands { START, ENDE, WEITER, UNKNOWN};
    


  • Man kann das Ganze dann auch in eine Map packen:

    std::map <std::string, Commands > bar
    

    Spart den schwer wartbaren switch.



  • TGGC schrieb:

    Man kann das Ganze dann auch in eine Map packen:

    std::map <std::string, Commands > bar
    

    Spart den schwer wartbaren switch.

    Also ein, switch basierend auf Enums würde ich jetzt nicht annähernd als schwer wartbar bezeichnen und der overhead der Map würde mich persönlich auch massiv stören aber ist wahrscheinlich Geschmackssache.



  • Die Strings parsen kostet ohnehin viel Performance. In den Maps ist ein schlauer Algorithmus drin, der das uebernimmt. Es wird also keinen Overhead geben. Und man kann die Map relativ leicht durch eine andere mit besserer Performance ersetzen, wenn es noetig wird. Mach das mal mit der STRNUMS vgl(string x); Funktion, wenn die ein paar hundert Eintraege hat. Ueber das Enum kannst du dann natuerlich auch noch switchen, aber ein Array mit Funktionspointern indizieren bietet sich auch an.


  • Mod

    5cript schrieb:

    Aber vielleicht hat ja Arcoth noch eine Lösung, die mich umhaut 😉

    So etwas leider nicht, aber die Hashing Idee ist schon recht bekannt und wird i.d.R. so implementiert:

    constexpr std::uint64_t fnv(const char* p, std::size_t len,
                                std::uint64_t h = 0xcbf29ce484222325) {
        return len == 0? h : fnv(p+1, len-1, (h ^ *p) * 0x00000100000001b3);
    }
    
    constexpr std::uint64_t operator "" _fnv( char const* str, std::size_t len ) {
        return fnv(str, len);
    }
    
    // [...]
    
        switch(fnv(str.data(), str.size()))
        {
            case "Hallo"_fnv:
                std::cout << "Welt!";
                break;
            case "Welt"_fnv:
                std::cout << "Hallo!";
                break;
            default:
                std::cout << "Nochmal probier'n!";
        }
    

    Demo.


  • Mod

    @TGCC: Boah, spielst echt gut Tetris! Hut ab. 👍



  • Arcoth schrieb:

    5cript schrieb:

    Aber vielleicht hat ja Arcoth noch eine Lösung, die mich umhaut 😉

    So etwas leider nicht, aber die Hashing Idee ist schon recht bekannt und wird i.d.R. so implementiert:

    Gneau da hab ich auch eine Library für, die speichert hashed String Identifier in den Objekten, allerdings auch den orginalen String in einer Datenbank:

    sid::default_database database;
    sid::string_id string("Hallo", database);
    switch (string.hash_code())
    {
    case "Hello"_id:
      std::cout << "Hello\n";
      break;
    case "world"_id:
      std::cout << "world\n";
      break;
    case "World"_id:
      std::cout << "World\n";
      break;
    }
    

    github.com/foonathan/string_id
    Selbe Prinzip wie Arcoth beschrieben, auch selbe Hashfunktion.



  • Wenn man hashen will nur um des hashens willen, kann man natuerlich auch die map in eine hashmap umwandeln. Und wenn man das letzte Stueck Performance braucht, sollte man sich besser mal ueberlegen, warum dort wo der String generiert wird nicht auch gleich ein passender Enum generiert werden kann.

    @Arcoth: Danke, bin noch am ueben 😉


Log in to reply