C/C++: switch for non-integers

C++CSwitch Statement

C++ Problem Overview


Often I need to choose what to do according to the value of a non-POD constant element, something like this:

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

Sadly switch can only be used with integers: error: switch quantity not an integer.

The most trivial way to implement such thing is then to have is a sequence of ifs:

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

But this solution looks dirty and should cost O(n), where n is the number of cases, while that piece of code could cost O(log n) at the worst case with a binary search.

Using some data structs (like Maps) it could be possible to obtain an integer representing the string ( O(log n) ), and then use an O(1) switch, or one could implement a static binary sort by nesting ifs in the right way, but still these hacks would require a lot of coding, making everything more complex and harder to maintain.

What's the best way to do this? (fast, clean and simple, as the switch statement is)

C++ Solutions


Solution 1 - C++

Using some nasty macro and template magic it's possible to get an unrolled binary search at compiletime with pretty syntax -- but the MATCHES ("case") have to be sorted: 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)

This will generate (roughly) a function bool xy_match(char *&_buf,T &user), so it must be at the outer level. Call it e.g. with:

xy_match("bqr",youruserdata);

And the breaks are implicit, you cannot fall-thru. It's also not heavily documented, sorry. But you'll find, that there are some more usage-possibilities, have a look. NOTE: Only tested with g++.

Update C++11:

Lambdas and initializer list make things much prettier (no macros involved!):

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

That's the idea. A more complete implementation can be found here: switch.hpp.

Update 2016: Compile time trie

My newest take on this problem uses advanced c++11 metaprogramming to generate a search-trie at compile time. Unlike the previous approaches, this will handle unsorted case-branches/strings just fine; they only have to be string-literals. G++ also allows constexpr for them, but not clang (as of HEAD 3.9.0 / trunk 274233).

In each trie node a switch-statement is utilized to harness the compiler's advanced code generator.

The full implementation is available at github: smilingthax/cttrie.

Solution 2 - C++

In C++, you can obtain O(lg n) performance by having a std::map<std::string, functionPointerType>. (In C you could implement what was essentially the same but it would be more difficult) Pull out the right function pointer using std::map<k, v>::find, and call that pointer. Of course, that's not going to be nearly as simple as a language supported switch statement. On the other hand, if you have enough items that there's going to be a huge difference between O(n) and O(lg n), that's probably an indication that you should be going for a different design in the first place.

Personally, I've always found the ELSEIF chain more readable anyway.

Solution 3 - C++

You can achieve it without using any map or unordered_map like below. Compare first character alone to identify which string. If more than one match, then you can fallback to if/else chain within that case statement. Number of comparisons will be greatly reduced if not many strings starting with same letter.

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

This looks to be optimal solution without any cost, even though its not standard.

Solution 4 - C++

Use an if...else block. You don't really have a compelling reason not to, aside from it not being pretty to look at, and the if...else block is the mostr straightforward solution.

Everything else requires additional code which as say say increases complexity. And it just moves the ugliness to elsewhere. But at some level, a string compare still has to happen. Now you've just covered it up with more code.

You might gain some performance increases by using a map or a hash map, but youcan also gain similar if not better gains by simply choosing a smart order to evaluate your if...else blocks. And switching to a map for performance reasons is really just premature micro-optimization.

Solution 5 - C++

In C, there are two common solutions. The first is to keep your keywords in a sorted array, say

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

and do a binary search on them. The previous is straight from the source code of awk by C grandmaster Brian W. Kernighan.

The other solution, which is O(min(m, n)) if n is the length of your input string and m the length of the longest keyword, is to use a finite-state solution such as a Lex program.

Solution 6 - C++

Something like that would be too much complex?

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

Solution 7 - C++

This is similar in spirit to the lambda and the unordered_map solutions, but I think this is the best of both worlds, with a very natural and readable syntax:

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

Solution 8 - C++

Here is example code that works:

This should work.
(but ONLY on strings that are 4-bytes or less)

