【问题标题】:Macro function with several lines for the parameter?参数有几行的宏​​函数?
【发布时间】:2011-04-06 12:37:24
【问题描述】:

在 C++ 中, 我需要定义一个宏。 该宏会将代码“块”作为参数。

我们可以安全地使用几行代码作为宏函数的参数吗?

我问自己是否:

  1. 以下代码是否有效,被标准定义为有效,如“跨平台”中的?
  2. 有没有更好的方法来做同样的事情(我不能在那里使用模板函数,因为我需要上下文)。

#define MY_MACRO( expr ) DOSOMETHING( (expr) ); DOANOTHERTHING( (expr) ); // etc...

int my_function() {
    int o = RandomNumber();
    MY_MACRO( 
        int k = AFunction();
        k++;
        AnotherFunction( k + o ); // here I need to keep the context of the call
    ); 
}

我们不能使用仿函数,因为我们需要访问调用的上下文。 我们不能使用 lambda (snif),因为我们使用的是不提供它的旧编译器(而且我们无法更改它)。

【问题讨论】:

  • 这个宏有什么用?
  • 你试过了吗?发生了什么 ?我的猜测是,您传递的每个表达式“参数”都需要包含在 () 中。
  • 感谢 Paul 的编辑,找不到正确显示的方法。 @CharlesB:它生成的代码在生成的代码的几个部分中插入表达式参数。问题更多地与:我们可以安全地使用几行代码作为宏函数的参数吗?
  • @Paul R:我们现在正在尝试,但问题更多的是它是否由标准定义(使用多行宏)以及它是否是跨平台的。
  • @Klaim,如果您解释 context 是什么意思,也许它可能有助于回答。例如,您是否需要DOSOMETHINGDOANOTHERTHING 中调用者的所有状态?如果没有,你能不能把你需要的东西封装在一个你可以传递给这些的函子中?

标签: c++ macros


【解决方案1】:

16.3/9:

在预处理序列内 构成对 a 的调用的标记 类似函数的宏,换行符是 被认为是正常的空白 字符。

所以一般来说多行宏调用是可以的。当然,如果DOSOMETHINGDOANOTHERTHING 没有为范围引入大括号,那么您的特定示例将重新定义k

编辑:

我们不能使用函子,因为我们需要 访问上下文 称呼。我们不能使用 lambda (snif),因为我们使用的是旧编译器

通常的方法是在仿函数中捕获您需要的任何变量,就像 lambda 一样。 lambda 唯一能做而仿函数不能做的是“捕获所有内容”而不必输入它,但是编写 lambda 的人可以看到他们使用的变量,所以这只是方便,如果他们可以将它们全部输入必须。在您的示例中:

struct MyFunctor {
    int o;
    MyFunctor(int o) : o(o) {}
    void operator()() const {  // probably not void in practice
        int k = AFunction();
        k++;
        AnotherFunction( k + o );
    }
};

template<typename F>
void DoThings(const F &f) {
    DOSOMETHING(f());
    DOANOTHERTHING(f());
}

int my_function() {
    int o = RandomNumber();
    DoBothThings(MyFunctor(o));
}

您还可以通过引用捕获变量(通常使用指针作为数据成员而不是引用,以便可以对仿函数进行复制分配)。

如果“上下文”是指例如宏参数和/或宏主体可能包含breakgoto,因此需要在调用者的词法范围内,那么你可以' t 使用仿函数或 lambda。惭愧 ;-)

