C语言中的“with”宏

5

我在寻找一个类似于with结构的宏。 使用方法应该像这样:

with (lock(&x), unlock(&x)) {
    ...
}

这可能对其他用途有用。

我想出了这个宏:

#define __with(_onenter, _onexit, v) \
    for (int __with_uniq##v=1; __with_uniq##v > 0; )\
        for (_onenter; __with_uniq##v > 0; _onexit) \
            while (__with_uniq##v-- > 0)

#define _with(x, y, z) __with(x, y, z)
#define with(_onenter, _onexit) _with(_onenter, _onexit, __COUNTER__)

它有3个嵌套循环,因为它应该:
  1. 初始化循环计数器(当然只适用于C99)
  2. 可能初始化_onenter变量(例如with(int fd=open(..), close(fd))
  3. 允许在代码块中使用break(也可以使用continue。并且该宏可以调整为assert()
我在XV6 OS的代码中使用过它,似乎非常有用。
我的问题是 - 这样一个宏存在哪些最大的问题? 除了使用C宏(特别是实现新控制流结构的宏)之外。
到目前为止,我已经发现以下缺点/问题:
  1. 不支持returngoto(但它可以节省内核代码中的一些goto
  2. 不支持错误(如fd <0)。 我认为这个问题是可以解决的。
  3. 仅支持gnu89 / c99及以上版本(循环计数器。唯一的变量技巧不是必需的)
  4. 比简单的锁定/解锁效率略低。我认为这是微不足道的。
还有其他问题吗?是否有更好的方法在C中实现类似的结构?
1个回答

6

那个宏让我感到害怕。我更喜欢使用传统的goto方法

那种方法很原始,但大多数C程序员都熟悉这种模式,如果他们不熟悉,可以通过阅读本地代码来理解。没有隐藏的行为。因此,它非常可靠。

你的宏很聪明,但对大多数人来说都是新的,并且带有隐藏的陷阱。新的贡献者必须遵守规则,例如“不要在with块中使用returngoto”以及“break将跳出with块,而不是跳出周围的循环”。我担心错误会很常见。

如果您可以向编译器添加有关此结构误用的警告,则平衡将发生变化。使用clang, 这似乎是一个选项。在这种情况下,会检测到误用,并且您的代码将保持可移植性到其他编译器。

如果你愿意限制自己使用GCC和Clang,你可以使用cleanup属性。这将使你的示例看起来像这样:
lock_t x = NULL __attribute__((cleanup(unlock)));
lock(&x);

当变量超出作用域时,将使用指向该变量的指针调用unlock。这与其他语言特性(如returngoto)以及混合C/C++项目中的异常集成。


哦,终于收到回复了...谢谢,我不知道cleanup - 听起来很有用。除了可怕之外,这个宏还有更具体的问题吗? - Elazar
1
缺乏对return的支持似乎是一个致命问题,除非你在遵循严格的编码标准,禁止在函数中间使用return语句。在with块中使用return看起来不起眼,但会在运行时造成严重后果。 - pdw
我理解并同意return是一个大问题(所以cleanup更好)。但是goto不需要同样严格的编码标准吗?在这方面,goto有什么优势呢? - Elazar
1
Gotos,如我所链接的文章中所使用的,是原始的,但大多数C程序员都熟悉这种模式,并且没有隐藏的行为。因此,它非常可靠。对于大多数人来说,你的宏是新的,并且带有隐藏的陷阱。一切取决于程序员能够多快、多可靠地学习规则,比如“不要从with块中returngoto出去”,以及“break将会跳出with块,而不是跳出周围的循环”。 - pdw
1
所以你的意思是,goto语句是一个警告标志,而用户定义的宏不是。好的。 - Elazar

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接