This treats the strings as 4-byte integers.

This is considered, ugly, not portable, "hacky", and not at all good style. But it does do what you wanted.

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

tested in MSVC2010

Solution 9 - C++

You can use my switch macros, which support all kind of value types. For a few cases, using op== a few times in a row is an order of magnitudes faster than creating map each time and looking up in it.

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

Solution 10 - C++

It comes to my mind a metaprogramming-based hash generator that you can use like in this example. This one is for c++0x, but I'm sure you can reproduce it similarly for standard C++.

Solution 11 - C++

you could still use a switch.. if you know the labels before hand.. (this is quite nasty (i.e. no checks, but that should be trivial to add as long as you have a valid null terminated string!), I should imagine that this performs faster than most 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
    }
  }
}

Of course, if you have a very large list of "labels", this will become quite complicated...

Solution 12 - C++

You can use any type c/c++ switch implementation. Your code will be like this:

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"

It is possible to use more complicated types for example std::pairs or any structs or classes that support equality operations (or comarisions for quick mode).

Features

  • any type of data which support comparisions or checking equality
  • possibility to build cascading nested switch statemens.
  • possibility to break or fall through case statements
  • possibility to use non constatnt case expressions
  • possible to enable quick static/dynamic mode with tree searching (for C++11)

Sintax differences with language switch is

  • uppercase keywords
  • need parentheses for CASE statement
  • semicolon ';' at end of statements is not allowed
  • colon ':' at CASE statement is not allowed
  • need one of BREAK or FALL keyword at end of CASE statement

For C++97 language used linear search. For C++11 and more modern possible to use quick mode wuth tree search where return statement in CASE becoming not allowed. The C language implementation exists where char* type and zero-terminated string comparisions is used.

Read more about this switch implementation.

Solution 13 - C++

LLVM has llvm::StringSwitch that you'd use as follows:

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

The major win here is that there are no problems due to hash collisions: no matter what, the actual strings are always compared before a case is accepted.

Solution 14 - C++

Note that switching by const char* wouldn't work as intended anyway, even if it's was allowed.

A C String is actually a pointer to char. A code like you suggested:

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

Provided our language is consistent - it would compare the pointer values, and not the actual string content. Comparing strings needs a strcmp(), so even if the compiler had a special case like "if we're switching against a char*, use strcmp() instead of == (which would probably be poor language design anyway), then anyway it would be impossible for the compiler to make this work like the O(1) hack with integers and jumps.

So don't feel bad for C/C++ as it's not supported. :)

I recommend the O(logn) solution with map (string -> funcptr) or (string -> some abstract object) - if you feel you need scalability here. If you don't, there's nothing particularly wrong with the O(n) solution with else if's. It's still clear, maintainable code, so there's nothing to feel bad about.

Solution 15 - C++

Some time ago, I have written a templated class achieving some sort of switch equivalent, usable on any data type. However, there are some constraints that limit its application fields:

  • the task to achieve on each branch must be a function call.
  • the functions to call have a single argument (or none, or two, you can tweak the template, but it has to be the same for all functions).
  • the argument value passed to the functions will be the same in every case (but it is given at the moment the switch is executed).

As an example, say, you want to switch on a value of type MyType, if it is equal to value1, call function1("abc"), if it is equal to value2, call function2("abc") (and so on). This would end up as:

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

Basically, it wraps a std::map container, holding the pair value/function. It can also handle the "default", that makes a switch so interesting. It can be easily adapted to other situations. Here is the 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
 };

Other details are here.

Solution 16 - C++

Hash your way to Victory

You could use a compile-time hash function like in this glorious stack overflow answer. If you create the functions

  • int_crc32_s that returns a hash of a string at run-time and
  • int_crc32 that returns a hash of a string at compile-time

you are set. To handle a false match of the crc of your key string and case string, you need to include an explicit check for a match. This doesn't really affect the performance because it's just a single check, but it makes it much uglier and the macro version look that much nicer.

