我的宏有点过于顽皮吗?

时间:2018-02-08 16:14:43

标签: c++ macros

我做了一个宏。我担心它可能有点“过于顽皮”,因为宏是邪恶的

以下是没有宏的代码:

case SDLK_a:
    _buffer_.InsertAtCursor('a');
    _buffer_.MutableCursor().Right();
    break;

这是宏定义及其用法:

#define SDLK_KEY_CASE_MACRO(X, Y) \
{\
case X: \
    _buffer_.InsertAtCursor(Y); \
    _buffer_.MutableCursor().Right(); \
    break;\
}

SDLK_KEY_CASE_MACRO(SDLK_b, 'b');
SDLK_KEY_CASE_MACRO(SDLK_c, 'c');
SDLK_KEY_CASE_MACRO(SDLK_d, 'd');
SDLK_KEY_CASE_MACRO(SDLK_e, 'e');
SDLK_KEY_CASE_MACRO(SDLK_f, 'f');
SDLK_KEY_CASE_MACRO(SDLK_g, 'g');
SDLK_KEY_CASE_MACRO(SDLK_h, 'h');
SDLK_KEY_CASE_MACRO(SDLK_i, 'i');
SDLK_KEY_CASE_MACRO(SDLK_j, 'j');
SDLK_KEY_CASE_MACRO(SDLK_k, 'k');
SDLK_KEY_CASE_MACRO(SDLK_l, 'l');
SDLK_KEY_CASE_MACRO(SDLK_m, 'm');
SDLK_KEY_CASE_MACRO(SDLK_n, 'n');
SDLK_KEY_CASE_MACRO(SDLK_o, 'o');
SDLK_KEY_CASE_MACRO(SDLK_p, 'p');
SDLK_KEY_CASE_MACRO(SDLK_q, 'q');
SDLK_KEY_CASE_MACRO(SDLK_r, 'r');
SDLK_KEY_CASE_MACRO(SDLK_s, 's');
SDLK_KEY_CASE_MACRO(SDLK_t, 't');
SDLK_KEY_CASE_MACRO(SDLK_u, 'u');
SDLK_KEY_CASE_MACRO(SDLK_v, 'v');
SDLK_KEY_CASE_MACRO(SDLK_w, 'w');
SDLK_KEY_CASE_MACRO(SDLK_x, 'x');
SDLK_KEY_CASE_MACRO(SDLK_y, 'y');
SDLK_KEY_CASE_MACRO(SDLK_z, 'z');

显然,这为我节省了大量的打字和大量的代码。代码也编译并运行正常。

  • 但这个宏太淘气了吗? (它是否可能产生意外行为,或者是否有理由不在我的代码中使用此“作弊”?)

2 个答案:

答案 0 :(得分:3)

与任何理智的系统一样,SDL_SCANCODE_A(又名SDLK_a)处于连续的整数范围内,并按顺序一直到SDL_SCANCODE_Z'a''z'也是如此。

if( val >= SDLK_a && val <= SDLK_z ) {
  _buffer_.InsertAtCursor( 'a' + (val-SDLK_a) );
  _buffer_.MutableCursor().Right();
} else {
  // handle other cases
}

这比宏墙更有效,更短,更清晰。

您可能需要重复数字。

对于特殊字符(制表符,引号等),我不会依赖于被激怒的东西。那里你想要一张短桌

struct SDL_table {
  unsigned code; 
  char value;
};

SDL_table table[] = {
  {SDL_SCANCODE_TAB, '\t'},
  // ... etc
};

然后,在早期检查失败后,快速搜索该表

auto it = std::find_if( std::begin(table), std::end(table), [&](SDL_table t) { return t.code == val; } );
if (it != std::end(table)) {
  _buffer_.InsertAtCursor( it->value );
  _buffer_.MutableCursor();
} else {
  // deal with unrecognized scancode
}

此时我可能会重构_buffer_代码。

char SDLK_to_char( unsigned sdlk ) {
  if( sdlk >= SDLK_a && sdlk <= SDLK_z )
     return 'a' + (val-SDLK_a);
  if( sdlk >= SDLK_0 && sdlk <= SDLK_9 )
     return '0' + (sdlk-SDLK_0);
  struct SDLK_entry {
    unsigned code; 
    char value;
  };

  static const SDLK_entry table[] = {
    {SDL_SCANCODE_TAB, '\t'},
    // ... etc
  };

  auto it = std::find_if( std::begin(table), std::end(table),
   [&](auto&& t) { return t.code == sdlk; }
  );
  if (it != std::end(table))
    return it->value;
  return 0; // null for failure
}

然后

char c = SDLK_to_char( sdlk );

if (c) {
  _buffer_.InsertAtCursor( c );
  _buffer_.MutableCursor().Right();
}

答案 1 :(得分:2)

好吧,我会避免使用宏 - 即使我必须用案例写出完整的switch

但首先我会考虑另一种方法。

例如,将SDLK_...翻译成...的查找表。在C ++中,您可以将其基于std::map

像:

  std::map<SDLK_type, char> look_up_table = {{SDLK_a, 'a'}, {SDLK_b, 'b'}};

  auto i = look_up_table.find(SDLK_value);
  if (i != look_up_table.end())
  {
    char c = i->second;
    _buffer_.InsertAtCursor(c);
    _buffer_.MutableCursor().Right();
  }
  else
  {
    std::cout << "Not found" << std::endl;
  }