使用代码块作为C宏的参数可以吗?

使用代码块作为C宏的参数可以吗?,c,macros,nesc,C,Macros,Nesc,我有一个模式,基本上是一些样板代码,中间有一个变化的部分 if(condition){ struct Foo m = start_stuff(); { m.foo = bar(1,2); m.baz = 17; } //this part varies end_stuff(); } 让宏taht将中间代码块作为参数可以吗?C语言中宏扩展的规则似乎非常复杂,所以我不确定将来是否会出现任何可能会影响我的情况(特别是,如果我的代码中有逗号,我不理解宏参数是如何分开的) 到目前

我有一个模式,基本上是一些样板代码,中间有一个变化的部分

if(condition){
    struct Foo m = start_stuff();
    { m.foo = bar(1,2); m.baz = 17; } //this part varies
    end_stuff();
}
让宏taht将中间代码块作为参数可以吗?C语言中宏扩展的规则似乎非常复杂,所以我不确定将来是否会出现任何可能会影响我的情况(特别是,如果我的代码中有逗号,我不理解宏参数是如何分开的)

到目前为止,我唯一能想到的是,如果我在宏中使用循环语句,则捕获
break
continue
,这对于我的特定用例来说是一个可接受的权衡


编辑:当然,如果可以,我会使用函数。我在这个问题中使用的示例是简化的,并且没有展示只能使用宏魔术的位。

如果宏参数没有无保护的逗号,则可以将代码块放入宏参数中。在您的示例中,参数中唯一的逗号被保护,因为它被括号包围

请注意,只有括号中有逗号。括号(
[]
)和大括号(
{}
)没有。(如注释中所述,尖括号(
)也不适用。)


但是,如果代码块参数是宏的最后一个参数,则可以使用可变宏来增加灵活性。但要注意:灵活性的提高也意味着错误可能会被忽略。如果这样做,只需确保括号是平衡的。(同样,对于宏处理器来说,只有括号才重要。)

您可以将代码块放入宏中,但必须警告您,使用调试器会使调试更加困难。IMHO最好是编写一个函数或者剪切粘贴代码行。

那么函数指针(或者可以选择
内联
函数)怎么样


在回答您的问题“是否可以使用宏”之前,我想知道为什么要将该代码块转换为宏。你想得到什么,付出什么代价

若重复使用相同的代码块,最好将其转换为函数,也可以是内联函数,并将其留给编译器使其内联或不内联

如果遇到crash\issue,调试宏是一项乏味的任务。

“可以吗?”可能意味着两件事:

  • 行吗?这里的答案通常是肯定的,但也有陷阱。第一,是一个不加保护的逗号。基本上,记住宏扩展是一个复制粘贴操作,预处理器不理解它复制粘贴的代码

  • 这是个好主意吗?我认为答案通常是否定的。这会使你的代码不可读,也很难维护。在一些罕见的情况下,如果实施得好,这可能比备选方案要好,但这是个例外


  • 作为替代,您可以考虑使用在复合语句前面的宏,如下所示。这样做的好处之一是,所有调试器仍然能够进入复合语句,而将复合语句作为宏参数方法的情况并非如此

    //usage
    MY_MACRO(Foo, condition) {
       m.foo = bar(1,2);
       m.baz = 17;
    }
    
    使用一些goto魔法(是的,“goto”在某些情况下可能是邪恶的,但我们在C中几乎没有其他选择),宏可以实现为:

    #define CAT(prefix, suffix)            prefix ## suffix
    #define _UNIQUE_LABEL(prefix, suffix)  CAT(prefix, suffix)
    #define UNIQUE_LABEL(prefix)           _UNIQUE_LABEL(prefix, __LINE__)
    
    #define MY_MACRO(typ, condition)  if (condition) { \
                                       struct typ m = start_stuff(); goto UNIQUE_LABEL(enter);} \
                                      if (condition)  while(1) if (1) {end_stuff(); break;} \
                                                               else UNIQUE_LABEL(enter):
    
    请注意,当禁用编译器优化时,这对性能和占用空间的影响很小。此外,当运行调用end_stuff()函数时,调试器似乎会跳回MY_宏行,这并不是真正需要的

    此外,您可能希望在新的块作用域内使用宏,以避免“m”变量污染您的作用域:

    {MY_MACRO(Foo, condition) {
        m.foo = bar(1,2);
        m.baz = 17;
    }}
    
    当然,在复合语句的嵌套循环中不使用“break”会跳过“end_stuff()”。为了允许那些中断周围循环并仍然调用“end_stuff()”,我认为您必须用一个开始标记和一个结束标记将复合语句括起来,如下所示:

    #define  MY_MACRO_START(typ, condition)  if (condition) { \
                                              struct typ m = start_stuff(); do {
    
    #define  MY_MACRO_EXIT                   goto UNIQUE_LABEL(done);} while (0); \
                                             end_stuff(); break; \
                                             UNIQUE_LABEL(done): end_stuff();}
    
    MY_MACRO_START(foo, condition) {
        m.foo = bar(1,2);
        m.baz = 17;
    } MY_MACRO_END
    
    请注意,由于该方法中的“中断”,MY_MACRO_EXIT宏只能在循环或开关中使用。当不在循环中时,可以使用更简单的实现:

    #define  MY_MACRO_EXIT_NOLOOP  } while (0); end_stuff();}
    

    我使用“condition”作为宏参数,但如果需要,您也可以将其直接嵌入宏中。

    do while false是一个标准的习惯用法,允许您在需要时中断宏。我对宏的唯一保留意见是,它使调试变得困难,特别是在崩溃情况下。如果有一个大约100行的长宏块,并且在运行5小时后,在数百次宏调用中的一次调用中,代码在其中某个地方崩溃,那么您将很难跟踪它。对于编译错误,请使用clang。它识别大型宏中的语法错误。对于运行时错误,我通常使用
    gcc-eprog.c | grep-v^#| indent>prog dbg.c
    ,然后编译并链接
    prog dbg.c
    。这是笨拙的,但工作。你的概念似乎是好的,因为它是。。。但是你为什么不回电话让你的生活更轻松呢?在线速度很重要吗?正如@cup所指出的,调试起来很痛苦。@AhmedMasud:我应该在我的问题中提到,boiler plate包含一些不能作为函数参数传递的东西(结构的类型,一些其他东西的名称),而且我还需要在上面加上一些
    黑魔法。接收结构作为参数的回调函数可能适用于问题中的示例,但在我的实际代码中,唯一的替代方法是手工编写样板。@missingno aaah。。。那么你需要让你的魔法更神奇一点,我会发布另一种方法,你能预测编译器吗?是GCC吗?不幸的是,在我的rel代码中,结构的类型不同,并且是宏的额外参数。虽然可以使用void指针来解决这个问题,但是还有其他一些事情
    #define  MY_MACRO_START(typ, condition)  if (condition) { \
                                              struct typ m = start_stuff(); do {
    
    #define  MY_MACRO_EXIT                   goto UNIQUE_LABEL(done);} while (0); \
                                             end_stuff(); break; \
                                             UNIQUE_LABEL(done): end_stuff();}
    
    MY_MACRO_START(foo, condition) {
        m.foo = bar(1,2);
        m.baz = 17;
    } MY_MACRO_END
    
    #define  MY_MACRO_EXIT_NOLOOP  } while (0); end_stuff();}