web-dev-qa-db-fra.com

C/C++: commutateur pour les non-entiers

Souvent, je dois choisir quoi faire en fonction de la valeur d'un élément constant non-POD, quelque chose comme ceci:

switch( str ) {
  case "foo": ...
  case "bar": ...
  default:    ...
}

Malheureusement, switch ne peut être utilisé qu'avec des entiers: error: switch quantity not an integer.

Le moyen le plus simple d'implémenter une telle chose est alors d'avoir une séquence de ifs:

if( str == "foo" )      ...
else if( str == "bar" ) ...
else                    ...

Mais cette solution semble sale et devrait coûter O (n), où n est le nombre de cas, alors que cette partie de code pourrait coûter O (log n) au pire des cas avec une recherche binaire.

En utilisant certaines structures de données (comme Maps), il pourrait être possible d’obtenir un entier représentant la chaîne (O (log n)), puis d’utiliser une O(1) switch, ou d’implémenter un tri binaire statique par imbriquer ifs dans le bon sens, mais toujours ces hacks nécessiteraient beaucoup de codage, rendant tout plus complexe et plus difficile à maintenir.

Quelle est la meilleure façon de faire cela? (rapide, propre et simple, comme dans l'instruction switch)

51
peoro

En utilisant de la magie de macros et de modèles magiques, il est possible d’obtenir une recherche binaire déroulée avec compiletime avec une jolie syntaxe - mais le MATCHES ("case") doit être sort: fastmatch.h

NEWMATCH
MATCH("asd")
  some c++ code
MATCH("bqr")
  ... the buffer for the match is in _buf
MATCH("zzz")
  ...  user.YOURSTUFF 
/*ELSE 
  optional
*/
ENDMATCH(xy_match)

Cela va générer (à peu près) une fonction bool xy_match(char *&_buf,T &user), donc elle doit être au niveau extérieur. Appelez ça par exemple avec:

xy_match("bqr",youruserdata);

Et les breaks sont implicites, vous ne pouvez pas vous tromper. Ce n'est pas non plus très documenté, désolé. Mais vous constaterez qu'il existe encore plus de possibilités d'utilisation. REMARQUE: testé uniquement avec g ++.

Mettre à jour C++ 11:

Lambdas et la liste d'initialisation rendent les choses beaucoup plus jolies (pas de macros impliquées!):

#include <utility>
#include <algorithm>
#include <initializer_list>

template <typename KeyType,typename FunPtrType,typename Comp>
void Switch(const KeyType &value,std::initializer_list<std::pair<const KeyType,FunPtrType>> sws,Comp comp) {
  typedef std::pair<const KeyType &,FunPtrType> KVT;
  auto cmp=[&comp](const KVT &a,const KVT &b){ return comp(a.first,b.first); };
  auto val=KVT(value,FunPtrType());
  auto r=std::lower_bound(sws.begin(),sws.end(),val,cmp);
  if ( (r!=sws.end())&&(!cmp(val,*r)) ) {
    r->second();
  } // else: not found
}

#include <string.h>
#include <stdio.h>
int main()
{
  Switch<const char *,void (*)()>("ger",{ // sorted:                      
    {"asdf",[]{ printf("0\n"); }},
    {"bde",[]{ printf("1\n"); }},
    {"ger",[]{ printf("2\n"); }}
  },[](const char *a,const char *b){ return strcmp(a,b)<0;});           
  return 0;
}

C'est l'idée. Une implémentation plus complète peut être trouvée ici: switch.hpp .

Mise à jour 2016: Compile Time Trie

Ma plus récente prise sur ce problème utilise la métaprogrammation avancée de c ++ 11 en générer un search-trie au moment de la compilation. Contrairement aux approches précédentes, ceci gérera unsorted case-branches/strings just fine; ils doivent seulement être des littéraux de chaîne . G ++ autorise également constexpr pour eux, mais pas pour clang (à partir de HEAD 3.9.0/trunk 274233).

Dans chaque noeud, une instruction switch est utilisée pour exploiter le générateur de code avancé du compilateur.

La mise en œuvre complète est disponible sur github: smilingthax/cttrie .

55
smilingthax

En C++, vous pouvez obtenir des performances O(lg n) en ayant un std::map<std::string, functionPointerType>. (En C, vous pouvez implémenter ce qui est essentiellement identique mais ce serait plus difficile) Tirez sur le bon pointeur de fonction en utilisant std::map<k, v>::find et appelez ce pointeur. Bien sûr, cela ne sera pas aussi simple qu'une déclaration de commutateur prise en charge par la langue. D'un autre côté, si vous avez suffisamment d'éléments pour qu'il y ait une énorme différence entre O(n) et O(lg n), cela indique probablement que vous devriez opter pour un design différent en premier lieu.

Personnellement, j'ai toujours trouvé la chaîne ELSEIF plus lisible de toute façon.

28
Billy ONeal

Vous pouvez y parvenir sans utiliser aucune carte ou unordered_map comme ci-dessous . Comparez le premier caractère seul pour identifier quelle chaîne . Si plusieurs correspondances, vous pouvez vous rabattre sur la chaîne if/else dans cette instruction case ..__ Le nombre de comparaisons sera grandement réduit, si ce n'est plusieurs chaînes commençant par la même lettre.

char *str = "foo";
switch(*str)
{
case 'f':
    //do something for foo
    cout<<"Foo";
    break;
case 'b':
    //do something for bar
    break;
case 'c':
    if(strcmp(str, "cat") == 0)
    {
        //do something for cat
    }
    else if(strcmp(str, "camel") == 0)
    {
        //do something for camel
    }
}

Cela semble être une solution optimale sans aucun coût, même si ce n'est pas standard.

15
bjskishore123

Utilisez un if...else block. Vous n'avez pas vraiment de raison impérieuse de ne pas le faire, mis à part que ce n'est pas beau à regarder, et le bloc if...else est la solution la plus simple.

Tout le reste nécessite du code supplémentaire qui, comme on dit, augmente la complexité. Et cela déplace simplement la laideur ailleurs. Mais à un certain niveau, une comparaison de chaîne doit encore se produire. Maintenant, vous venez de couvrir avec plus de code.

Vous pourriez obtenir des gains de performances en utilisant une carte ou une carte de hachage, mais vous pouvez également obtenir des gains similaires, voire meilleurs, en choisissant simplement un ordre intelligent pour évaluer vos blocs if...else. Et opter pour une carte pour des raisons de performances ne constitue en réalité qu'une micro-optimisation prématurée.

10
John Dibling

En C, il existe deux solutions communes. La première consiste à conserver vos mots clés dans un tableau trié, par exemple

typedef struct Keyword {
    const char *Word;
    int         sub;
    int         type;
} Keyword;

Keyword keywords[] ={   /* keep sorted: binary searched */
    { "BEGIN", XBEGIN, XBEGIN },
    { "END",   XEND,   XEND },
    { "NF",    VARNF,  VARNF },
    { "atan2", FATAN,  BLTIN },
    ...
};

et faire une recherche binaire sur eux. La précédente provient directement du code source de awk de C, le grand maître Brian W. Kernighan.

L'autre solution, qui est O (min (m, n)) si n est la longueur de votre chaîne d'entrée et m la longueur du mot-clé le plus long, consiste à utiliser une solution à états finis telle qu'un programme Lex.

5
Fred Foo

Quelque chose comme ça serait trop complexe?

#include <iostream>
#include <map>

struct object
{
    object(int value): _value(value) {}

    bool operator< (object const& rhs) const
    {
        return _value < rhs._value;
    }

    int _value;
};

typedef void(*Func)();

void f1() {
    std::cout << "f1" << std::endl;
}

void f2() {
    std::cout << "f2" << std::endl;
}

void f3() {
    std::cout << "f3" << std::endl;
}

int main()
{
    object o1(0);
    object o2(1);
    object o3(2);

    std::map<object, Func> funcMap;
    funcMap[o1] = f1;   
    funcMap[o2] = f2;   
    funcMap[o3] = f3;

    funcMap[object(0)](); // prints "f1"
    funcMap[object(1)](); // prints "f2"
    funcMap[object(2)](); // prints "f3"
}
4
Simone

C’est semblable dans l’esprit aux solutions lambda et unordered_map, mais je pense que c’est le meilleur des deux mondes, avec une syntaxe très naturelle et lisible:

#include "switch.h"
#include <iostream>
#include <string>

int main(int argc, const char* argv[])
{
    std::string str(argv[1]);
    Switch(str)
        .Case("Apple",  []() { std::cout << "Apple" << std::endl; })
        .Case("banana", []() { std::cout << "banana" << std::endl; })
        .Default(       []() { std::cout << "unknown" << std::endl; });    
    return 0;
}

switch.h:

#include <unordered_map>
#include <functional>
template<typename Key>
class Switcher {
public:
    typedef std::function<void()> Func;
    Switcher(Key key) : m_impl(), m_default(), m_key(key) {}
    Switcher& Case(Key key, Func func) {
        m_impl.insert(std::make_pair(key, func));
        return *this;
    }
    Switcher& Default(Func func) {
        m_default = func;
        return *this;
    }
    ~Switcher() {
        auto iFunc = m_impl.find(m_key);
        if (iFunc != m_impl.end())
            iFunc->second();
        else
            m_default();
    }
private:
    std::unordered_map<Key, Func> m_impl;
    Func m_default;
    Key m_key;
};
template<typename Key>
Switcher<Key> Switch(Key key)
{
    return Switcher<Key>(key);
}
4
Aaron Frantisak

Voici un exemple de code qui fonctionne:

Cela devrait marcher.
(mais uniquement sur les chaînes de 4 octets ou moins)

Cela traite les chaînes comme des entiers de 4 octets.

Ceci est considéré, moche, pas portable, "hacky", et pas du tout bon style. Mais il fait ce que tu voulais.

#include "Winsock2.h"
#pragma comment(lib,"ws2_32.lib")

void main()
{
  char day[20];
  printf("Enter the short name of day");

  scanf("%s", day);

  switch(htonl(*((unsigned long*)day)))
  {
    case 'Sun\0':
      printf("sunday");
      break;
    case 'mon\0':
      printf("monday");
      break;
    case 'Tue\0':
      printf("Tuesday");
      break;
    case 'wed\0':
      printf("wednesday");
      break;
    case 'Thu\0':
      printf("Thursday");
      break;
    case 'Fri\0':
      printf("friday");
      break;
    case 'sat\0':
      printf("saturday");
      break;
  }
}

testé en MSVC2010

3
abelenky

Vous pouvez utiliser n’importe quel type c/c ++ changer d’implémentation . Votre code sera comme ceci:

std::string name = "Alice";

std::string gender = "boy";
std::string role;

SWITCH(name)
  CASE("Alice")   FALL
  CASE("Carol")   gender = "girl"; FALL
  CASE("Bob")     FALL
  CASE("Dave")    role   = "participant"; BREAK
  CASE("Mallory") FALL
  CASE("Trudy")   role   = "attacker";    BREAK
  CASE("Peggy")   gender = "girl"; FALL
  CASE("Victor")  role   = "verifier";    BREAK
  DEFAULT         role   = "other";
END

// the role will be: "participant"
// the gender will be: "girl"

Il est possible d'utiliser des types plus compliqués, par exemple std::pairs, ou des structures ou des classes prenant en charge les opérations d'égalité (ou des commandes pour le mode quick).

Caractéristiques

  • tout type de données prenant en charge les comparaisons ou la vérification de l'égalité
  • possibilité de construire des statemens de commutateur imbriqués en cascade.
  • possibilité de casser ou de passer à travers les déclarations de cas
  • possibilité d'utiliser des expressions de casse non constatnt
  • possible d'activer le mode statique/dynamique rapide avec la recherche dans les arbres (pour C++ 11)

Différences Sintax avec changement de langue est

  • mots-clés majuscules
  • besoin de parenthèses pour l'instruction CASE
  • point-virgule ';' à la fin des déclarations n'est pas autorisé
  • les deux points ':' à l'instruction CASE ne sont pas autorisés
  • besoin du mot clé BREAK ou FALL à la fin de l'instruction CASE

Pour C++97 langue utilisée recherche linéaire . Pour C++11 et plus moderne possible d'utiliser le mode quick recherche d'arborescence en mode où l'instruction return dans CASE devenant non autorisée . L'implémentation du langage C existe où le type char* et zéro des comparaisons de chaînes terminées sont utilisées.

Lisez plus d'informations sur cette implémentation de commutateur.

1
oklas

vous pouvez toujours utiliser un commutateur .. si vous connaissez les étiquettes auparavant .. (c'est assez désagréable (c.-à-d. pas de vérification, mais cela devrait être trivial d'ajouter si vous avez une chaîne valide terminée par null!), j'imagine que cela fonctionne plus vite que la plupart des options?

//labels: "abc", "foo", "bar", "ant" "do"

switch(lbl[0])
{
  case 'a':
  {
    switch(lbl[1])
    {
      case 'b': // abc
      case 'n': // ant
      default:  // doofus!
    }
  }
  case 'b':
  {
    switch(lbl[1])
    {
      case 'a': //bar
      default:  // doofus
    }
  }
  case 'd':
  {
    switch(lbl[1])
    {
      case 'o': //do
      default:  // doofus
    }
  }
  case 'f':
  {
    switch(lbl[1])
    {
      case 'o': //foo
      default:  // doofus
    }
  }
}

Bien sûr, si vous avez une très grande liste de "labels", cela deviendra assez compliqué ...

1
Nim

Vous pouvez utiliser mes macros switch , qui prennent en charge tous les types de valeurs. Dans quelques cas, utiliser op== plusieurs fois de suite est un ordre de grandeur plus rapide que de créer une carte à chaque fois et de la regarder. 

 sswitch(s) {
    scase("foo"): {
      std::cout << "s is foo" << std::endl;
      break; // could fall-through if we wanted
    }

    // supports brace-less style too
    scase("bar"):
      std::cout << "s is bar" << std::endl;
      break;

    // default must be at the end
    sdefault():
      std::cout << "neither of those!" << std::endl;
      break;
 }
1

Je pense à un générateur de hachage basé sur la métaprogrammation que vous pouvez utiliser comme dans cet exemple . Celui-ci est pour c ++ 0x, mais je suis sûr que vous pouvez le reproduire de la même manière pour le C++ standard.

1
Diego Sevilla

LLVM a llvm::StringSwitch que vous utiliseriez comme suit:

Color color = StringSwitch<Color>(argv[i])
   .Case("red", Red)
   .Case("orange", Orange)
   .Case("yellow", Yellow)
   .Case("green", Green)
   .Case("blue", Blue)
   .Case("Indigo", Indigo)
   .Cases("Violet", "purple", Violet)
   .Default(UnknownColor);

La principale victoire ici est que les collisions de hachage ne posent aucun problème: quoi qu'il en soit, les chaînes réelles sont toujours comparées avant qu'un cas ne soit accepté.

1
Kuba Ober

Il y a quelque temps, j'ai écrit une classe basée sur un modèle qui obtenait une sorte de commutateur équivalent, utilisable sur tout type de données. Cependant, certaines contraintes limitent ses champs d'application:

  • la tâche à accomplir sur chaque branche doit être un appel de fonction.
  • les fonctions à appeler ont un seul argument (ou aucun, ou deux, vous pouvez modifier le modèle, mais il doit être identique pour toutes les fonctions).
  • la valeur d'argument transmise aux fonctions sera la même dans tous les cas (mais elle est donnée au moment où le commutateur est exécuté).

Par exemple, vous voulez activer une valeur de type MyType, si elle est égale à value1, appelez function1("abc"), si elle est égale à value2, appelez function2("abc") (et ainsi de suite). Cela finirait comme:

// set up the object
//               Type  -        function sig       - function arg. type
SWITCH mySwitch< MyType, void(*)(const std::string&), std::string >;
mySwitch.Add( value1, function1 );
mySwitch.Add( value2, function2 );
mySwitch.AddDefault( function_def );

// process the value
MyType a =...// whatever.
mySwitch.Process( a, "abc" );

Fondamentalement, il encapsule un conteneur std :: map contenant la paire valeur/fonction. Il peut également gérer le "défaut", ce qui rend un commutateur si intéressant. Il peut être facilement adapté à d'autres situations. Voici le code:

template < typename KEY, typename FUNC, typename ARG >
class SWITCH
{
    public:
    SWITCH()
    {
      Def = 0; // no default function at startup
    }

    void Process( const KEY& key, ARG arg )
    {
      typename std::map< KEY, FUNC >::const_iterator it = my_map.find( key );
      if( it != my_map.end() )  // If key exists, call
         it->second( arg );    // associated function
      else               // else, call
        if( Def )       // default function, if there is one.
           Def( arg );  // else, do nothing
    }

    void Add( const KEY& key, FUNC my_func )
    {
      typename std::map< KEY, FUNC >::const_iterator it = my_map.find( key );
      if( it != my_map.end() )
      {
        throw "Already defined !\n";
      }
      my_map[ key ] = my_func;
    }

    void AddDefault( FUNC f )
    {
      Def = f;
    }

 private:
   std::map< KEY, FUNC > my_map;
   FUNC Def; // default function
 };

Autres détails sont ici .

0
kebs

Notez que le passage par const char * ne fonctionnerait pas comme prévu de toute façon, même si c'était autorisé.

Une chaîne C est en fait un pointeur sur char. Un code comme vous l'avez suggéré:

// pseudocode (incorrect C!):
switch(str) {
   case "a": ...
   case "b": ...
}

À condition que notre langue soit cohérente - elle comparerait les valeurs du pointeur , et non le contenu réel de la chaîne. La comparaison de chaînes nécessite une strcmp(); ainsi, même si le compilateur avait un cas spécial du type "si nous basculons contre un char*, utilisez strcmp() au lieu de == (ce qui serait probablement une mauvaise conception du langage), compilateur pour que cela fonctionne comme le hack O(1) avec entiers et sauts.

Donc, ne vous sentez pas mal pour C/C++, car ce n'est pas supporté. :)

Je recommande la solution O(logn) avec map (string -> funcptr) ou (string -> some abstract object) - si vous estimez que vous avez besoin d'évolutivité ici. Si vous ne le faites pas, la solution O(n) avec rien d'autre ne va pas particulièrement mal. C'est toujours clair, le code maintenable, donc il n'y a rien à redire.

0
Kos

Hache ton chemin vers la victoire

Vous pouvez utiliser une fonction de hachage à la compilation, comme dans ce glorieux débordement de pile réponse . Si vous créez les fonctions

  • int_crc32_s qui renvoie le hachage d'une chaîne au moment de l'exécution et 
  • int_crc32 qui renvoie le hachage d'une chaîne au moment de la compilation

tu es prêt. Pour gérer une fausse correspondance du crc de votre chaîne de clé et de votre chaîne de casse, vous devez inclure une vérification explicite de la correspondance. Cela n’affecte pas vraiment les performances car il ne s’agit que d’une simple vérification, mais cela la rend beaucoup plus laide et la version macro plus agréable.

J'ai trouvé ces deux chaînes qui ont le même CRC32 .

//two strings that yield same crc32
const char* collision1="DeferredAmbient_6_1_18-1of2_5";
const char* collision2="PostEffect_Lighting_18_6-0of1_8_14_13-1of2_19";

Sans macros

//without macros (you need to check for collisions)
switch( int_crc32_s(str.c_str()) )
{
    case int_crc32("foo"): if( str=="foo"){std::cout << "foo you\n"; break;}
    case int_crc32("bar"): if( str=="bar"){std::cout << "bar you\n"; break;}
    case int_crc32("baz"): if( str=="baz"){std::cout << "baz you\n"; break;}
    case int_crc32("PostEffect_Lighting_18_6-0of1_8_14_13-1of2_19"):
        if( str=="PostEffect_Lighting_18_6-0of1_8_14_13-1of2_19"){
            std::cout << "jackpot!\n"; break;
        }
    default: std::cout << "just you\n";
}

Avec des macros

//convenient macros
#define S_SWITCH( X ) const char* SWITCH_KEY(X.c_str()); switch( int_crc32_s(X.c_str()) )
#define S_CASE( X ) case int_crc32(X): if( strcmp(SWITCH_KEY,X) ){ goto S_DEFAULT_LABEL;}
#define S_DEFAULT S_DEFAULT_LABEL: default:

//with macros
S_SWITCH( str )
{
    S_CASE("foo"){ std::cout << "foo you\n"; break; }
    S_CASE("bar"){ std::cout << "bar you\n"; break; }
    S_CASE("baz"){ std::cout << "baz you\n"; break; }
    S_CASE("PostEffect_Lighting_18_6-0of1_8_14_13-1of2_19"){ std::cout << "jackpot!\n"; break; }
    S_DEFAULT{ std::cout << "just you\n"; }
}    

Mise en œuvre complète [ Gist ]

// This is a demonstration of using a COMPILE-TIME hash to do a
// switch statement with a string to answer this question.
//
// https://stackoverflow.com/questions/4165131/c-c-switch-for-non-integers
//
// It is based on the StackOverflow question:
// https://stackoverflow.com/questions/2111667/compile-time-string-hashing
//
// And the solution
// https://stackoverflow.com/questions/2111667/compile-time-string-hashing/23683218#23683218
//

#include <iostream>
#include <string>
#include <vector>
namespace detail {

// CRC32 Table (zlib polynomial)
static constexpr uint32_t crc_table[256] =
{
    0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
    0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
    0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
    0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
    0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
    0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
    0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
    0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
    0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
    0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
    0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
    0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
    0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
    0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
    0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
    0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
    0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
    0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
    0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
    0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
    0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
    0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
    0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
    0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
    0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
    0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
    0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
    0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
    0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
    0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
    0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
    0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
    0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
    0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
    0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
    0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
    0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
    0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
    0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
    0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
    0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
    0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
    0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
};

//constexpr combine
template<size_t idx>
constexpr uint32_t combine_crc32(const char * str, uint32_t part) {
  return (part >> 8) ^ crc_table[(part ^ str[idx]) & 0x000000FF];
}

//constexpr driver
template<size_t idx>
constexpr uint32_t crc32(const char * str) {
  return combine_crc32<idx>(str, crc32<idx - 1>(str));
}

//constexpr recursion stopper
template<>
constexpr uint32_t crc32<size_t(-1)>(const char * str) {
  return 0xFFFFFFFF;
}

//runtime combine
uint32_t combine_crc32_s(size_t idx, const char * str, uint32_t part) {
  return (part >> 8) ^ crc_table[(part ^ str[idx]) & 0x000000FF];
}

//runtime driver
uint32_t crc32_s(size_t idx, const char * str) {
  if( idx==static_cast<size_t>(-1) )return 0xFFFFFFFF;
  return combine_crc32_s(idx, str, crc32_s(idx-1,str));
}

} //namespace detail

//constexpr that returns unsigned int
template <size_t len>
constexpr uint32_t uint_crc32(const char (&str)[len]) {
  return detail::crc32<len - 2>(str) ^ 0xFFFFFFFF;
}

//constexpr that returns signed int
template <size_t len>
constexpr int int_crc32(const char (&str)[len]) {
  return static_cast<int>( uint_crc32(str) );
}

//runtime that returns unsigned int
uint32_t uint_crc32_s( const char* str ) {
  return detail::crc32_s(strlen(str)-1,str) ^ 0xFFFFFFFF;
}

//runtime that returns signed int
int int_crc32_s( const char* str) {
  return static_cast<int>( uint_crc32_s(str) );
}

//convenient macros
#define S_SWITCH( X ) const char* SWITCH_KEY(X.c_str()); switch( int_crc32_s(X.c_str()) )
#define S_CASE( X ) case int_crc32(X): if( strcmp(SWITCH_KEY,X) ){ goto S_DEFAULT_LABEL;}
#define S_DEFAULT S_DEFAULT_LABEL: default:

int main()
{
    std::string str;
    std::cin >> str;

    //two strings that yield same crc32
    const char* collision1="DeferredAmbient_6_1_18-1of2_5";
    const char* collision2="PostEffect_Lighting_18_6-0of1_8_14_13-1of2_19";

    //without macros (you need to check
    switch( int_crc32_s(str.c_str()) )
    {
        case int_crc32("foo"): if( str=="foo"){std::cout << "foo you\n"; break;}
        case int_crc32("bar"): if( str=="bar"){std::cout << "bar you\n"; break;}
        case int_crc32("baz"): if( str=="baz"){std::cout << "baz you\n"; break;}
        case int_crc32("PostEffect_Lighting_18_6-0of1_8_14_13-1of2_19"):
            if( str=="PostEffect_Lighting_18_6-0of1_8_14_13-1of2_19"){
                std::cout << "jackpot!\n"; break;
            }
        default: std::cout << "just you\n";
    }

    //with macros
    S_SWITCH( str )
    {
        S_CASE("foo"){ std::cout << "foo you\n"; break; }
        S_CASE("bar"){ std::cout << "bar you\n"; break; }
        S_CASE("baz"){ std::cout << "baz you\n"; break; }
        S_CASE("PostEffect_Lighting_18_6-0of1_8_14_13-1of2_19"){ std::cout << "jackpot!\n"; break; }
        S_DEFAULT{ std::cout << "just you\n"; }
    }
}
0
redwizard792