my_macro << 1 << "hello world" << blah->getValue() << std::endl;
应该扩展为:
std::ostringstream oss;
oss << 1 << "hello world" << blah->getValue() << std::endl;
ThreadSafeLogging(oss.str());
my_macro << 1 << "hello world" << blah->getValue() << std::endl;
应该扩展为:
std::ostringstream oss;
oss << 1 << "hello world" << blah->getValue() << std::endl;
ThreadSafeLogging(oss.str());
#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
上进行的一样。my_stream
析构函数通过之前“收集”的数据调用 ThreadSafeLogging
。operator<<
。可惜赞不能分享。my_macro << std::string( "surprise " )
)在C++中,流不应被用作临时变量。创建<<
和>>
运算符的成本并不高。 - NikkomyCOutObject << 1 << "hello world" << blah->getValue() << std::endl;
你是否想不使用宏定义,正确使用C++来获得完全相同的功能?
#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;
}
开发人员可能不需要在同一行上两次使用此宏,因为运算符<<
的简单性。但是,如果您需要这样做,可以通过__COUNTER__
(非标准)切换使用__LINE__
。感谢Quuxplusone提供了这个提示。
MyObject()
作为临时变量,并依赖于在语句结束时它被销毁。 - dascandy__COUNTER__
而不是标准的__LINE__
,然后即使在同一行上多次使用它也可以工作。但是,在任何情况下,这个宏都不是卫生的;如果您说if (log) my_macro << 4 << std::endl;
,那么您会遇到麻烦。基于google-glog的LOG()析构函数技巧的最高投票答案是卫生的。 - Quuxplusonefor
技巧足以使您的宏具有卫生性,但这相当微妙,我不确定。(啊,我看到Nicolas在下面提供了一个更简洁的例子。)简化当前宏的一种方法是将重复的子表达式Var(s, __LINE__)
分解为一个变量:#define my_macro my_macro_helper(Var(s, __LINE__))
,然后#define my_macro_helper(v) for...
。 - Quuxplusone不行。问题在于,如果没有使用函数语法,宏只能在其所在位置被替换。
但是,如果您愿意使用函数语法,那么您就可以在参数之前和之后替换内容。
my_macro(1 << "hello world" << blah->getValue() << std::endl);
MyMacro定义如下:
#define my_macro(args) std::ostreamstring oss; \
oss << args; \
ThreadSafeLogging(oss.str());
我有的日志设置非常相似:
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() 函数则将其作为单个命令刷新到日志输出并添加一个换行符。在我的实现中,它总是这样做,但如果已经有条件,则可以使其成为有条件。
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;
}