source

C/C++: 정수 이외의 스위치

bestscript 2022. 11. 12. 08:31

C/C++: 정수 이외의 스위치

종종 다음과 같은 비POD 상수 요소의 가치에 따라 수행할 작업을 선택해야 합니다.

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

도 ★★★★★★★★★★★★★★★★.switch정수만 할 수 .error: switch quantity not an integer.

의 을 갖는 입니다.ifs:

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

단, 이 솔루션은 더러워서 O(n) 비용이 듭니다.여기서 n은 케이스 수이며, 바이너리 검색에서는 최악의 경우 O(log n) 비용이 들 수 있습니다.

등 n을 나타내는 한 후 O(1를할 수 .switch수 있습니다.ifs는 올바른 방법이지만, 여전히 이러한 해킹에는 많은 코딩이 필요하며, 모든 것이 더 복잡하고 유지보수가 더 어려워집니다.

(요?)switch★★★★★★★★★★★★★★★★★)

몇 가지 귀찮은 매크로와 템플릿 마법을 사용하면 컴파일 시 구문이 예쁜 바이너리 검색을 전개할 수 있지만 MATCHS("case")는 정렬해야 합니다.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)

가 생성됩니다.bool xy_match(char *&_buf,T &user)외부 레벨에 있어야 합니다.다음과 같이 입력합니다.

xy_match("bqr",youruserdata);

★★★★★★★★★★★★★★★★.break가을 스루또한 문서화되어 있지 않습니다. 알 수 g G++로 하다.

업데이트 C++11:

Lamdas와 이니셜라이저 목록을 사용하면 더 예뻐집니다(매크로가 포함되지 않음).

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

그게 아이디어야.보다 완전한 실장은 switch.hpp 입니다.

업데이트 2016: 컴파일 시간 trie

이 문제에 대한 저의 최근 견해는 고급 c++11 메타프로그래밍을 사용하여 컴파일 시 검색 트리를 생성합니다.이전 접근법과 달리 이 방법은 정렬되지 않은 대소문자(브랜치/스트링)를 적절하게 처리합니다. 문자열 리터럴만 있으면 됩니다.G++에서는 constexpr도 허용되지만 clang은 허용되지 않습니다(HEAD 3.9.0/trunk 274233).

각 trie 노드에서는 switch-statement를 사용하여 컴파일러의 고급 코드 생성기를 활용합니다.

완전한 구현은 github: smilingthax/cttrie에서 이용할 수 있습니다.

다음과 같이 map이나 unordered_map을 사용하지 않고 실행할 수 있습니다.첫 번째 문자만 비교하여 어떤 문자열을 식별하십시오.일치하는 항목이 여러 개 있는 경우 해당 케이스 스테이트먼트 내의 if/else 체인으로 폴백할 수 있습니다.같은 문자로 시작하는 문자열이 많지 않으면 비교 횟수가 크게 줄어듭니다.

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

이 솔루션은 표준 사양은 아니지만 비용 부담 없이 최적의 솔루션입니다.

C에는 두 가지 일반적인 솔루션이 있습니다.첫 번째는 키워드를 정렬된 배열로 유지하는 것입니다.

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

바이너리 검색을 하는 거죠앞의 것은 C 그랜드마스터 Brian W. Kernighan의 awk 소스코드에서 직접 나온 것입니다.

다른 솔루션(n이 입력 문자열 길이이고 m이 가장 긴 키워드 길이일 경우 O(min(m, n)))은 Lex 프로그램과 같은 유한 상태 솔루션을 사용하는 것입니다.

모든 종류의 값 유형을 지원하는 스위치 매크로를 사용할 수 있습니다.몇 가지 경우,op==연속 몇 번 지도는 매번 지도를 만들고 지도를 찾아보는 것보다 크기가 빠른 순서입니다.

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

