C 在不使用宏的情况下简化并减少代码量
有时,在某些情况下,重复简单代码块是不可避免的。为了进行说明,请使用以下示例代码: 注意:此代码仅用于说明,实际代码更大、更复杂。它也可能包含错误,但问题的关键不在于此C 在不使用宏的情况下简化并减少代码量,c,optimization,coding-style,macros,C,Optimization,Coding Style,Macros,有时,在某些情况下,重复简单代码块是不可避免的。为了进行说明,请使用以下示例代码: 注意:此代码仅用于说明,实际代码更大、更复杂。它也可能包含错误,但问题的关键不在于此 开关(cmd){ case CMD_BLOCK_读取: if(当前用户!=密钥){ 错误(“访问冲突-无效密钥!”); res=CR\u访问被拒绝; 打破 } 如果(当前状态new_state; switch(cmd_desc->io_dir) { case IO_READ: if(block_read(id
开关(cmd){
case CMD_BLOCK_读取:
if(当前用户!=密钥){
错误(“访问冲突-无效密钥!”);
res=CR\u访问被拒绝;
打破
}
如果(当前状态<状态忙){
警告(“此状态下不允许使用命令%s!”,cmd_name[cmd]);
res=不允许的CR;
打破
}
如果(ioctl(fd,HPI\u CTL\u BR)!=0){
警告(“握手失败(%s)。已中止!”,strerror(errno));
res=CR\u超时;
转到邮政局;
}
如果(块读取(id)!=0){
错误(“读取%d个块(%s)失败!已中止!”,id,strerror(errno));
res=CR_故障;
goto send_nop;
}
res=成功;
打破
case CMD_BLOCK_WRITE:
if(当前用户!=密钥){
错误(“访问冲突-无效密钥!”);
res=CR\u访问被拒绝;
打破
}
如果(当前状态<状态忙){
警告(“此状态下不允许使用命令%s!”,cmd_name[cmd]);
res=不允许的CR;
打破
}
如果(ioctl(fd,HPI\u CTL\u BR)!=0){
警告(“握手失败(%s)。已中止!”,strerror(errno));
res=CR\u超时;
转到邮政局;
}
如果(块写入(id)!=0){
错误(“未能写入%d个块-%s。命令已中止!”,id,strerror(errno));
res=CR_故障;
goto send_nop;
}
res=成功;
打破
案例CMD\U REQ\U START:
if(当前状态<状态就绪){
警告(“此状态下不允许使用命令%s!”,cmd_name[cmd]);
res=不允许的CR;
打破
}
state=state\u BUSY;
如果(ioctl(fd,HPI\u CTL\u BR)!=0){
警告(“握手失败(%s)。已中止!”,strerror(errno));
res=CR\u超时;
goto send_nop;
}
如果(块读取(id)!=0){
错误(“读取%d个块(%s)失败!已中止!”,id,strerror(errno));
res=CR_故障;
转到邮政局;
}
res=成功;
打破
}
/*其余28个左右类似的命令*/
}
正如您所看到的,由于小的差异和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; \
}
/*注意:不要在大交换机之外使用这些宏*/
#定义检查键(\u键)\
如果(当前用户!=(\u键))\
{ \
错误(“访问冲突!”)\
res=CR\u访问被拒绝\
中断\
}
#定义检查状态(\u状态)\
如果(当前状态<\u状态)\
{ \
警告(“此状态下不允许使用命令%s!”,cmd_name[cmd])\
res=不允许的CR\
中断\
}
#定义握手(\u失败)\
如果(ioctl(fd,CTL_BR)!=0)\
{ \
警告(“握手失败(%s)。已中止!”,strerror(errno))\
res=CR\u超时\
后进先出\
}
#定义块读取(\u id,\u fail)\
如果(块读取((int)((u id))!=0)\
{ \
错误(“读取%d个块(%s)失败!已中止!”,(int)\u id,strerror(errno))\
res=CR_故障\
后进先出\
}
#定义块写入(\u id,\u失败)\
如果(块写入((int)(_id))!=0)\
{ \
错误(“未能写入%d个块-%s。已中止!”,(int)\u id,strerror(errno))\
res=CR_故障\
后进先出\
}
…并使用它们编写相同的代码。代码变得越来越小,可读性也越来越强:
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 */
<..>
开关(cmd)
{
case CMD_BLOCK_读取:
检查_键(KEY);
检查状态(状态为忙碌);
握手(post_resp);
块读取(id,发送\U nop);
res=成功;
打破
case CMD_BLOCK_WRITE:
检查_键(KEY);
检查状态(状态为忙碌);
握手(post_resp);
块写入(id,send\u nop);
res=成功;
打破
案例CMD\U REQ\U START:
{
检查_状态(状态_就绪);
state=state\u BUSY;
握手(send_nop);
块读取(id、后处理);
res=成功;
打破
}
/*其余28个左右类似的命令*/
代码看起来更像是某种脚本语言,而不是老式的C语言,而且非常难看,但为了可读性,我愿意牺牲它
问题是您如何应对类似情况?有哪些更优雅的解决方案和最佳实践
p.S.我承认,在一般情况下,宏和goto语句是糟糕设计的标志,因此无需大肆宣扬它们有多邪恶或我的编程风格有多糟糕。我不想说Python源代码是组织的典范,但它包含(IMHO)宏用于简化复杂代码的一个很好的例子 Python主循环实现了一个基于堆栈的字节码执行VM。它包含一个巨大的开关大小写,Python支持的每个操作码都有一个大小写。操作码的分派如下所示:
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;
其中,TOP
、SECOND
和STACKADJ
都定义为在堆栈对象上操作的宏。一些宏有备用的#define
用于协助调试。所有操作码都是以这种方式编写的,通过用是一种微型脚本语言
在我看来,谨慎、明智和有限地使用宏可以提高代码可读性并使逻辑更清晰
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;
#define IO_NOOP 0
#define IO_READ 1
#define IO_WRITE 2
struct cmd_desc {
int check_key; /* non-zero to do a check */
int check_state;
int new_state;
void* handshake_fail;
int io_dir;
void* io_fail;
};
const struct cmd_desc cmd_desc_list[] = {
{ 1, STATE_BUSY, -1, &&post_resp, IO_READ, &&send_nop }, /* CMD_BLOCK_READ */
{ 1, STATE_BUSY, -1, &&post_resp, IO_WRITE, &&send_nop }, /* CMD_BLOCK_WRITE */
{ 0, STATE_READY, STATE_BUSY, &&send_nop, IO_READ, &&post_rep } /* CMD_REQ_START */
};
const struct cmd_desc* cmd_desc = cmds[cmd];
if(cmd_desc->check_key) {
if(current_user != key) {
ERROR("Access violation - invalid key!");
return CR_ACCESS_DENIED;
}
}
if(cmd_desc->check_state != -1) {
if(current_state check_state) {
WARN("Command %s is not allowed in this state!", cmd_name[cmd]);
return CR_NOT_PERMITTED;
}
}
if(cmd_desc->new_state != -1)
state = cmd_desc->new_state;
switch(cmd_desc->io_dir) {
case IO_READ:
if(block_read(id) != 0) {
ERROR("Failed to read %d block (%s)! Aborted!", id, strerror(errno));
res = CR_FAIL;
goto *cmd_desc->io_fail;
}
break;
case IO_WRITE:
if(block_write(id) != 0) {
ERROR("Failed to write %d block (%s)! Aborted!", id, strerror(errno));
res = CR_FAIL;
goto *cmd_desc->io_fail;
}
break;
case IO_NOOP:
break;
}
res = CR_SUCCESS;