programing

C / C ++ : 정수가 아닌 스위치

procenter 2021. 1. 17. 12:07
반응형

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) 비용이들 수 있습니다.

일부 데이터 구조체 (Maps와 같은)를 사용하면 문자열 (O (log n))을 나타내는 정수를 얻은 다음 O (1)을 사용할 switch수 있거나 if오른쪽에 s를 중첩하여 정적 이진 정렬을 구현할 수 있습니다. 그러나 여전히 이러한 해킹 에는 많은 코딩이 필요하므로 모든 것이 더 복잡하고 유지 관리가 더 어려워집니다.

이를 수행하는 가장 좋은 방법은 무엇입니까? ( switch성명 대로 빠르고 깨끗하며 간단합니다 )


불쾌한 매크로와 템플릿 마법을 사용하면 예쁜 구문으로 컴파일 타임에 펼쳐진 바이너리 검색을 얻을 수 있지만 MATCHES ( "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);

그리고 breaks는 암시 적이며 넘어 질 수 없습니다. 또한 문서화가 많지 않습니다. 죄송합니다. 그러나 더 많은 사용 가능성이 있다는 것을 알게 될 것입니다. 참고 : g ++로만 테스트되었습니다.

C ++ 11 업데이트 :

Lambda와 이니셜 라이저 목록은 작업을 훨씬 더 예쁘게 만듭니다 (매크로가 필요하지 않습니다!).

#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 업데이트 : 컴파일 타임 트라이

이 문제에 대한 필자의 최신 정보는 고급 C ++ 11 메타 프로그래밍을 사용하여 컴파일 타임에 검색 시도를 생성합니다 . 이전 접근 방식과 달리 이것은 정렬되지 않은 대 / 소문자 구분 / 문자열을 잘 처리합니다. 문자열 리터럴 만 있으면됩니다. G ++에서는 constexpr도 허용하지만 clang은 허용하지 않습니다 (HEAD 3.9.0 / trunk 274233 기준).

각 트라이 노드에서 스위치 문은 컴파일러의 고급 코드 생성기를 활용하는 데 사용됩니다.

전체 구현은 github : smilingthax / cttrie 에서 사용할 수 있습니다 .


C ++에서, 당신은 얻을 수 O(lg n)을함으로써 성능을 std::map<std::string, functionPointerType>. (C에서는 본질적으로 동일한 것을 구현할 수 있지만 더 어려울 것입니다.)를 사용하여 올바른 함수 포인터를 꺼내 해당 포인터를 std::map<k, v>::find호출합니다. 물론 이것은 언어 지원 switch 문만큼 간단하지는 않을 것입니다. 당신이 거기 사이에 큰 차이가 될 것 충분히 항목이있는 경우 반면에, O(n)그리고 O(lg n), 그 첫 번째 장소에서 다른 디자인을 위해 갈 것을 표시는 아마.

개인적으로 저는 항상 ELSEIF 체인이 어쨌든 더 읽기 쉽다는 것을 발견했습니다.


아래와 같이지도 나 무순지도를 사용하지 않고도 달성 할 수 있습니다. 첫 번째 문자 만 비교하여 어떤 문자열을 식별하십시오. 둘 이상의 일치하는 경우 해당 case 문 내에서 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
    }
}

이것은 표준은 아니지만 비용이 들지 않는 최적의 솔루션으로 보입니다.


를 사용합니다 if...else block. 보기에 예쁘지 않다는 점을 제외하고는 그렇게하지 않을 설득력있는 이유가 없으며 if...else블록은 가장 간단한 해결책입니다.

다른 모든 것에는 추가 코드가 필요하므로 복잡도가 높아집니다. 그리고 그것은 단지 추악함을 다른 곳으로 이동시킵니다. 그러나 어떤 수준에서는 문자열 비교가 여전히 발생해야합니다. 이제 더 많은 코드로 덮어 썼습니다.

당신은 수있는 지도 나 해시 맵을 사용하여 약간의 성능 향상을 얻을 수 있지만, 간단하게 평가할 수있는 스마트 위해 선택하여도 낫지 이득 경우 이득 유사한 youcan if...else블록을. 그리고 성능상의 이유로 맵으로 전환하는 것은 정말 너무 이른 마이크로 최적화입니다.


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 프로그램과 같은 유한 상태 솔루션을 사용하는 것입니다.