에서는 C++로, C++로, C++로, C+로, C+로, C+로, C+로 수 있습니다.O(lg n)퍼 a a having std::map<std::string, functionPointerType> 것을 할 수 수 ) (C에서) 를 사용하여 함수 .std::map<k, v>::find포인터를 호출합니다.물론 이는 언어를 지원하는 switch 문처럼 간단하지는 않습니다., 이, 이, 아, 그, 그, 그, 그, 그, 그, 그, 그, 그, 그, on, on, on, on, on, on, on, on, on, on, on, on, on, on, on, on, on, on, on, on, on, , onO(n) ★★★★★★★★★★★★★★★★★」O(lg n)이는 애초에 다른 디자인을 선택해야 한다는 의미일 수 있습니다.

개인적으로, 저는 항상 엘시프 체인이 더 읽기 쉽다는 것을 발견했어요.

LLVM에는 다음과 같이 사용할 수 있습니다.

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

여기서 가장 큰 장점은 해시 충돌로 인한 문제가 없다는 것입니다.무슨 일이 있어도 케이스가 접수되기 전에 항상 실제 문자열이 비교됩니다.

이것은 lamda 및 unordered_map 솔루션과 정신적으로 비슷하지만, 매우 자연스럽고 읽기 쉬운 구문을 가진 두 가지 모두 최고라고 생각합니다.

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

'어울리다'를 사용하세요.if...else block 않고, 또 보기에도 좋지 않은 것 꼭 if...else블록은 가장 간단한 솔루션입니다.

그 외의 모든 것은, 예를 들면 복잡성이 증가하는 추가 코드를 필요로 합니다.그리고 그것은 추악함을 다른 곳으로 옮긴다.그러나 어떤 수준에서는 문자열 비교가 여전히 이루어져야 합니다.이제 넌 더 많은 코드로 그걸 덮어버렸어.

맵이나 해시 맵을 사용하면 퍼포먼스가 다소 향상될 수 있지만 단순히 스마트 오더를 선택하여 평가함으로써 더 나은 이점을 얻을 수 있습니다.if...else성능상의 이유로 맵으로 전환하는 것은 시기상조입니다.

그런 게 너무 복잡할 것 같아?

#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」)

그러면 문자열이 4바이트 정수로 처리됩니다.

이것은 추하고, 휴대할 수 없고, "해키하고" 전혀 좋은 스타일이 아니라고 여겨집니다.하지만 네가 원하는 걸 할 수 있어

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

MSVC2010에서 테스트 완료

이 예시와 같이 메타프로그래밍 기반의 해시 생성기가 생각납니다.이것은 c++0x 용이지만, 표준 C++ 용에서도 똑같이 재현할 수 있을 것입니다.

스위치를 사용할 수 있습니다.사전에 라벨을 알고 있다면..(이것은 매우 귀찮습니다(즉, 체크는 하지 않지만 유효한 늘 종단 문자열이 있는 한 추가하는 것은 간단할 것입니다!) 대부분의 옵션보다 실행 속도가 빠릅니다.

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

물론, "라벨" 목록이 매우 많으면, 이것은 상당히 복잡해질 것입니다.

임의의 타입의 c/c++ 스위치 실장을 사용할 수 있습니다.코드는 다음과 같습니다.

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"

좀 더 할 수 .std::pairs또는 동일 연산을 지원하는 구조 또는 클래스(또는 빠른 모드의 경우 충돌)를 모두 지원합니다.

특징들

  • 비교 또는 동일성을 확인하는 데 도움이 되는 모든 것
  • 캐스케이드 중첩 스위치 스테이트멘을 구축할 가능성.
  • 케이스 스테이트먼트를 깨거나 실패할 가능성
  • 불변 대소문자 표현 사용 가능성
  • 트리 검색을 사용하여 빠른 정적/동적 모드를 활성화할 수 있습니다(C++11의 경우).

언어 스위치와의 신탁스 차이는

  • 대문자 키워드
  • CASE 문장에 괄호 필요
  • 문의 끝에 세미콜론 ';'이(가) 허용되지 않습니다.
  • CASE 문의 콜론 ':'은 허용되지 않습니다.
  • CASE 문의 끝에 BREAK 키워드 또는 FALL 키워드 중 하나가 필요합니다.

★★★의 C++97언어를 사용하여 선형 검색을 수행합니다.★★★의 C++11 보다 이 있습니다.quick모드 wuth 트리 검색에서는 CASE의 반환문이 허용되지 않습니다.C은 '어느 곳에 있는가'에 합니다.char*타입과 제로 종단 문자열의 비교가 사용됩니다.

이 스위치의 실장에 대한 자세한 내용은 여기를 참조해 주세요.

const char*에 의한 전환은 허용되어도 정상적으로 동작하지 않습니다.

C String은 실제로 char에 대한 포인터입니다.제안하신 코드:

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

언어가 일관성이 있는 경우 - 실제 문자열 내용이 아닌 포인터 값을 비교합니다.문자열 비교에는strcmp()그래서 컴파일러가 특별한 케이스를 가지고 있다고 해도, "만약 우리가 다른 컴파일러에 대항해서char*하다, 사용하다strcmp()==(어쨌든 언어 디자인은 형편없을 것이다), 어쨌든 컴파일러가 정수와 점프를 가진 O(1) 해킹처럼 이 작업을 하는 것은 불가능할 것이다.

C/C++는 지원되지 않으므로 아쉬워하지 마십시오.:)

