【问题标题】:wrapping goto labels in parenthesis用括号包裹 goto 标签
【发布时间】:2021-02-25 08:02:51
【问题描述】:

所以我有一个像这样的 marco 函数:

#define PROPOGATE_METHOD(status, function, propogationMethod) \
     status = function; \
     if(status != eSuccess)\
     {\
        propogationMethod; \
     }

所以就像任何优秀的开发人员都会做的那样,我想将每个参数都包装成这样:

#define PROPOGATE_METHOD(status, function, propogationMethod) \
     (status) = (function); \
     if((status) != eSuccess)\
     {\
        (propogationMethod); \
     }

但是如果我用 goto 或 return 调用这个宏函数,我会得到一个错误(预期 goto 之前的表达式)。 i.e. PROPOGATE_METHOD(status, functionCall(), goto Error;);

想解决这个问题?我正在考虑将 goto 移动到宏函数中,并环绕标签,但这会引发另一个错误: expected identifier or ‘*’ before ‘(’ token

【问题讨论】:

  • 该宏展示了宏的危险之一。尝试将它与 if (condition) PROPAGATE_METHOD(...) 之类的东西一起使用,然后看到你的代码惨遭失败。
  • macroevil 密切相关。 goto 是到相对地址的跳转(近跳转)。您如何确保跳​​转地址对宏有意义?
  • 向我们展示您如何在最小的主函数中使用它。 TBH,我认为任何优秀的开发人员都会做的就是根本不使用这样的宏。
  • 我不明白为什么这个问题会被否决。我认为这些宏是个坏主意,但我确实认为这是个好问题。
  • “就像任何优秀的开发人员都会做的那样,我想包装每个参数” - 优秀的开发人员会理解建议存在的原因、解决的问题以及最重要的问题重要的是,当它不适用时。盲目地做某事并不是好的发展。

标签: c goto


【解决方案1】:

就像任何优秀的开发人员都会做的那样,我想将每个参数都包装起来

@StoryTeller 在评论部分对此做出了很好的回应。 “一个好的开发者会理解这个建议为什么存在,它解决了什么问题,最重要的是,它什么时候不适用。盲目地做某事不是好的开发。”

另一个很好的适用引用是“盲目遵循最佳实践不是最佳实践”

在这里,您似乎确实要添加括号,因为有人说“在参数周围加上括号是件好事”。不是因为在这种特殊情况下的任何有效目的。

跳过宏

我真的不明白这个宏的用途。 TBH,看起来你在炫耀,但是像这样的宏很可能会导致难以追踪的错误。如果只是为了节省一些行,你实际上可以在没有任何宏的情况下制作一个不错的单行。

if((status = foo()) != eSuccess) goto Error;

if((status = foo()) != eSuccess) return x;

if((status = foo()) != eSuccess) bar();

在许多情况下,我更喜欢在两行或三行上制作这些内容。但上述情况并没有那么糟糕。我肯定会说它比宏更好。请记住status = foo() 周围的额外括号。如果忘记这是一个大问题,您可以这样做:

int foo_wrapper(int *status) { return *status = foo(); }
...
if(foo_wrapper(&status) != eSuccess) goto Error;

甚至:

int status_assign(int *dest, int src) { return *dest = src; }
...
if(status_assign(&status, foo()) != eSuccess) goto Error;

另一方面,这应该不是问题,因为如果你用-Wall编译,你应该这样做,你会得到:

warning: suggest parentheses around assignment used as truth value

就我个人而言,当它是单个语句时,我不认为大括号非常重要,但如果你想要一个带大括号的单行符,那就这样做吧:

if((status = foo()) != eSuccess) { goto Error; }

有些人会喜欢它。有些人不会,但这不是世界上最重要的问题。但在您建议的宏之前,我更喜欢上面的任何

比较这些:

PROPOGATE_METHOD(status, foo(), goto Error;);
if((status = foo()) != eSuccess) goto Error;

当并排比较时,我真的看不出宏有什么作用。它不会使任何事情变得更清晰或更安全。没有宏,我可以确切地看到正在发生的事情,我不需要对此感到怀疑。宏有其用途,但据我在这里看到的,这不是其中之一。

来自下面的 cmets

我明白了。我正在经历并重构一个代码库。我不喜欢使用这些 if 语句来减少代码库,而是更喜欢宏语句,因为

我可以理解,但我真的鼓励你重新考虑。至少如果你被允许这样做。如果我要重构该代码库,那么摆脱该宏将具有很高的优先级。毕竟,重构就是重写代码,使其变得更好,专注于设计和可读性。如果您不想这样做,请在statusfunction 周围加上括号,然后离开。它不会使它变好,但也不会造成任何伤害。

如果是你写了那个宏,我不会使用这个表达式,但既然不是你,我可以使用它。 “修复”那个宏真的是在擦屎。不管你做什么,它永远不会发光,永远都是一团糟。

【讨论】:

  • 我明白了。我正在经历并重构一个代码库。我不喜欢使用这些 if 语句来减少代码库,而是更喜欢宏语句,因为
  • @GeorgeWu 如果我要重构代码库,那么摆脱那个宏将具有很高的优先级。 ;)
  • @GeorgeWu 但是如果你不敢碰它,在statusfunction 周围加上括号,然后收工。不好但是比以前好多了,但是试图修复这个宏真的就像在擦粪。无论你做什么,它都不会发光。
【解决方案2】:

不管该宏是否有用或令人困惑,以及 goto 的优点,请考虑宏/定义中的括号的用途。如果你说,这个:

#define FOO a + b
...
int y = x * FOO;

你最终得到的是y = x * a + b(因为它只是文本替换,而不是真正的变量),它与y = (x * a) + b 相同。因此,将 (a + b) 周围的括号放在 FOO 中可以解决此问题。

这当然有类似的问题(在x 和宏之外),有类似的解决方案:

#define FOO2(x) x * 123
...
int y = FOO2(a + b);

现在,你有

#define BAR(x) { x };

那里有类似的问题吗? x 应该包含什么括号将消除源自运算符优先级的类似问题?我真的没有看到这样的问题,在某种程度上,大括号已经可以保护x 部分免受宏周围代码的影响。添加括号只是强制 x 成为一个表达式,而不是一个完整的语句。

【讨论】:

  • status 周围的括号也没什么用吧?
  • @klutt 在状态周围有() 确实有意义。有一大堆运算符的优先级低于==/!=
  • @PSkocik 当然,如果您正在做一些非常愚蠢的事情,例如将x, y 作为status 的参数,那么它会有所作为。但是在正常使用期间呢?
  • x && y 可能是一个更好的例子,因为您不能使用 x, y 作为单个宏参数。我会用括号括起来,因为没有理由不这样做。但是,我不会首先使用 OP 的宏:D.
  • @PSkocik 没错,它不会造成任何伤害。但是我说的对吗,括号 status 仅在您做一些非常愚蠢的事情时才能保护您,并且它与括号 function 所获得的保护无法相提并论吗?
【解决方案3】:

goto 语句 (goto label;) 不是表达式,因此不能将其括起来(goto label 也不能没有 ;,它甚至不是 C 语法中可单独识别的构造)。

即使你传递了一些你可以用括号括起来的东西(例如,longjmp(jbuf,1)),在这种情况下用括号括起来也没多大意义({ HOOK; })。

现在,如果您在HOOK_EXPR * 2 这样的上下文中扩展它,那么括号将有助于强制HOOK_EXPR* 更紧密地分组(假设您将3+4 传递为HOOK_EXPR),但在这种情况下你不需要它们。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多