【问题标题】:Is there anyway to write the following as a C++ macro?无论如何将以下内容编写为 C++ 宏?
【发布时间】:2011-01-12 20:39:58
【问题描述】:
my_macro << 1 << "hello world" << blah->getValue() << std::endl;

应该扩展为:

std::ostringstream oss;
oss << 1 << "hello world" << blah->getValue() << std::endl;
ThreadSafeLogging(oss.str());

【问题讨论】:

标签: c++ logging thread-safety ostringstream


【解决方案1】:
#define my_macro my_stream()
class my_stream: public std::ostringstream  {
public:
    my_stream() {}
    ~my_stream() {
        ThreadSafeLogging(this->str());
    }
};
int main() {
    my_macro << 1 << "hello world" << std::endl;
}

创建了一个my_stream 类型的临时对象,它是ostringstream 的子类。对该临时工作的所有操作都像在ostringstream 上一样工作。

当语句结束时(即在 main() 中整个打印操作的分号之后),临时对象超出范围并被销毁。 my_stream 析构函数调用 ThreadSafeLogging 之前“收集”的数据。

经过测试 (g++)。

感谢/感谢dingo 指出如何简化整个事情,所以我不需要重载的operator&lt;&lt;。可惜点赞无法分享。

【讨论】:

  • 这是我一生中见过的最出色的 c++ 析构函数使用(或滥用)。
  • 查看此问题以了解换行问题。 stackoverflow.com/questions/1134388/…
  • 只使用 std::ostringstream 作为 my_stream 的基类不是更简单吗?这样看来,析构函数是唯一需要的方法。
  • 我会摆脱宏并直接使用 my_stream() ,但除此之外,这太棒了。 +1
  • 你怎么能有这么多票支持一个真正糟糕的 C++ 并且不起作用并且可能导致问题的技巧。 (尝试my_macro &lt;&lt; std::string( "surprise " ))C++ 中的流不应该用作临时对象。创建 > 运算符的成本并不高
【解决方案2】:

难道您不能直接从 ostream 派生并提供您自己的线程安全实现吗?那你就可以了

myCOutObject << 1 << "hello world" << blah->getValue() << std::endl;

并在没有宏的情况下获得完全相同的功能并正确使用 C++?

【讨论】:

    【解决方案3】:

    没有。问题是,如果不使用函数语法,宏就只能在它所在的地方被替换。

    但是,如果您愿意使用函数语法,则可以替换 args 前后的内容。

    my_macro(1 << "hello world" << blah->getValue() << std::endl);
    

    您可以将 MyMacro 定义为:

    #define my_macro(args) std::ostreamstring oss; \
                           oss << args; \
                           ThreadSafeLogging(oss.str());
    

    【讨论】:

      【解决方案4】:

      看看google-glog,他们使用一个用

      实例化的临时对象来做​​到这一点
      LOG(INFO) << "log whatever" << 1;
      

      它们还有其他有趣的宏,例如 LOG_IF 等。

      【讨论】:

        【解决方案5】:

        考虑到您的代码中包含这些行,是的,这是可能的

        #include <iostream>
        #include <sstream> 
        

        __LINE__ 宏由所有标准编译器定义。 所以我们可以用它来生成一个变量名,每次你使用宏时都是不同的:)

        这是一个仅被视为单语句指令的新版本: (已编辑)

        #define Var_(Name, Index) Name##Index
        #define Var(Name, Index) Var_(Name, Index)
        #define my_macro \
        for (struct { int x; std::ostringstream oss; } Var(s, __LINE__) = { 0 }; \
             Var(s, __LINE__).x<2; ++Var(s, __LINE__).x)  \
            if (Var(s, __LINE__).x==1) ThreadSafeLogging(Var(s, __LINE__).oss.str()); \
            else Var(s, __LINE__).oss
        
        // So you can use it like this 
        int main() 
        { 
            if (4 != 2)
                my_macro << 4 << " hello "  << std::endl; 
            my_macro << 2 << " world !" << std::endl; 
        } 
        

        由于运算符&lt;&lt; 的简单性,开发人员可能不需要在同一行上使用此宏两次。但是如果你需要这个,你可以通过__COUNTER__ 切换__LINE__ 的使用(这是非标准的!)。感谢 Quuxplusone 的提示

        【讨论】:

        • 为什么要给它起个名字?您可以将对象 MyObject() 用作临时对象并依赖它在语句末尾被破坏。
        • 聪明!请注意,在所有流行的编译器上,您都可以使用非标准的__COUNTER__ 而不是标准的__LINE__,然后它甚至可以在同一行上工作多次。但是,无论哪种情况,这个宏都不卫生;如果您说if (log) my_macro &lt;&lt; 4 &lt;&lt; std::endl;,那么您将度过一段糟糕的时光。根据 google-glog 的 LOG() 析构函数技巧,投票最多的答案是卫生的。
        • @Quuxplusone:你说得对,你问的确实是写宏时的“标准”。所以我更新了解决方案,
        • 认为 for 技巧足以使您的宏观卫生,但它非常微妙,我不确定。 (啊,我看到 Nicolas 在下面有一个不那么混乱的例子。)整理当前宏的一种方法是将重复的子表达式 Var(s, __LINE__) 分解为一个变量:#define my_macro my_macro_helper(Var(s, __LINE__)),然后是 #define my_macro_helper(v) for...
        【解决方案6】:

        这是我在其他地方看到的另一个讨厌的把戏。与我的其他答案相比,它有一个明显的缺点:您不能在同一范围内使用它两次,因为它声明了一个变量。但是,对于其他您希望somemacro foofoo 之后运行某些东西 的情况可能仍然很有趣。

        #define my_macro \
            std::ostringstream oss; \
            for (int x=0; x<2; ++x) \
                if (x==1) ThreadSafeLogging(oss.str()); \
                else oss
        
        int main() {
            my_macro << 1 << "hello world" << std::endl;
        }
        

        【讨论】:

          【解决方案7】:

          我的日志记录设置非常相似:

          bool ShouldLog(const char* file, size_t line, Priority prio);
          
          class LoggerOutput : public std::stringstream {
          public:
            LoggerOutput(const char* file, size_t line, Priority prio) 
            : prio(prio) 
            {
              Prefix(file, line, prio);
            }
            void Prefix(const char* file, size_t line, Priority prio);
            ~LoggerOutput() {
              Flush();
            }
            void Flush();
          private:
            Priority prio;
          };
          
          #define LOG(Prio) if (!Logging::ShouldLog(__FILE__, __LINE__, Prio)) {} else Logging::LoggerOutput(__FILE__, __LINE__, Prio)
          

          如果您的日志记录被禁用,则永远不会创建 ostream,并且几乎没有开销。您可以配置文件名和行号或优先级的日志记录。 ShouldLog 函数可以在调用之间更改,因此您可以限制或限制输出。日志输出使用两个函数来修改自身,Prefix 将“file:line: (PRIO)”前缀添加到该行,Flush() 将其作为单个命令刷新到日志输出并添加换行符.在我的实现中它总是这样,但是如果还没有的话,你可以设置条件。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2022-06-14
            • 2011-08-08
            • 2010-11-24
            • 1970-01-01
            相关资源
            最近更新 更多