在宏中使用打开/关闭括号来强制执行C中的配对

时间:2017-01-31 08:19:02

标签: c macros c-preprocessor

假设我要求如果在范围内执行操作,则必须在同一范围内撤消操作。一个例子是进入和离开关键部分。

为了强制用户使用配对的do-undo操作,定义了一对使用open和close括号的宏:

#define BEGIN \
{ \
    do_something();

#define END \
    undo_something(); \
}

当然,有很多方法可以防止"恶意"编码器可以欺骗这些宏(例如通过添加一个开口或右大括号),但通常这有助于记住BEGIN必须后跟END。此外,例如,如果注释掉现有的BEGIN,编译器将会抱怨,表明还必须删除END

我看到这是几个内部项目。正如我所说,它不提供100%的保护,但这种做法是否可以证明有害?这是一个众所周知的做法吗?

5 个答案:

答案 0 :(得分:4)

如果我理解您要强制使用BEGIN / END的问题。

你可以使用臭名昭着的goto

来做到这一点
#define BEGIN \
do { \
    goto check_label_end; \
label_begin: \
    puts("begin"); \
} while (0);

#define END \
do { \
    puts("end"); \
    break; \
check_label_end: \
    goto label_begin; \
} while (0);

然后,如果在没有BEGIN部分的情况下使用END,您会收到错误消息:

error: label ‘check_label_end’ used but not defined

修改

正如@KlasLindbäck在评论中指出的那样,此版本限制了每个功能使用BEGIN / END一次。

您可以传递标签名称以使用多个块:

#define BEGIN(op) \
do { \
    goto check_label_end_##op; \
label_begin_##op: \
    puts("begin"); \
} while (0);

#define END(op) \
do { \
    puts("end"); \
    break; \
check_label_end_##op: \
    goto label_begin_##op; \
} while (0);

BEGIN(undo)
...
END(undo)

BEGIN(something_else)
...
END(something_else)

答案 1 :(得分:2)

对于简单的情况,我没有看到问题,但是你可以构建一个有趣的地方,这会破坏你的代码:

BEGIN
...
for(...) {
    END
    ...
    BEGIN
}
...
END

答案 2 :(得分:2)

这样做的明智方法不是通过宏,而是通过功能。例如:

typedef void task_t (void);

inline void do_critical_stuff (task_t* task)
{
  enter_critical_section();
  task();
  leave_critical_section();
}

或者,如果您正在寻找完全可变的东西:

inline void do_stuff (begin_t* begin, task_t* task, end_t* end)
{
  begin();
  task();
  end();
}

答案 3 :(得分:0)

#define FOO(body) {\
    do_something();\
    body\
    undo_something();\
}

您可以使用此宏:

FOO({
    //your code here
})

-

编辑:

好的,StoryTeller的例子以我从未见过的方式轻松破解我的代码。

我想我很少在一行中写出多个声明。

逗号运算符和函数调用永远不会像a=(1,2);memcpy(&a,&b,1);

那样中断

答案 4 :(得分:0)

我没有看到任何严重的问题,BEGIN / END使用(和缩进)的方式与其他块相同。

显而易见的警告是,return / longjmp / goto打破了这种模式,但只要所有程序员都知道这一点,就不应该成为一个问题。< / p>

示例:

void foo() {
  ...
  BEGIN
    ...
    if (some_condition) {
      return;  // Cleanup code skipped
    }
    ...
  END
  ...
}