反正有没有把以下内容写成C ++宏?

时间:2010-02-03 22:57:25

标签: c++ logging thread-safety ostringstream

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

应扩展为:

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

7 个答案:

答案 0 :(得分:72)

#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 ++)。

感谢{/ 3}}指出如何简化整个事情,所以我不需要重载operator<<。太糟糕的upvotes无法分享。

答案 1 :(得分:3)

难道你不能从ostream派生并提供自己的线程安全实现吗?然后你可以做

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

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

答案 2 :(得分:2)

没有。问题是如果不使用函数语法,宏只限于被替换的地方。

但是如果你愿意使用函数语法,那么你可以在args之前和之后替换东西。

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

您可以将MyMacro定义为:

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

答案 3 :(得分:2)

看看google-glog,他们使用与

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

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

答案 4 :(得分:2)

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

#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; 
} 

由于操作符<<的简单性,开发者可能不需要在同一行上使用此宏两次。但是如果你需要这个,你可以将__LINE__的使用改为__COUNTER__(这是非标准的!)。感谢Quuxplusone的提示

答案 5 :(得分:1)

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

#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;
}

答案 6 :(得分:1)

我的日志设置非常相似:

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()将它作为单个命令刷新到日志输出并为其添加换行符。在我的实现中它始终如此,但是如果还没有条件,你可以使条件有条件。