とくにあぶなくないRiSKのブログ

危ないRiSKのブログだったかもしれない。本当はRiSKだけどググラビリティとか取得できるIDの都合でsscriskも使ったり。

switch case で文字列を使う

「なるほどー」と思ったので文字列のハッシュを使う実験。

#include<string>
#include<iostream>

constexpr int hash(char const * s)
{
 return *s ? *s + hash(s + 1) : 0; // 超絶てけとーハッシュ
}

int main()
{
 for(std::string s; std::cin >> s;)
  switch(hash(s.c_str()))
  {
  case hash("foo"): std::cout << "case foo" << std::endl; break;
  case hash("bar"): std::cout << "case bar" << std::endl; break;
  case hash("baz"): std::cout << "case baz" << std::endl; break;
  case hash("hoge"): std::cout << "case hoge" << std::endl; break;
  default: std::cout << "default" << std::endl; break;
  }
}

実行結果:

foo
case foo
bar
case bar
baz
case baz
hoge
case hoge
sscrisk
default
^Z

hash関数を改造すればかなり使える範囲広がりそう。constexpr で夢ひろがりんぐ。

問題点

ハッシュですから当然衝突が起こりえます。

  • case 同士が衝突してコンパイルエラー。
  • defaultへ飛ばず予想外のcaseへ飛ぶ。

解決策

  • 別のハッシュ関数を用いる。
  • caseへ飛んだ後,(実行時に)厳密に比較。
  • switch内では特定の文字列のみを用いる。この場合コンパイル時のうちに問題を把握できる。
衝突が起きた後に厳密に比較する例
#include<cstring>
#include<iostream>

constexpr int hash(char const * s)
{
 return *s ? *s + hash(s + 1) : 0; // 超絶てけとーハッシュ
}

void test(char const * s)
{
 switch(hash(s))
 {
 case hash("hoge"): // or "hohd"
  std::strcmp(s, "hoge") == 0 && (std::cout << "hoge" << std::endl);
  std::strcmp(s, "hohd") == 0 && (std::cout << "hohd" << std::endl);
 }
}

int main()
{
 // ここで仮に「"hoge" と "hohd" は衝突する」ことにする
 test("hoge");
 test("hohd");
}

実行結果:

hoge
hohd

constexprなCRCの実装

constexprなCRCを実装する - iorateの日記