【讨论】:

  • 谢谢,这正是我需要知道的!
  • 这个例子可能会产生误导:我需要的宏可以在很多函数中使用,“上下文”是函数范围内可用的对象。将“上下文”替换为“范围”以理解我的意思。所以在这里,仿函数没有用,因为我必须在仿函数中移动我的所有函数,甚至是同一函数的不同部分。在实践中,那将是地狱。这就是为什么我希望我们可以使用 lambda。但我们不能:(
  • @Klaim: 嗯,如果你必须访问这么多变量以至于很难列出它们,那么我无论如何都会寻求重构,也许以不同的方式表达这些对象之间的逻辑联系,而不是仅仅,“目前它们都在范围内”。最简单的方法,让它们成为一些愚蠢的“状态”对象的所有成员并传递它。然后尝试找出如何使“状态”变得不那么愚蠢和更有意义。但我接受你可能在一个太深的洞里爬不出来,或者至少太难爬出来了今天
  • 我的意思是我必须为大约 40 个函数执行此操作,而不是函数中有很多变量。
【解决方案2】:

使其工作的方法(至少对于 gcc 版本 4.8.1 (Ubuntu/Linaro 4.8.1-10ubuntu9))是 使用大括号 { } 括起您的实际值 用于宏。

一个有用的例子:

#ifdef DEBUG
#define MYDEBUG(X) (X)
#else
#define MYDEBUG(X)
#endif

MYDEBUG({
  if (shit_happens) {
     cerr << "help!" << endl;
     ....
  }
});

【讨论】:

  • @xaxxon 抱歉,我没有收到您的问题。什么评论?我这里什么都没写。
【解决方案3】:

我认为您需要使用额外的括号来使您的表达式看起来像一个不会被预处理器分解的单个参数,即更像这样:

#define MY_MACRO( (expr) ) DOSOMETHING( (expr) ); DOANOTHERTHING( (expr) ); // etc...

int my_function() {
    MY_MACRO( 
        (int k = AFunction();
        k++;
        AnotherFunction( k );)
    ); 
}

虽然我还没有真正尝试过。

【讨论】:

  • 我也没有尝试过,但我认为逗号是唯一可以分解宏参数的东西。看起来很奇怪,你可以把分号放在那里而不做任何特殊的事情,它仍然是一个恰好包含分号的宏参数。
  • @Steve: 啊,好吧 - 有道理 - 过去我不得不为类似 printf 的宏添加额外的括号,但当然这些会包含逗号。
  • 这个答案不起作用:完成整个表达式。为了使其工作,您还需要将所有表达式放在 () 之间。 (我们检查了 GCC4.4)
  • 是逗号之间的表达式,将在 () 之间。
【解决方案4】:

在有多个参数的情况下,具有多行参数的宏也可以,它甚至允许在内部使用“逗号”,但我强烈反对使用“逗号”,因为如果它对机器来说不是模棱两可的,那对人类来说肯定是模棱两可的:

#include <iostream>
using namespace std;

#define MACRO2FUN( X, Y) x; y;
void function(int a, int b, int c){
    std::cout<<a<<" "<<b<<" "<<c<<std::endl;
}

int main() {
    MACRO2FUN(
      function(3,4,5), 
      function(6,7,8)
      )
      return 0;
}

【讨论】:

  • 为什么引号中有“逗号”?您不是指逗号的正常定义吗?
  • Borg 宏和 cpp 使用逗号和参数分隔符,有时不太清楚编译器会选择什么
  • Borg 宏和 cpp 使用逗号和参数分隔符,有时不太清楚编译器会选择什么
  • 什么是“广告参数”?您还发表了两次评论。
  • 抱歉,从手机发布。 "宏和 cpp 都使用逗号作为参数分隔符"
【解决方案5】:

在 C++ 中,您应该使用仿函数! ;)

struct ninja
{
  void operator()() const
  {
    int k = AFunction();
    k++;
    AnotherFunction( k );    
  }
};

template <typename Functor>
void do_something(Functor const& f)
{
  f();
}

template <typename Functor>
void do_otherthing(Functor const& f)
{
  f();
}

int my_function()
{
  ninja foo;
  do_something(foo);
  do_otherthing(foo);
}

【讨论】:

  • 或者在 C++0x 中,没有名字:[](){ int k = AFunction(); ++k, AnotherFunction( k ); }
  • 是的,我们考虑过这一点,但问题是我们需要保留调用的上下文。但是 lambda 可以解决这个问题,但我们目前不能使用提供 lambda 功能的编译器 TT__TT 我正在更新答案,无论如何感谢确认我的想法。
  • 对C++0x这个东西不太了解... :(
  • @Klaim,你说的 context 是什么意思?
  • @Nim:看例子,代码需要访问调用函数中的变量。 C++0x 是定义 lambda 特性的 C++ 新标准 (C++2011) 的昵称。
【解决方案6】:

我看到的主要问题是expr 根本不是一个表达式。它甚至包含一个声明。显然,my_function 中定义的两个变量 k 会有问题。

如果您可以使用 C++0x(例如 VS2010、GCC4.6),那么您可以使用 lambda 来捕获上下文。并不是说您需要为这样一个简单的案例捕获上下文,也不需要模板,您的宏只需要一个std::function&lt;void(void)&gt;

【讨论】:

  • 太糟糕了,我们暂时只能使用 GCC4.4 :(
  • @Klaim 由于问题中不存在限制,抱怨答案很俗气。此评论也应删除。
猜你喜欢
  • 1970-01-01
  • 2018-10-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-17
  • 1970-01-01
  • 2021-05-07
  • 2018-05-16
相关资源
最近更新 更多