原始问题
我正在编写一个日志类,其目标是能够执行此操作:
// thread one Logger() << "Some string" << std::ios::hex << 45; // thread two Logger() << L"Some wide string" << std::endl;
目前我的Logger标题看起来像这样:
#pragma once; #includeclass Logger { public: Logger(); ~Logger(); std::ostream* out_stream; }; template Logger& operator<< (Logger& logger, T thing) { *logger.out_stream << thing; return logger; }
关于这门课的一些注意事项
跨平台兼容性不是问题.
在Logger.cpp里面有一个单例类,负责创建"真正的"ostream.
Logger构造函数和解构函数执行单例的必要锁定.
我有三个问题:
如何使operator << function成为朋友或成员,以便将out_stream设置为私有?
如何使操作符<<函数适用于操纵器?
我如何添加一个特化,以便如果T是WCHAR*或std :: wstring,它会在将它传递给out_stream之前将其转换为char*或std :: string?(我可以进行转换.在我的情况下,丢失高的unicode字符不是问题.)
在答案中学到的东西总结:
把模板放在朋友之前而不是之后.
std :: ios :: hex不是操纵者.std :: hex是一个操纵器.
最终结果
#pragma once #include#include std::string ConvertWstringToString(std::wstring wstr); class Logger { public: Logger(); ~Logger(); template Logger& operator<< (T data) { *out << data; return *this; } Logger& operator<< (std::wstring data) { return *this << ConvertWstringToString(data); } Logger& operator<< (const wchar_t* data) { std::wstring str(data); return *this << str; } private: std::ostream* out; };
Johannes Sch.. 7
您可以使用友元定义,它将在类的周围命名空间中定义运算符,并使其仅对运算符重载分辨率可见(不能使用:: operator << ...语法手动调用):
class Logger { public: Logger(); ~Logger(); std::ostream* out_stream; templatefriend Logger& operator<< (Logger& logger, T thing) { *logger.out_stream << thing; return logger; } /* special treatment for std::wstring. just overload the operator! No need * to specialize it. */ friend Logger& operator<< (Logger& logger, const std::wstring & wstr) { /* do something here */ } };
另一种方法是,保持代码不变,只需将操作符<<模板设为好友,就可以将此行添加到类定义中:
templatefriend Logger& operator<< (Logger& logger, T thing);
对于操纵器问题,我会给你我前一段时间写的代码:
#include#include using namespace std; template > struct logger{ typedef std::basic_ostream ostream_type; typedef ostream_type& (*manip_type)(ostream_type&); logger(ostream_type& os):os(os){} logger &operator<<(manip_type pfn) { if(pfn == static_cast (std::endl)) { time_t t = time(0); os << " --- " << ctime(&t) << pfn; } else os << pfn; return *this; } template logger &operator<<(T const& t) { os << t; return *this; } private: ostream_type & os; }; namespace { logger clogged(cout); } int main() { clogged << "something with log functionality" << std::endl; }
};
请注意,它是std :: hex,但不是std :: ios :: hex.后者用作setf
流的功能的操纵器标志.请注意,对于您的示例,不需要对操纵器进行特殊处理.上面对std :: endl的特殊处理只是需要因为我在使用std :: endl时会另外流式传输时间.
您可以使用友元定义,它将在类的周围命名空间中定义运算符,并使其仅对运算符重载分辨率可见(不能使用:: operator << ...语法手动调用):
class Logger { public: Logger(); ~Logger(); std::ostream* out_stream; templatefriend Logger& operator<< (Logger& logger, T thing) { *logger.out_stream << thing; return logger; } /* special treatment for std::wstring. just overload the operator! No need * to specialize it. */ friend Logger& operator<< (Logger& logger, const std::wstring & wstr) { /* do something here */ } };
另一种方法是,保持代码不变,只需将操作符<<模板设为好友,就可以将此行添加到类定义中:
templatefriend Logger& operator<< (Logger& logger, T thing);
对于操纵器问题,我会给你我前一段时间写的代码:
#include#include using namespace std; template > struct logger{ typedef std::basic_ostream ostream_type; typedef ostream_type& (*manip_type)(ostream_type&); logger(ostream_type& os):os(os){} logger &operator<<(manip_type pfn) { if(pfn == static_cast (std::endl)) { time_t t = time(0); os << " --- " << ctime(&t) << pfn; } else os << pfn; return *this; } template logger &operator<<(T const& t) { os << t; return *this; } private: ostream_type & os; }; namespace { logger clogged(cout); } int main() { clogged << "something with log functionality" << std::endl; }
};
请注意,它是std :: hex,但不是std :: ios :: hex.后者用作setf
流的功能的操纵器标志.请注意,对于您的示例,不需要对操纵器进行特殊处理.上面对std :: endl的特殊处理只是需要因为我在使用std :: endl时会另外流式传输时间.