6

一連のキー押下を一連のコマンドにマップしようとしています。複数の場所からコマンドを処理するため、キーとコマンドの間に抽象化レイヤーを設定して、基になるキー マッピングを変更した場合にコードをあまり変更する必要がないようにしたいと考えています。私の現在の試みは次のようになります。

// input.h
enum LOGICAL_KEYS {
    DO_SOMETHING_KEY,
    DO_SOMETHING_ELSE_KEY,
    ...
    countof_LOGICAL_KEYS
};

static const SDLKey LogicalMappings[countof_LOGICAL_KEYS] = {
    SDLK_RETURN,    // Do Something
    SDLK_ESCAPE,    // Do Something Else
    ...
};

// some_other_file.cpp
...
switch( event.key.keysym.key ) {
    case LogicalMappings[ DO_SOMETHING_KEY ]:
        doSomething();
        break;
    case LogicalMappings[ DO_SOMETHING_ELSE_KEY ]:
        doSomethingElse();
        break;
    ...
}

これをコンパイルしようとすると (gcc 4.3.2)、次のエラー メッセージが表示されます。

エラー: 'LogicalMappings' は定数式に表示できません

コンパイラがこれに問題を抱えている理由がわかりません。case ステートメントで変数を使用できない理由は理解できますが、定数はコンパイル時に評価できるため、定数を使用できるという印象を受けました。定数配列は switch ステートメントでは機能しませんか? もしそうなら、配列を次のようなものに置き換えることができると思います:

static const SDLKey LOGICAL_MAPPING_DO_SOMETHING      = SDLK_RETURN;
static const SDLKey LOGICAL_MAPPING_DO_SOMETHING_ELSE = SDLK_ESCAPE;
...

しかし、それははるかにエレガントではないようです。ここで定数配列を使用できない理由を誰か知っていますか?

編集:「整数定数式には、リテラル (2.13)、列挙子、const 変数、または定数式で初期化された整数型または列挙型の静的データ メンバーのみを含めることができる (8.5)」と主張する C++ 標準のビットを見てきました。 ...」。定数配列が「定数式で初期化された列挙型」としてカウントされない理由はまだわかりません。私の質問に対する答えが「その通りだから」というだけかもしれませんが、私はそれを回避する必要があります。しかし、そうであれば、コンパイラはコンパイル時にこれらの値を実際に決定できるため、ちょっとがっかりです。

4

7 に答える 7

3

C++ 標準のセクションを参照する: 6.4.2 では、case 式が整数または列挙定数に評価される必要があります。5.19 では、それが何であるかを定義しています。

整数定数式には、リテラル (2.13)、定数式 (8.5) で初期化された整数型または列挙型の列挙子、const 変数または静的データ メンバー、整数型または列挙型の非型テンプレート パラメーター、および sizeof 式のみを含めることができます。浮動リテラル (2.13.3) は、整数型または列挙型にキャストされている場合にのみ表示できます。整数型または列挙型への型変換のみを使用できます。特に、sizeof 式を除いて、関数、クラス オブジェクト、ポインター、または参照は使用してはならず、代入、インクリメント、デクリメント、関数呼び出し、またはコンマ演算子は使用してはなりません。

したがって、あなたの質問が「コンパイラがこれを拒否する理由」である場合、1 つの答えは「標準がそう言っているため」です。

于 2008-12-15T05:14:03.860 に答える
2

とにかく、配列参照は「十分に一定」ではありません。

マッピングを少し異なる方法で行う必要があるだけです。論理キーが押されたときに同じアクションが発生するようにするため、ステートメントのcase句で論理キー コードを使用します。次に、実際のキー コードを、場合によってはそれ自体で、または場合によっては事前にswitch論理コードにマップします。switchLogicalMappings 配列または同様の構造を引き続き使用できます。また、G11N (グローバリゼーション) への支援として、マッピング配列を非定数にすることもできるため、さまざまな人がニーズに合わせてキーを再マッピングできます。

于 2008-12-15T05:12:57.357 に答える
1

他の誰もこれに返信していないので、ここで手足を進めます。最近はC++ではなくJavaを主に使用していますが、覚えている限りでは、配列ルックアップは定数整数とは見なされません。ルックアップはコンパイル時に決定できます。これは、構文の問題でさえあるかもしれません。

于 2008-12-15T05:11:49.403 に答える
0

ブーストにはsignalというライブラリがあり、イベントマッピングの抽象化を作成するのに役立ちます。時間があれば、これはより良いアプローチになるはずです。

于 2008-12-15T06:03:02.780 に答える
0

仕事中のコンパイラの第一人者がこれを私に説明しました。問題は、配列自体は定数ですが、配列へのインデックスは必ずしも定数ではないということです。したがって、式LogicalMappings [some_variable]はコンパイル時に評価できなかったため、配列はコンパイルされるのではなく、とにかくメモリに格納されることになります。コンパイラがconstまたはリテラルインデックスを使用して配列参照を静的に評価できなかった理由はまだないので、理論的には可能であるはずですが、思ったより少し難しいので、gccがなぜそうしないのか理解できますやらないで。

于 2008-12-17T05:57:59.183 に答える
0

関数ポインターまたはファンクターの配列(ファンクターアドレスだと思います)を使用して、switchステートメントを完全に回避し、配列インデックス->関数ポインター/ファンクターから直接移動することもできます。

たとえば(警告、テストされていないコードが続きます)

class Event // you probably have this defined already
{
}

class EventHandler // abstract base class
{
public:
  virtual void operator()(Event& e) = 0;
};

class EventHandler1
{
  virtual void operator()(Event& e){
    // do something here 
  }
};
class EventHandler2
{
  virtual void operator()(Event& e){
    // do something here 
  }
};

EventHandler1 ev1;
EventHandler2 ev2;
EventHandler *LogicalMappings[countof_LOGICAL_KEYS] = {
  &ev1,
  &ev2,
  // more here...

};

// time to use code:
Event event;
if (event.key.keysym.key < countof_LOGICAL_KEYS)
{
   EventHandler *p = LogicalMappings[event.key.keysym.key];
   if (p != NULL)
      (*p)(event);
}
于 2008-12-15T14:33:12.763 に答える
0

「LogicalMappings」に定義された比較演算子はありますか? そうでない場合、それはエラーです。

于 2008-12-15T05:09:07.443 に答える