让宏定义返回函数的值是不好的做法吗?

Is it bad practice to have a macro definition return a value for a function?

本文关键字:宏定义 返回 函数      更新时间:2023-10-16

使用定义为有条件返回值的宏有一个缺点,即仅从查看客户端代码时无法看出可能在宏的位置退出。

我正在考虑的用例是编写一个值和错误检查,如下所示:

#define WRITE_CHK(file, param)
if (!write_that_returns_zero_on_fail(file, param)) {
   handle_error();
   return false;
}
客户机代码:

bool myfunc()
{
   ...
   WRITE_CHK(file, param) // function might return here
   ...
   return true;
}

我很好奇这个宏的好处(它将在我的代码中的许多地方使用)是否会超过上面提到的缺点。除了简单地展开(不使用宏)之外,是否有更好的替代方案?

标准答案是"不要使用宏";但这通常有点过于简单化了。在某些情况下,它们可以大大减少冗长的样板文件。

那么,为什么不将事实编码到宏名称中呢?例:WRITE_OR_RETURN_ON_FAILURE;它可能有点冗长,但它不太可能使代码的读者出错。

在宏中隐藏控制流并不常用,因此对于必须理解或调试代码的开发人员来说,这可能会使他们感到困惑。

我建议完全不要使用宏,但如果必须使用宏,请确保控制流部分是显式的。

// assume handle_error returns true
#define WRITE_FAILED(file, param)
(!write_that_returns_zero_on_fail(file, param) && handle_error())
// macro usage
if(WRITE_FAILED(file, param))
{
    return;
}

另一个要问自己的问题是——你为什么要在这里使用宏呢?如果要隐藏控制流,那就不是个好主意。还有别的原因吗?

创建两步宏:

#define WRITE_CHK_(file, param, HANDLER)
if (!write_that_returns_zero_on_fail(file, param)) {
    handle_error();
    HANDLER
}
#define RFALSE return false;
#define WRITE_CHK(file, param) WRITE_CHK_(file, param, RFALSE)

如果您需要在其他地方进行类似的检查(或直接使用WRITE_CHK(file, param, return false)),则可以制作其他包装器

是的,很糟糕。使用goto

 #define fail_unless(x) do {if(!(x)) goto fail;} while (0)
 int foo()
 {
     fail_unless( my_write(...) );
     fail_unless( bar() );
     return 0;
 fail:
     cleanup();
     return -1;
 }

Edit:如果你因为不明白while循环在那里做什么而投票,你应该问一下。这是确保C宏在任何上下文中都充当单个语句的标准技术。如果你的宏展开为If语句(就像其他的宏一样),那么你会得到意想不到的副作用,例如:

 #define DONT_DO_THIS(x) if (!x) { foo; }
 if (a) DONT_DO_THIS(x)
 else something_else();

您期望在!a时执行something_else,但宏实际上扩展为:

 if (a)
     if (!x) { foo;}
     else something_else();

something_else在a && x时执行,而不是在!a时执行。