O(가 있는 O(logn)합니다.(string -> funcptr) ★★★★★★★★★★★★★★★★★」(string -> some abstract object)하다.그렇지 않은 경우 O(n) 솔루션에는 특별히 문제가 없습니다.여전히 명확하고 유지보수가 가능한 코드니까 기분 나쁠 건 없어

얼마 전 어떤 데이터 유형에서도 사용할 수 있는 동등한 스위치를 실현하는 템플릿 클래스를 작성한 적이 있습니다.그러나 응용 프로그램 필드를 제한하는 몇 가지 제약사항이 있습니다.

  • 각 브런치에서 수행하는 태스크는 함수 호출이어야 합니다.
  • 호출하는 함수의 인수는 1개입니다(또는 1개도, 2개도, 템플릿을 조정할 수 있습니다만, 모든 함수에 대해 동일할 필요가 있습니다).
  • 함수에 전달되는 인수 값은 모든 경우에 동일합니다(단, 스위치 실행 시 지정됩니다).

들어 으로 하는 등입니다.MyType「」와 value1콜, 콜, 콜function1("abc")「」와 value2콜, 콜, 콜function2("abc")(으) 이렇게 됩니다.

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

기본적으로는 std::map 컨테이너를 랩하여 쌍값/함수를 유지합니다.또, 「디폴트」도 처리할 수 있기 때문에, 스윗치가 매우 흥미로워집니다.그것은 다른 상황에 쉽게 적응할 수 있다.코드는 다음과 같습니다.

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

다른 세부 사항은 이쪽입니다.

승리를 향해 힘차게 나아가다

이 훌륭한 스택오버플로우 응답과 같은 컴파일 타임 해시 함수를 사용할 수 있습니다.함수를 만든 경우

  • int_crc32_s시하고 " " " 를 반환합니다.
  • int_crc32합니다.

준비되었습니다.키 문자열과 케이스 문자열의 CRC가 잘못 일치한 경우 이를 처리하려면 일치 여부를 명시적으로 확인해야 합니다.이것은 한 번의 점검으로 성능에 큰 영향을 미치지 않지만, 더 보기 흉하고 매크로 버전이 훨씬 더 좋아 보입니다.

같은 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 (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";
}

매크로 사용

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

완전 실장 [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"; }
    }
}

이 답변은 smilingthax에 의한 같은 질문에 대한 답변 https://stackoverflow.com/a/4165312/10075467에 대한 추가 작업입니다.또한 실제로 정렬할 필요 없이 결과를 얻을 수 있도록 Switch 기능을 변경합니다(대신 맵을 사용하여 달성한 목적은 여전히 복잡성에 영향을 미칩니다).

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

지도에서 우리를 위해 분류를 하도록 하자.switch_case 목록이 이미 정렬되어 있다면 initializer_list를 다시 사용하는 것이 좋습니다.

편집: 최근 코드쉐프 중 한 명이 레이더스(간단한) 문제에 제출하는 데 동일한 내용을 사용했습니다.여기에서는 보이드 대신 정수형을 반환하는 데 사용했습니다.

언급URL : https://stackoverflow.com/questions/4165131/c-c-switch-for-non-integers