C / C ++: cambiar por no enteros

A menudo tengo que elegir qué hacer de acuerdo con el valor de un elemento constante no POD, algo como esto:

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

Lamentablemente, el switch solo se puede usar con números enteros: error: switch quantity not an integer .

La forma más trivial de implementar tal cosa es tener una secuencia de if s:

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

Pero esta solución parece sucia y debería costar O (n), donde n es el número de casos, mientras que ese fragmento de código podría costar O (log n) en el peor de los casos con una búsqueda binaria.

Usando algunas estructuras de datos (como Maps) podría ser posible obtener un número entero que represente la cadena (O (log n)), y luego usar un switch O (1), o uno podría implementar una clasificación binaria estática anidando if s de la manera correcta, pero aún así estos hacks requerirían mucha encoding, haciendo que todo sea más complejo y más difícil de mantener.

¿Cuál es la mejor manera de hacer esto? (rápido, limpio y simple, como la statement de switch es)

    Usando magia desagradable de macro y plantilla es posible obtener una búsqueda binaria desenrollada en tiempo de comstackción con una syntax bonita, pero los MATCHES (“casos”) deben ser ordenados : 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) 

    Esto generará (más o menos) una función bool xy_match(char *&_buf,T &user) , por lo que debe estar en el nivel exterior. Llámalo, por ejemplo, con:

     xy_match("bqr",youruserdata); 

    Y las break son implícitas, no se pueden caer. Tampoco está muy documentado, lo siento. Pero encontrará que hay más posibilidades de uso, eche un vistazo. NOTA: Solo probado con g ++.

    Actualizar C ++ 11:

    Lambdas y la lista de inicializadores hacen las cosas mucho más bonitas (¡no se necesitan macros!):

     #include  #include  #include  template  void Switch(const KeyType &value,std::initializer_list> sws,Comp comp) { typedef std::pair 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  #include  int main() { Switch("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; } 

    Esa es la idea. Se puede encontrar una implementación más completa aquí: switch.hpp .

    Actualización 2016: tiempo de comstackción trie

    Mi última versión de este problema utiliza una metaprogtwigción c ++ 11 avanzada para generar un trie de búsqueda en tiempo de comstackción. A diferencia de los enfoques anteriores, esto maneja casos-twigs / cadenas sin clasificar muy bien; solo tienen que ser literales de cadena. G ++ también permite constexpr para ellos, pero no clang (a partir de HEAD 3.9.0 / trunk 274233).

    En cada nodo trie se utiliza una statement de conmutación para aprovechar el generador de código avanzado del comstackdor.

    La implementación completa está disponible en github: smilingthax / cttrie .

    En C ++, puede obtener el rendimiento O(lg n) al tener std::map . (En C podría implementar lo que esencialmente era lo mismo, pero sería más difícil) Extraiga el puntero de función correcto usando std::map::find , y llame a ese puntero. Por supuesto, eso no va a ser tan simple como una statement de cambio compatible con el lenguaje. Por otro lado, si tiene suficientes elementos que va a haber una gran diferencia entre O(n) y O(lg n) , eso es probablemente una indicación de que debe ir a un diseño diferente en primer lugar.

    Personalmente, siempre he encontrado la cadena ELSEIF más legible de todos modos.

    Puedes lograrlo sin usar ningún mapa o mapa no ordenado como a continuación. Compara el primer carácter solo para identificar qué cadena. Si hay más de una coincidencia, puede recurrir a la cadena if / else dentro de esa statement de caso. El número de comparaciones se reducirá en gran medida si no hay muchas cadenas que comiencen con la misma letra.

     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 } } 

    Esta parece ser la solución óptima sin ningún costo, aunque no es estándar.

    Use un if...else block . En realidad, no tienes una razón convincente para no hacerlo, aparte de que no es bonito de ver, y el bloque if...else es la solución más sencilla.

    Todo lo demás requiere un código adicional que, digamos, aumenta la complejidad. Y solo mueve la fealdad a otro lado. Pero en algún nivel, todavía tiene que ocurrir una comparación de cadenas. Ahora lo has tapado con más código.

    Puede obtener algunos aumentos de rendimiento mediante el uso de un mapa o un mapa hash, pero también puede obtener ganancias similares, si no mejores, simplemente eligiendo una orden inteligente para evaluar sus bloques if...else . Y cambiar a un mapa por razones de rendimiento es en realidad una micro-optimización prematura.

    En C, hay dos soluciones comunes. La primera es mantener sus palabras clave en una matriz ordenada, por ejemplo

     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 }, ... }; 

    y hacer una búsqueda binaria en ellos. Lo anterior es directamente del código fuente de awk del gran maestro de C Brian W. Kernighan.

    La otra solución, que es O (min ( m , n )) si n es la longitud de su cadena de entrada y m la longitud de la palabra clave más larga, es usar una solución de estado finito como un progtwig Lex.

    ¿Algo así sería demasiado complejo?

     #include  #include  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 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" } 

    Esto es similar en espíritu a las soluciones lambda y unordered_map, pero creo que esto es lo mejor de ambos mundos, con una syntax muy natural y legible:

     #include "switch.h" #include  #include  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  #include  template class Switcher { public: typedef std::function 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 m_impl; Func m_default; Key m_key; }; template Switcher Switch(Key key) { return Switcher(key); } 

    Aquí hay un código de ejemplo que funciona:

    Esto debería funcionar.
    (SÓLO en cadenas de 4 bytes o menos)

    Esto trata las cadenas como enteros de 4 bytes.

    Esto se considera feo, no portátil, “hacky”, y para nada un buen estilo. Pero hace lo que querías.

     #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; } } 

    probado en MSVC2010

    Me viene a la mente un generador de hash basado en metaprogtwigción que puedes usar como en este ejemplo . Este es para c ++ 0x, pero estoy seguro de que puedes reproducirlo de manera similar para C ++ estándar.

    aún podrías usar un interruptor … si conoces las tags de antemano … (esto es bastante desagradable (es decir, no hay cheques, pero eso debería ser trivial para agregar mientras tengas una cadena con terminación nula válida), debería imaginar que esto funciona más rápido que la mayoría de las opciones?

     //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 } } } 

    Por supuesto, si tienes una lista muy grande de “tags”, esto se volverá bastante complicado …

    Puede usar mis macros de conmutación , que admiten todo tipo de tipos de valores. En algunos casos, usar op== unas pocas veces seguidas es un orden de magnitudes más rápido que crear un mapa cada vez y buscar en él.

      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; } 

    Puede utilizar cualquier tipo de implementación de interruptor c / c ++. Tu código será así:

     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" 

    Es posible usar tipos más complicados, por ejemplo std::pairs o cualquier estructura o clase que soporte operaciones de igualdad (o comarisions para modo rápido ).

    Caracteristicas

    • cualquier tipo de datos que apoyen las comparaciones o verifiquen la igualdad
    • posibilidad de construir estados de conmutación nesteds en cascada.
    • posibilidad de romper o caer a través de declaraciones de casos
    • posibilidad de usar expresiones de casos no constantes
    • Posibilidad de habilitar el modo estático / dynamic rápido con la búsqueda de árbol (para C ++ 11)

    Las diferencias de Sintax con el cambio de idioma son

    • palabras clave mayúsculas
    • necesita paréntesis para la statement CASE
    • punto y coma ‘;’ al final de las declaraciones no está permitido
    • colon ‘:’ en la instrucción CASE no está permitido
    • necesita una palabra clave BREAK o FALL al final de la sentencia CASE

    Para el C++97 utilizado la búsqueda lineal. Para C++11 y más moderno es posible usar quick modo quick búsqueda de árbol wuth donde la statement de retorno en CASE no está permitida. La implementación del lenguaje C existe donde se usan las comparaciones de cadenas de tipo char* y terminadas en cero.

    Lea más sobre esta implementación de conmutadores.

    Tenga en cuenta que la conmutación por const char * no funcionaría de la manera prevista, incluso si estuviera permitida.

    AC String es en realidad un puntero a char. Un código como usted sugirió:

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

    Siempre que nuestro lenguaje sea coherente, compararía los valores del puntero y no el contenido real de la cadena. La comparación de cadenas necesita un strcmp() , por lo que incluso si el comstackdor tuviera un caso especial como “si cambiamos a un char* , use strcmp() lugar de == (que de todos modos sería un diseño deficiente), entonces de todos modos sería imposible para el comstackdor hacer que esto funcione como el O (1) hackeo con enteros y saltos.

    Así que no te sientas mal por C / C ++ ya que no es compatible. 🙂

    Recomiendo la solución O (logn) con map (string -> funcptr) o (string -> some abstract object) – si cree que necesita escalabilidad aquí. Si no lo hace, no hay nada particularmente malo con la solución O (n) con else if’s. Todavía está claro, es un código que se puede mantener, así que no hay nada de lo que sentirse mal.

    Hace algún tiempo, escribí una clase de plantilla para lograr algún tipo de conmutador equivalente, utilizable en cualquier tipo de datos . Sin embargo, existen algunas limitaciones que limitan sus campos de aplicación:

    • la tarea para lograr en cada twig debe ser una llamada a función.
    • las funciones a llamar tienen un único argumento (o ninguno, o dos, puede modificar la plantilla, pero tiene que ser el mismo para todas las funciones).
    • el valor de argumento pasado a las funciones será el mismo en todos los casos (pero se da en el momento en que se ejecuta el cambio).

    Como ejemplo, digamos que desea activar un valor de tipo MyType , si es igual a value1 , value1 function1("abc") , si es igual a value2 , value2 function2("abc") (y así sucesivamente) ) Esto terminaría como:

     // 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" ); 

    Básicamente, envuelve un contenedor std :: map, que contiene el valor / función de par. También puede manejar el “predeterminado”, que hace que un cambio sea tan interesante. Se puede adaptar fácilmente a otras situaciones. Aquí está el código:

     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 }; 

    Otros detalles están aquí .

    LLVM tiene llvm::StringSwitch que llvm::StringSwitch la siguiente manera:

     Color color = StringSwitch(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 gran victoria aquí es que no hay problemas debido a las colisiones hash: no importa qué, las cadenas reales siempre se comparan antes de que se acepte un caso.

    Hash tu camino a la victoria

    Puede usar una función hash en tiempo de comstackción como en esta gloriosa respuesta de desbordamiento de stack. Si creas las funciones

    • int_crc32_s que devuelve un hash de una cadena en tiempo de ejecución y
    • int_crc32 que devuelve un hash de una cadena en tiempo de comstackción

    estás listo. Para manejar una coincidencia falsa del crc de su cadena de clave y cadena de mayúsculas y minúsculas, debe incluir una verificación explícita de una coincidencia. Esto realmente no afecta el rendimiento porque es solo una comprobación, pero lo hace mucho más feo y la versión de macros se ve mucho mejor.

    Encontré estas dos cadenas que tienen el mismo 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"; 

    Sin 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"; } 

    Con 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"; } } 

    Implementación completa [ 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/cc-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  #include  #include  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 constexpr uint32_t combine_crc32(const char * str, uint32_t part) { return (part >> 8) ^ crc_table[(part ^ str[idx]) & 0x000000FF]; } //constexpr driver template constexpr uint32_t crc32(const char * str) { return combine_crc32(str, crc32(str)); } //constexpr recursion stopper template<> constexpr uint32_t crc32(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(-1) )return 0xFFFFFFFF; return combine_crc32_s(idx, str, crc32_s(idx-1,str)); } } //namespace detail //constexpr that returns unsigned int template  constexpr uint32_t uint_crc32(const char (&str)[len]) { return detail::crc32(str) ^ 0xFFFFFFFF; } //constexpr that returns signed int template  constexpr int int_crc32(const char (&str)[len]) { return static_cast( 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( 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"; } } }