原始问题
我正在编写一个日志记录类,其目标是能够执行此操作:
// thread one
Logger() << "Some string" << std::ios::hex << 45;
// thread two
Logger() << L"Some wide string" << std::endl;
目前我的Logger标题看起来像这样:
#pragma once;
#include <ostream>
class Logger
{
public:
Logger();
~Logger();
std::ostream* out_stream;
};
template <typename T>
Logger& operator<< (Logger& logger, T thing) {
*logger.out_stream << thing;
return logger;
}
关于这堂课的一些注意事项:
我有三个问题:
在答案中学到的事情摘要:
最终结果
#pragma once
#include <ostream>
#include <string>
std::string ConvertWstringToString(std::wstring wstr);
class Logger
{
public:
Logger();
~Logger();
template <typename T>
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;
};
答案 0 :(得分:7)
您可以使用友元定义,它将在类的周围命名空间中定义运算符,并使其仅对运算符重载分辨率可见(不能使用::运算符&lt;&lt; ...语法手动调用):< / p>
class Logger
{
public:
Logger();
~Logger();
std::ostream* out_stream;
template <typename T>
friend 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 */
}
};
替代方案,保持代码不变,只需使运算符&lt;&lt;模板朋友,你将这一行添加到你的类定义:
template <typename T>
friend Logger& operator<< (Logger& logger, T thing);
对于操纵器问题,我会在一段时间之前给你我的代码:
#include <iostream>
#include <cstdlib>
using namespace std;
template<typename Char, typename Traits = char_traits<Char> >
struct logger{
typedef std::basic_ostream<Char, Traits> ostream_type;
typedef ostream_type& (*manip_type)(ostream_type&);
logger(ostream_type& os):os(os){}
logger &operator<<(manip_type pfn) {
if(pfn == static_cast<manip_type>(std::endl)) {
time_t t = time(0);
os << " --- " << ctime(&t) << pfn;
} else
os << pfn;
return *this;
}
template<typename T>
logger &operator<<(T const& t) {
os << t;
return *this;
}
private:
ostream_type & os;
};
namespace { logger<char> clogged(cout); }
int main() { clogged << "something with log functionality" << std::endl; }
};
请注意,它是std :: hex,但不是std :: ios :: hex 。后者用作流的setf
函数的操纵符标志。请注意,对于您的示例,不需要对操纵器进行特殊处理。上面对std :: endl的特殊处理只是需要因为我在使用std :: endl时会另外流时间。
答案 1 :(得分:2)
使用模板是正确的方法,但您只需确保模板位于标题文件(logger.h
或您调用的任何内容)中,不在实现文件(logger.cpp
)中。这将自动适用于operator <<
定义std::ostream
的任何类型。它还会自动使用流操纵器对象 - 这些实际上只是带有std::ostream
参数的函数,operator <<
只调用ostream
上的函数。
您可以按如下方式设置operator <<
朋友的功能:
template <typename T> friend Logger& operator<< (Logger& logger, T thing);
专业化很简单 - 只需使用模板专业化(再次在头文件中):
template <typename T>
Logger& operator<< (Logger& logger, T thing) {
*logger.out_stream << thing;
return logger;
}
// Template specialization - the "template <>" part is necessary
template <>
Logger& operator<< (Logger& logger, const wchar_t *wstr)
{
// convert wstr to an ANSI string and log it
}
template <>
Logger& operator<< (Logger& logger, const std::wstring & wstr)
{
// convert wstr to an ANSI string and log it
}
答案 2 :(得分:2)
不需要友情宣言:
class Logger
{
public:
Logger();
~Logger();
template <typename T>
inline Logger& Display(T thing)
{
*out_stream << thing;
return *this;
}
private:
std::ostream* out_stream;
};
template <typename T>
Logger& operator<< (Logger& logger, T thing)
{
return logger.Display(thing);
}
答案 3 :(得分:0)
为什么不使用printf方式并使用多参数方法(使用三个点......)。这仍然为你提供了很多形成能力,并且当你使用&lt;&lt;。
时,它并不会让它变得混乱。例如:
Logger("This is my log msg %0X", 45);
挂起两秒钟,然后为你提取代码示例。
编辑:
void Logger(const char* format, ...)
{
char szMsg[3000];
va_list args;
va_start( args, format );
vsnprintf( szMsg, sizeof(szMsg) - 1, format, args );
va_end(args);
// code to print szMsg to a file or whatever here
}
如果你想将它作为一个类而不是一个独立的函数使用,你可以重载logger operator()并且它可以正常工作