너무 복잡할까요?

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

이것은 람다 및 무 순서지도 솔루션과 정신적으로 비슷하지만 매우 자연스럽고 읽기 쉬운 구문을 사용하여 두 세계 모두에서 최고라고 생각합니다.

#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 바이트 이하의 문자열에만 해당)

이것은 문자열을 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 ++에서도 유사하게 재현 할 수 있습니다.


당신은 여전히 ​​스위치를 사용할 수 있습니다 .. 레이블을 미리 알고 있다면 .. (이것은 매우 불쾌합니다 (즉, 체크가 없지만 유효한 null로 끝나는 문자열이있는 한 추가하는 것은 사소한 것입니다!), 상상해야합니다 이것이 대부분의 옵션보다 빠르게 수행된다는 것을?

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

물론, "라벨"목록이 매우 큰 경우 이는 매우 복잡해집니다.


모든 종류의 값 유형을 지원 하는 내 스위치 매크로를 사용할 수 있습니다 . 몇 번의 경우 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 ++ 스위치 구현을 사용할 수 있습니다 . 코드는 다음과 같습니다.

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하거나 같음 연산 (또는 빠른 모드의 경우 비교) 을 지원하는 구조체 또는 클래스 를 사용할 수 있습니다 .

풍모

  • 비교를 지원하거나 동등성을 확인하는 모든 유형의 데이터
  • 계단식 중첩 스위치 상태를 구축 할 수 있습니다.
  • 사건 진술을 깨거나 넘어 뜨릴 가능성
  • nonconstatnt case 표현을 사용할 가능성
  • 트리 검색으로 빠른 정적 / 동적 모드 활성화 가능 (C ++ 11 용)

언어 전환과의 신택스 차이점은 다음과 같습니다.

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

를 들어 C++97언어 선형 검색을 사용했다. 들어 C++11와 사용 가능한 더 현대적인 quick곳 모드 wuth 트리 검색 반환 되는 경우에는 문이 없다 허용했다. C경우 언어 구현이 존재 char*유형과 제로 종료 문자열 comparisions 사용됩니다.

이 스위치 구현 에 대해 자세히 알아 보십시오 .


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

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

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


const char *로 전환하는 것은 허용 된 경우에도 의도 한대로 작동하지 않습니다.

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

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

우리의 언어가 일관 적이라면 실제 문자열 내용이 아닌 포인터 값을 비교합니다 . 문자열을 비교하려면이 필요합니다 strcmp(). 따라서 컴파일러가 "로 전환하는 char*경우 (어쨌든 언어 디자인이 좋지 않을 것입니다) strcmp()대신에 사용 하는 경우"와 같은 특별한 경우 ==가 있어도 컴파일러가이를 만들 수 없습니다. 정수와 점프로 O (1) 해킹처럼 작동합니다.

따라서 지원되지 않으므로 C / C ++에 대해 나쁘게 생각하지 마십시오. :)

나는지도와 함께 O (logn) 솔루션을 권장 (string -> funcptr)또는 (string -> some abstract object)- 당신이 여기에 확장 성을 필요로 느끼는 경우. 그렇지 않은 경우 O (n) 솔루션과 else if '에 특별히 잘못된 것은 없습니다. 여전히 명확하고 유지 관리가 가능한 코드이므로 나쁘게 느낄 필요가 없습니다.


얼마 전에 나는 모든 데이터 유형 에서 사용할 수있는 일종의 스위치 등가물을 달성하는 템플릿 클래스를 작성했습니다 . 그러나 응용 분야를 제한하는 몇 가지 제약이 있습니다.

  • 각 분기에서 수행 할 작업은 함수 호출이어야합니다.
  • 호출 할 함수는 단일 인수를 갖습니다 (또는 아무 것도 또는 두 개, 템플릿을 조정할 수 있지만 모든 함수에 대해 동일해야 함).
  • 함수에 전달 된 인수 값은 모든 경우에 동일합니다 (그러나 스위치가 실행되는 순간에 제공됨).

예를 들어, 유형의 값을 켜고 싶습니다.이 값 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"; }
}    

전체 구현 [ 요점 ]

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

참조 URL : https://stackoverflow.com/questions/4165131/cc-switch-for-non-integers

반응형