c - 在不使用宏的情况下简化和减少代码量

标签 c optimization coding-style macros

<分区>

有时,在某些情况下,简单代码块的重复是不可避免的。为了说明这一点,请使用此示例代码:

注意:此代码仅用于说明目的,实际代码更大更复杂。它也可能包含错误,但这个问题的重点不是这个。

switch(cmd) {
    case CMD_BLOCK_READ:
        if(current_user != key) {
            ERROR("Access violation - invalid key!");
            res = CR_ACCESS_DENIED;
            break; 
        }
        if(current_state < STATE_BUSY) {
            WARN("Command %s is not allowed in this state!", cmd_name[cmd]);
            res = CR_NOT_PERMITTED;
            break;
        }
        if(ioctl(fd, HPI_CTL_BR) != 0) {
            WARN("Handshake failed (%s). Aborted!", strerror(errno));
            res = CR_TIME_OUT;
            goto post_resp;
        }
        if(block_read(id) != 0) {
            ERROR("Failed to read %d block (%s)! Aborted!", id, strerror(errno));
            res = CR_FAIL;
            goto send_nop;
        }

        res = CR_SUCCESS;
        break;
    case CMD_BLOCK_WRITE:
        if(current_user != key) {
            ERROR("Access violation - invalid key!");
            res = CR_ACCESS_DENIED;
            break; 
        }
        if(current_state < STATE_BUSY) {
            WARN("Command %s is not allowed in this state!", cmd_name[cmd]);
            res = CR_NOT_PERMITTED;
            break;
        }
        if(ioctl(fd, HPI_CTL_BR) != 0) {
            WARN("Handshake failed (%s). Aborted!", strerror(errno));
            res = CR_TIME_OUT;
            goto post_resp;
        }
        if(block_write(id) != 0) {
            ERROR("Failed to write %d block - %s. Command aborted!", id, strerror(errno));
            res = CR_FAIL;
            goto send_nop;
        }
        res = CR_SUCCESS;
        break;
    case CMD_REQ_START:
        if(current_state < STATE_READY) {
            WARN("Command %s is not allowed in this state!", cmd_name[cmd]);
            res = CR_NOT_PERMITTED;
            break;
        }
        state = STATE_BUSY;
        if(ioctl(fd, HPI_CTL_BR) != 0) {
            WARN("Handshake failed (%s). Aborted!", strerror(errno));
            res = CR_TIME_OUT;
            goto send_nop;
        }
        if(block_read(id) != 0) {
            ERROR("Failed to read %d block (%s)! Aborted!", id, strerror(errno));
            res = CR_FAIL;
            goto post_resp;
        }
        res = CR_SUCCESS;
        break;
    }

    /* The remaining 28 or so similar commands */
}

如您所见,由于细微差别和大量使用 break/goto 语句,无法使用函数或内联。我通常做的是定义一些宏:

/* NOTE: DO NOT USE these macros outside of Big Switch */
#define CHECK_KEY(_key) \
   if(current_user != (_key)) \
   { \
      ERROR("Access violation!"); \
      res = CR_ACCESS_DENIED; \
      break; \
   }
#define CHECK_STATE(_state) \
   if(current_state < _state) \
   { \
      WARN("Command %s is not allowed in this state!", cmd_name[cmd]); \
      res = CR_NOT_PERMITTED; \
      break; \
   }

#define HANDSHAKE(_fail) \
   if(ioctl(fd, CTL_BR) != 0) \
   { \
      WARN("Handshake failed (%s). Aborted!", strerror(errno)); \
      res = CR_TIME_OUT; \
      goto _fail; \
   }

#define BLOCK_READ(_id, _fail) \
   if(block_read((int)(_id))!= 0) \
   { \
      ERROR("Failed to read %d block (%s)! Aborted!", (int)_id, strerror(errno)); \
      res = CR_FAIL; \
      goto _fail; \
   }

#define BLOCK_WRITE(_id, _fail) \
   if(block_write((int)(_id)) != 0) \
   { \
      ERROR("Failed to write %d block - %s. Aborted!", (int)_id, strerror(errno)); \
      res = CR_FAIL; \
      goto _fail; \
   }

..并使用它们编写相同的代码。代码变得大大更小并且(可以说)更易读:

switch(cmd) 
{
case CMD_BLOCK_READ:
   CHECK_KEY(key);
   CHECK_STATE(STATE_BUSY);
   HANDSHAKE(post_resp);
   BLOCK_READ(id, send_nop);
   res = CR_SUCCESS;
   break;
case CMD_BLOCK_WRITE:
   CHECK_KEY(key);
   CHECK_STATE(STATE_BUSY);
   HANDSHAKE(post_resp);
   BLOCK_WRITE(id, send_nop);
   res = CR_SUCCESS;
   break;
case CMD_REQ_START:
{
   CHECK_STATE(STATE_READY);
   state = STATE_BUSY;
   HANDSHAKE(send_nop);
   BLOCK_READ(id, post_resp);
   res = CR_SUCCESS;
   break;
}
/* The remaining 28 or so similar commands */
<..>

代码看起来更像是某种脚本语言,而不是旧的 C,而且真的很难看,但为了可读性,我愿意牺牲它。

问题是您如何应对类似情况?有哪些更优雅的解决方案和最佳实践?

P.S. 我承认,在一般情况下,宏和 goto 语句是糟糕设计的标志,因此无需提示它们有多邪恶或我的编程有多糟糕风格是。

最佳答案

我不会声称 Python 源代码是组织的典范,但它包含(恕我直言)一个使用宏来简化复杂代码段的好例子。

Python 主循环实现了一个字节码执行的基于堆栈的 VM。它包含一个巨大的 switch-case,每个 Python 支持的操作码都有一个 case。操作码的调度如下所示:

case STORE_ATTR:
    w = GETITEM(names, oparg);
    v = TOP();
    u = SECOND();
    STACKADJ(-2);
    err = PyObject_SetAttr(v, w, u); /* v.w = u */
    Py_DECREF(v);
    Py_DECREF(u);
    if (err == 0) continue;
    break;

其中TOPSECONDSTACKADJ都定义为操作栈对象的宏。一些宏有备用的 #define 用于协助调试。所有的操作码都是这样写的,通过这种微型脚本语言表达逻辑,有助于使每个操作码的实现更加清晰。

在我看来,谨慎、明智和有限地使用宏可以提高代码的可读性并使逻辑更清晰。在您的情况下,宏隐藏了一些小但重要的功能,使用宏来标准化实现并确保您没有相同代码片段的多个副本要更新可能很有用。

关于c - 在不使用宏的情况下简化和减少代码量,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14707689/

相关文章:

C - 从内存中读取和写入字节

c - C语言只打印字符串中以指定字母开头的单词

c++ - 如何将 C/C++ 库代码封装为可在具有多个实例的单独线程中运行?

bool 值 : $var === true vs $var 的 PHP IF 语句

c - 从 `char' 到 `char*' 的转换无效

c++ - 编译器优化如何加快简单操作之间的时间?

javascript - 在大量对象中改变单个对象属性的最快方法是什么?

Php数组搜索优化

string - 即使字符串只使用过一次,我是否应该使用常量而不是字符串?

.net - Winform 样式问题 : Windows classics style?