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

但这似乎不那么优雅。有人知道为什么你不能在这里使用常量数组吗?

编辑:我已经看到 C++ 标准中声称“一个整数常量表达式只能涉及文字(2.13)、枚举器、常量变量或整数或枚举类型的静态数据成员用常量表达式初始化(​​8.5) ……”。我仍然不明白为什么常量数组不能算作“用常量表达式初始化的枚举类型”。可能只是我的问题的答案是“因为它就是这样”,我将不得不解决它。但如果是这样的话,那就有点令人失望了,因为编译器确实可以在编译时确定这些值。

4

7 回答 7

3

参考 C++ 标准的部分:6.4.2 要求 case 表达式计算为整数或枚举常量。5.19 定义了什么是:

一个整型常量表达式只能包含字面量 (2.13)、枚举数、常量变量或使用常量表达式初始化的整型或枚举类型的静态数据成员 (8.5)、整型或枚举类型的非类型模板参数,以及 sizeof 表达式。浮动文字 (2.13.3) 只有在转换为整数或枚举类型时才能出现。只能使用到整数或枚举类型的类型转换。特别是,除了 sizeof 表达式,不得使用函数、类对象、指针或引用,不得使用赋值、递增、递减、函数调用或逗号运算符。

因此,如果您的问题是“为什么编译器会拒绝这个”,那么一个答案是“因为标准是这样说的”。

于 2008-12-15T05:14:03.860 回答
2

无论如何,数组引用都不够“恒定”。

您只需要稍微不同地进行映射。您希望在按下逻辑键时发生相同的操作,因此在语句的case子句中使用逻辑键代码switch。然后将实际的关键代码映射到逻辑代码,可能在其switch本身中,或者可能在之前。您仍然可以使用 LogicalMappings 数组或类似的构造。而且,作为对 G11N(全球化)的辅助,您甚至可以使映射数组不恒定,以便不同的人可以重新映射键以满足他们的需要。

于 2008-12-15T05:12:57.357 回答
1

我会在这里冒险,因为没有其他人对此做出回应,而且我最近大部分时间都在做 Java,而不是 C++,但据我记得,数组查找不被视为常量整数,即使结果查找可以在编译时确定。这甚至可能是语法中的一个问题。

于 2008-12-15T05:11:49.403 回答
0

boost 中有一个名为signal的库,可以帮助您创建事件映射抽象。如果您有时间,这应该是更好的方法

于 2008-12-15T06:03:02.780 回答
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 回答
0

工作中的编译器大师向我解释了这一点。问题是数组本身是常量,但它的索引不一定是常量。因此,表达式 LogicalMappings[some_variable] 无法在编译时进行评估,因此该数组最终还是被存储在内存中,而不是被编译出来。编译器仍然没有理由不能使用 const 或文字索引静态评估数组引用,所以我想做的理论上应该是可能的,但它比我想象的要复杂一些,所以我可以理解为什么 gcc 不不要这样做。

于 2008-12-17T05:57:59.183 回答