C ++の2つのパラメーターの切り替え

Habrの投稿を読んで、そのような質問につまずいた。 コメントで解決策が提案されましたが、関数呼び出しのオーバーヘッドの観点から著者にふさわしいものはありませんでした。 そして、私は考えましたが、実際には通常のスイッチを使用しないで、 スイッチで使用する必要がある2つのパラメータから1つのハッシュを計算します。 しかし、質問の著者の例をより注意深く見ると、ネストされたスイッチデフォルトをキャッチする必要があるため、そのようなオプションはそのようには機能しないことがわかりました。



2つの変数nmがあり 、それぞれが0〜9の値を取ることができ、そのような構造があるとします。

switch (n) { case 0: { switch (m) { case 2: case 4: ... break; case 5: ... break; default: ... break; } } break; ... }
      
      





各変数の値はバイト単位で配置されるため、ハッシュ関数は次のようにします((n << SHIFT )+ m)( SHIFT = 8)。

そして、ここで問題が発生します:両方の値が定義されている場合、ハッシュの計算後に特定の数値を取得します。 しかし、n = 0と言い、mが2,4,5に等しくない場合、この場合はどうすればよいでしょうか? デフォルトのアクションを実行するための残りのオプションをリストするには、値の範囲が0..9よりもはるかに大きくなる可能性があるため、コストがかかりすぎます。 つまり、実際には、計算されたハッシュの値を特定の範囲でキャッチする必要があります。



そして、 デフォルトを自分でリストアップするとどうなるか考えました。 つまり、最初にいくつかのアクションを実行する必要があるすべてのペアmncaseを記述し、次にデフォルトで1つのパラメーターnでネストされたスイッチを宣言して、処理する必要がある他のすべての組み合わせが列挙されるようにします。



だから私は何を得た


このような単純なハッシュ関数は、通常のマクロで実装できますが、C ++++ 11でconst ++関数が登場したため、使用することにしました。

 constexpr int hash(int n, int m) { return verifyValues(n, m) ? ((n << SHIFT) + m): -1; }
      
      





、verifyValuesは、パラメーターが指定された範囲内にあることを検証する関数です。 このために、定数MAX_N、MAX_Mが使用され、パラメーターが有効でない場合、-1が返されます。



 #define isInBound(min, value, max) ((value >= min) && (value <= max)) constexpr bool verifyValues(int n, int m) { return isInBound(0, n, MAX_N) && isInBound(0, m, MAX_M); }
      
      





ハッシュ関数を見ると、次のチェックを追加するのもいいでしょう。

 MAX_M < pow(2, SHIFT)
      
      





追加のコードを非表示にし、マクロを使用してチェックします。

 #define SWITCH(n, m) static_assert(MAX_M < pow(2, SHIFT), "shift value is not enough to cover all M values"); \ switch(hash(n, m)) #define CASE(n, m) static_assert(verifyValues(n, m), "N or M value is out of range"); \ case hash(n, m)
      
      





ここでは、 スイッチを決定するときに、ハッシュ関数の操作性がすぐにチェックされ、後で各ケースについて 、変数が指定された範囲にあることがチェックされます。



したがって、最初の部分が整理され、 デフォルトのセクション自体になりました。

ここでは、ネストされたスイッチを単純に登録することが可能ですが、マクロを書き始めたので、彼のためにそれを定義します。

 #define DEFAULT(n) \ case -1: ASSERT(false); break; \ default: switch(n) { #define DEFAULT_CASE(n) case n #define END_DEFAULT }
      
      





ハッシュを計算するとき、関数は-1を返すことがあるため(パラメーターが誤って渡された場合)、 ケース -1のハンドラーが追加されました(私の例では、これは通常のASSERTです)。



その結果、2つのパラメーターを持つこのようなスイッチが得られました。

 SWITCH(a, b) { CASE(0, 1): CASE(0, 2): CASE(0, 3): CASE(0, 4): ... break; CASE(5, 3): ... break; DEFAULT(a) DEFAULT_CASE(0): ... break; DEFAULT_CASE(1): ... break; DEFAULT_CASE(2): ... break; DEFAULT_CASE(3): ... break; DEFAULT_CASE(4): ... break; DEFAULT_CASE(5): ... break; DEFAULT_CASE(6): ... break; DEFAULT_CASE(7): ... break; DEFAULT_CASE(8): ... break; DEFAULT_CASE(9): ... break; END_DEFAULT }
      
      





ここでの主なことは、 DEFAULTマクロの後にコロンを置く必要がないことを忘れないことです。



全コード
 const int MAX_N = 10; const int MAX_M = 10; const int SHIFT = 8; #define isInBound(min, value, max) ((value >= min) && (value <= max)) constexpr bool verifyValues(int n, int m) { return isInBound(0, n, MAX_N) && isInBound(0, m, MAX_M); } constexpr int hash(int n, int m) { return verifyValues(n, m) ? ((n << SHIFT) + m): -1; } #define SWITCH(n, m) static_assert(MAX_M < pow(2, SHIFT), "shift value is not enough to cover all M values"); \ switch(hash(n, m)) #define CASE(n, m) static_assert(verifyValues(n, m), "N or M value is out of range"); \ case hash(n, m) #define DEFAULT(n) \ case -1: Q_ASSERT(false); break; \ default: switch(n) { #define DEFAULT_CASE(n) case n #define END_DEFAULT } ... SWITCH(a, b) { CASE(0, 1): CASE(0, 2): CASE(0, 3): CASE(0, 4): printf("0, 1-4\n"); break; CASE(5, 3): printf("5, 3\n"); break; DEFAULT(a) DEFAULT_CASE(0): printf("0\n"); break; DEFAULT_CASE(1): printf("1\n"); break; DEFAULT_CASE(2): printf("2\n"); break; DEFAULT_CASE(3): printf("3\n"); break; DEFAULT_CASE(4): printf("4\n"); break; DEFAULT_CASE(5): printf("5\n"); break; DEFAULT_CASE(6): printf("6\n"); break; DEFAULT_CASE(7): printf("7\n"); break; DEFAULT_CASE(8): printf("8\n"); break; DEFAULT_CASE(9): printf("9\n"); break; END_DEFAULT }
      
      







その結果、2つのパラメーターで動作するスイッチがあります。 オーバーヘッドのうち、基本ハッシュ関数。 ただし、両方のパラメーターが定義されているcaseの場合 、最初のswitchで既に移行が実行され、2番目の場合と同様にデフォルトで移行が実行されます



この実装が誰かに役立つことを願っています。 ご清聴ありがとうございました。



PS原則として、このような構造を使用すると、2つのパラメーターだけでなく、3つ以上のパラメーターに対してスイッチを作成できます。



All Articles