【问题标题】:c++ #define a macro with brackets?c ++ #define一个带括号的宏?
【发布时间】:2010-10-20 01:31:26
【问题描述】:

而不是每次都执行以下操作

start();

// some code here

stop();

我想定义某种宏,它可以写成这样:

startstop()
{

//code here

}

在 C++ 中可以吗?

【问题讨论】:

    标签: c++ macros


    【解决方案1】:

    在 C++ 中执行此操作的惯用方式称为 Resource Acquisition Is Initialization,或简称为 RAII。除了提供你想要的东西之外,它还有一个额外的好处是异常安全:即使你的代码抛出异常,stop 函数也会被调用。

    定义一个保护结构:

    struct startstop_guard
    {
        startstop_guard()
        {
            start();
        }
        ~startstop_guard()
        {
            stop();
        }
    };
    

    然后用这种方式重写你的代码:

    {
        startstop_guard g;
        // your code
    }
    

    守卫的析构函数(以及 stop 函数)将在封闭块的末尾自动调用。

    【讨论】:

      【解决方案2】:

      你想做什么?我建议将RAII 视为一种比宏黑客更面向 C++ 的做事方式,它会带来所有无法预料的后果。

      【讨论】:

        【解决方案3】:

        您可以使用一个小的 C++ 帮助类来做一些非常接近的事情。

        class StartStopper {
        public:
            StartStopper() { start(); }
            ~StartStopper() { stop(); }
        };
        

        然后在你的代码中:

        {
            StartStopper ss;
            // code here
        }
        

        当执行进入块并构造ss变量时,start()函数将被调用。当执行离开块时,会自动调用StartStopper析构函数,然后调用stop()

        【讨论】:

        • +1。这种方法是坚如磐石的。即使抛出异常,stop() 仍然会被调用,因为 C++ 保证在范围退出时总是调用析构函数。
        • 它还避免了宏可能出现的难以调试的问题。
        • +1。 Dirkgently 回应中的对话很有趣,但这是最好的解决方案,因为它不会创建“秘密宏语言”。
        • 顺便说一句 - 使用 MSVC++ 编译器我已经看到它在作用域完成之前调用了析构函数。它以某种方式检测到该变量将不再被使用并立即调用析构函数,尽管范围内还有更多的东西。
        • @Vilx:很有趣。我检查了标准,如果析构函数有副作用(在 3.7.2.3 的范围退出之前允许无副作用的破坏),那肯定是不符合标准的行为。
        【解决方案4】:
        #define startstop(x, y, ...) for( /* use macro args */ )
        

        【讨论】:

        • +1,即使没有那么多人会理解那里的依赖注入......
        • @Iraimbilanja:什么不清楚,什么不完整?该问题未指定有关 start() 或 stop() 的任何内容。
        • 该问题指定 start() 和 stop() 的签名,并请求一个宏,该宏使“startstop { ... }”语法在左大括号和 stop() 处调用 start()在右大括号处。您的回答没有提供一个有效的实现。不清楚 /*use macro args*/ 注释应该替换为什么,也不清楚宏参数 x 和 y 如何与问题相关。 FWIW 可以使用 'for' 来实现工作,但你还没有做到。
        • 您应该提到“...”语法是 c99 并且还不是 C++ 的一部分(将与 c++1x 一起使用)
        • @litb:我不打算使用可变参数。 @Iraimbilanja:如果不了解 start() 和 stop() 到底是什么,我认为这是不可能的。
        【解决方案5】:

        不要使用宏。您可以改用内联函数,因为它提供类型检查和其他功能。你可以看这里:inline functions

        【讨论】:

          【解决方案6】:

          具有 RAII 和 boost::function ( std::function ) 的通用解决方案。

          class starter
          {
              typedef boost::function< void () > action;
              action end_;
          public:
              starter(action start, action end):
                  end_(end)
              { 
                  log("starter start");
                  start(); 
              }
              ~starter()
              { 
                  log("starter end");
                  end_() ; 
              }
          };
          int main()
          {
              {
                  starter s(start, stop);
                  middle();
              }
          
              return 0;
          }
          

          或测试和检查这个想法

              void print(const std::string& message)
              {
                  std::cout << message << std::endl;
              }
              int main()
              {
                  starter s(boost::bind(print, "globalstart"),
                            boost::bind(print, "globalend")); 
          
                  {
                      starter s(boost::bind(print, "start"),
                                boost::bind(print, "end")); 
                      std::cout << "middle" << std::endl;
                  }
                  return 0;
              }
          

          【讨论】:

          • +1 使用 boost::function 很好地实现了作用域守卫,可以结合依赖注入到宏中以获得更自然的用法。
          【解决方案7】:

          其他答案已经很好地解决了问题的 RAII 方面,所以我将解决它的语法方面

          #define startstop for(Starter s; s.loop; s.loop = false)
          struct Starter {
              bool loop;
              Starter() { start(); loop = true; }
              ~Starter() { stop(); }
          };
          

          类似

          startstop {
              // some code
          }
          

          应该足够不言自明。

          【讨论】:

          • 确实,我也想写那件事。你也可以做 #define startstop() if(...) ... ,这样的好处是不接触 int startstop = 1; 之类的东西等等。即类似函数的宏需要括号来进行替换,这会更安全(并且也直接符合他的意愿,并且还允许将参数推送到 Starter 的 ctor)。但你当然仍然会得到 +1 :)
          • 另一件事:从 c++03 开始​​,编译器可以获取 Starter() 的副本并将引用绑定到该副本。这意味着,您将创建两个对象(一个显式创建,一个由编译器创建,用于绑定对它的引用)。类似于 alexandrescu 的 scope_guard 的实现,您需要引入一个可变的布尔变量,由 cctor 设置为 false (rhs.bool_var),在默认 ctor 中设置为 true,并由 cctor 设置为 this->bool_var。仅当它为 true 时才调用 end())
          • 类似(颠倒了_guard的意思,因为我认为它更有意义:)) struct Starter { mutable bool垃圾;启动器():垃圾(){开始(); } Starter(Starter const& rhs):trash(rhs.trash) { rhs.trash = true; } ~Starter() { if(!trash) end(); } operator bool() const { return false; } }。现在,end 只被调用一次,用于未复制的那个(绑定到引用的那个)。
          • 谁 C++ 允许在这里复制?奇怪,为什么会这样:) 感谢您的改进。现在代码已经足够复杂了,我不得不写一个“解释”部分:)
          • 详情请咨询:comeaucomputing.com/iso/cwg_defects.html#391 :) 这很奇怪。我很高兴它会随着 c++1x 改变 :)
          【解决方案8】:

          在 c# 中,您可以使用 IDisposable 模式,并在 Dispose() 方法中实现您的 Stop() 功能,但如果您使用的是 c++ 的 .net 变体,这将起作用。

          【讨论】:

          • 嗯,除了离题之外,C++/CLI 甚至没有 C# 的 using() 构造。
          【解决方案9】:

          归功于这个想法。我想我会填写其余的

          #define startstop() for(start();isStarted();stop())

          【讨论】:

            猜你喜欢
            • 2021-07-22
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2019-04-19
            相关资源
            最近更新 更多