I found these two string that have the same 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";

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

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

Full Implementation [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"; }
    }
}

Solution 17 - C++

This answer is an additional work on the answer https://stackoverflow.com/a/4165312/10075467 for the same question by smilingthax, with modification of the Switch function to get result without the actual need of sorting (the purpose being achieved with map instead, complexity is still affected).

#include "iostream"
#include "algorithm"
#include "cstring"
#include "map"
#include "typeinfo"

using namespace std;

template <class key_t, class val_fn>
void Switch(const key_t switch_key, auto switch_cases, val_fn default_case = []{printf("not found\n");} )
{
    //using key_value_pair = pair<const key_t, val_fn>;
    for(auto x : switch_cases) { cout<<x.first<<" : "; x.second(); cout<<"\t"; } cout<<endl;
    auto match = switch_cases.find(switch_key);
    if(match == switch_cases.end()) { default_case(); return; }
    match -> second();
    //cout<<typeid(switch_cases).name();
}

int main()
{
    string switch_key = " 6ZG ";
    //getline(cin, switch_key);
    map <string, void (*)()> switch_cases = {
            { "gef", []{ printf("0\n"); } },
            { "hde", []{ printf("1\n"); } },
            { "ger", []{ printf("2\n"); } },
            { "aTe", []{ printf("0\n"); } },
            { "ymX", []{ printf("1\n"); } },
            { "zcx", []{ printf("16\n"); } },
            { "i5B", []{ printf("17\n"); } },
            { "5ui", []{ printf("18\n"); } },
            { "zkB", []{ printf("19\n"); } },
            { " zxw ", []{ printf(" 0 \n"); } },
            { " Aq0 ", []{ printf(" 1 \n"); } },
            { " uSa ", []{ printf(" 2 \n"); } },
            { " 3pY ", []{ printf(" 3 \n"); } },
            { " 6ZG ", []{ printf(" 4 \n"); } },
            { " PHT ", []{ printf(" 5 \n"); } },
            { " Jv9 ", []{ printf(" 6 \n"); } },
            { " 0NQ ", []{ printf(" 7 \n"); } },
            { " 4Ys ", []{ printf(" 8 \n"); } },
            { " GzK ", []{ printf(" 9 \n"); } }
        };
    Switch<string, void (*)()> ( switch_key, switch_cases);//, [](const string a, string b){ return a!=b;} );

    return 0;
}

Let map do the sorting thing for us. If we have an already sorted list of switch_cases, it'd be better again to use the initializer_list.

Edit: Recently used the same for one of me codechef submission to a (simple) problem of laddus. Here I have used it to return integer type instead of void.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionpeoroView Question on Stackoverflow
Solution 1 - C++smilingthaxView Answer on Stackoverflow
Solution 2 - C++Billy ONealView Answer on Stackoverflow
Solution 3 - C++bjskishore123View Answer on Stackoverflow
Solution 4 - C++John DiblingView Answer on Stackoverflow
Solution 5 - C++Fred FooView Answer on Stackoverflow
Solution 6 - C++SimoneView Answer on Stackoverflow
Solution 7 - C++Aaron FrantisakView Answer on Stackoverflow
Solution 8 - C++abelenkyView Answer on Stackoverflow
Solution 9 - C++Johannes Schaub - litbView Answer on Stackoverflow
Solution 10 - C++Diego SevillaView Answer on Stackoverflow
Solution 11 - C++NimView Answer on Stackoverflow
Solution 12 - C++oklasView Answer on Stackoverflow
Solution 13 - C++Kuba hasn't forgotten MonicaView Answer on Stackoverflow
Solution 14 - C++KosView Answer on Stackoverflow
Solution 15 - C++kebsView Answer on Stackoverflow
Solution 16 - C++wawieselView Answer on Stackoverflow
Solution 17 - C++Himanshu TanwarView Answer on Stackoverflow