Person이라는 구조체가 있으며, 이 구조체는 여러개의 member를 포함하고 있습니다.

struct Person {
  int age;
  string name;
  string country;
  string address;
  string tel;
  // ...
};

외부의 설정 파일(ini, xml, json 등)에 의해 본 구조체를 초기화하려고 합니다. attribute 의 name과 value는 모두 string이라고 가정합니다.

 void setProperty(string name, string value) {
    if (name == "age") age = stoi(value);
    else if (name == "name") name = value;
    else if (name == "country") country = value;
    else if (name == "address") address = value;
    else if (name == "tel") tel = value;
    else cout << "Invalid attribute " << name << endl;
  }

여기에서 구조체의 멤버 갯수가 늘어 나면 늘어 나는 만큼 setProperty의 수행 시간이 그만큼 늘어 나게 된다는 문제가 발생하게 됩니다. O(N)이 되어 버리죠. setProperty 함수의 첫번째 인자(name)의 type이 primitive type이라면 switch를 이용해서 최적화를 할 수 있겠지만, 문제는 이 name이라는 변수의 type이 string일 수 밖에 없다는 것입니다(ini, xml, json과 같은 외부 설정 파일에서 읽어 와야 하므로).

string을 switch의 case expression type으로 사용할 수 없을까 하는 고민을 해 보고 검색을 해 보았습니다. 그 결과 산뜻한(?) 방식을 찾아 내었습니다

 static constexpr uint32_t const_hash(const char *p) {
    return *p ? static_cast<uint32_t>(*p) + 33 * const_hash(p + 1) :  5381;
  }

  void setProperty(string name, string value) {
    uint32_t hash = const_hash(name.c_str());
    switch (hash) {
      case const_hash("age"):     if (name == "age") age = stoi(value); break;
      case const_hash("name"):    if (name == "name") name = value; break;
      case const_hash("country"): if (name == "country") country = value; break;
      case const_hash("address"): if (name == "address") address = value; break;
      case const_hash("tel"):     if (name == "tel") tel = value; break;
      default: cout << "Invalid attribute " << name << endl;
    }
  }

C++11에는 constexpr이라는 예약어를 제공합니다. 이는 해당 값이 상수값(compile time에 값이 결정되어 질 수 있음)이라고 컴파일러에게 알려 주는 기능을 하게 됩니다 ( https://en.cppreference.com/w/cpp/language/constexpr ). 상기 예제에서는 const_hash라는 함수의 결과값이 상수값임을 컴파일러에게 알려 주게 되고, 아 함수의 결과 값을 switch 내부의 각각의 case의 expression으로 넣을 수 있습니다.

단 이러한 방식을 사용하는 데에는 약간의 주의 사항이 있습니다.

  • constexpr 함수의 반환값은 컴파일러로 하여금 예측될 수 있어야 한다

  • hash값은 collision이 날 수 있기 때문에 hash 값이 같다고 해서 그냥 처리하면 안되고, 반드시 string의 값이 같은지 확인하는 작업을 거쳐야 한다.

이상 허접 팁이었습니다.

ps : const_hash에서 사용되어 지는 5381이라는 값은 primitive number로 충돌의 가능성을 조금이라도 낮추게 됩니다. 또한 33을 곱하기를 하는 것은 mul, imul과 같이 CPU clock을 많이 잡아 먹는 코드로 변환되지 않고 (32 + 1)을 곱하기 하는 것으로(shl 5와 add 명령어의 조합)으로 최적화가 이루어 지기 때문에 조금이나마 CPU 연산을 줄이게 됩니다.

Source code : https://github.com/snoopspy/study/blob/master/cpp/string_switch_test/string_switch_test.cpp

출처 : http://www.gilgil.net/?document_srl=812969