我想创建一个灵活的记录器类。我希望它能够将数据输出到文件或标准输出。另外,我想使用流。该课程应该类似于:
class Logger
{
private:
std::ostream m_out; // or ofstream, iostream? i don't know
public:
void useFile( std::string fname);
void useStdOut();
void log( symbol_id si, int val );
void log( symbol_id si, std::string str );
//etc..
};
symbol_id
是枚举并定义格式。我想要实现的是能够轻松地从标准输出切换到文件,反之亦然(这是use*
方法的目的)。最好只使用m_out
并简单地编写m_out << "something";
而无需检查我是否要写入文件或标准输出。
我知道有很多方法可以解决这个问题(使用if's
测试我是否要写入文件或标准输出,“C way”(使用{{ 1}}和FILE*
))等等,但我确信必须有一种方法可以用很好的方式实现C ++流。但我似乎无法找到如何做到这一点。有人可以帮帮我吗?
答案 0 :(得分:9)
C ++中的std::o*stream
类继承自std :: ostream。这意味着你应该根据std :: ofstream指针或引用来编写你的接口:
class Logger
{
std::ostream *m_out; // use pointer so you can change it at any point
bool m_owner;
public:
// constructor is trivial (and ommited)
virtual ~Logger()
{
setStream(0, false);
}
void setStream( std::ostream* stream, bool owner )
{
if(m_owner)
delete m_out;
m_out = stream;
m_owner = owner;
}
template<typename T>
Logger& operator << (const T& object)
{
if(!m_out)
throw std::runtime_error("No stream set for Logger class");
(*m_out) << object;
return *this;
}
};
// usage:
Logger logger;
logger.setStream( &std::cout, false ); // do not delete std::cout when finished
logger << "This will be logged to std::cout" << std::endl;
// ...
logger.setStream(
new std::ofstream("myfile.log", std::ios_base::ate|std::ios_base::app),
true ); // delete the file stream when Logger goes out of scope
logger << "This will be appended to myfile.log" << std::endl;
答案 1 :(得分:8)
我以前攻击此问题的方法是使Logger
成为一个抽象基类,并创建单独的FileLogger
和OutStreamLogger
类。然后创建一个实现CompositeLogger
接口的Logger
对象,它只输出所有记录器:
CompositeLogger compLogger;
compLogger.Add(new FileLogger("output.txt"));
compLogger.Add(new StreamLogger(std::cout));
...
compLogger.log(...);
如果您不需要这种级别的灵活性并希望将所有这些保留在一个类中,则可以使m_Out变量成为std::ostream
的指针并添加一个额外的标志以跟踪您是否需要在清理时删除它:
private:
std::ostream* m_out;
bool m_OwnsStream;
Logger() {
m_Out=&std::cout; // defaults to stdout
m_OwnsStream=false;
}
void useFile(std::string filename) {
m_Out=new std::ofstream(filename);
m_OwnsStream=true;
}
~Logger() {
if (m_OwnStream)
delete m_Out;
}
显然,您需要在useFile
和useStdOut
中进行更多检查以防止内存泄漏。
答案 2 :(得分:4)
博士。多布斯发表了一篇我用作伐木灵感的文章。值得一读。 http://www.drdobbs.com/cpp/201804215
看起来最近另一篇文章也已发表,但我还没看过。 http://www.drdobbs.com/cpp/225700666
答案 3 :(得分:2)
the_mandrill解决方案的变体,因为我认为策略模式在概念上更适合这个问题。
我们可以随时通过调用context-&gt; SetLogger来更改日志策略
我们还可以为文件记录器使用不同的文件。
class Logger
{
protected:
ostream* m_os;
public:
void Log(const string& _s)
{
(*m_os) << _s;
m_os->flush();
}
};
class FileLogger : public Logger
{
string m_filename;
public:
explicit FileLogger(const string& _s)
: m_filename(_s)
{
m_os = new ofstream(m_filename.c_str());
}
~FileLogger()
{
if (m_os)
{
ofstream* of = static_cast<ofstream*>(m_os);
of->close();
delete m_os;
}
}
};
class StdOutLogger : public Logger
{
public:
StdOutLogger()
{
m_os = &std::cout;
}
};
class Context
{
Logger* m_logger;
public:
explicit Context(Logger* _l) {m_logger = _l;}
void SetLogger(Logger* _l) {m_logger = _l;}
void Log(const string& _s)
{
if (m_logger)
{
m_logger->Log(_s);
}
}
};
int main()
{
string filename("log.txt");
Logger* fileLogger = new FileLogger(filename);
Logger* stdOutLogger = new StdOutLogger();
Context* context = new Context(fileLogger);
context->Log("this log out to file\n");
context->SetLogger(stdOutLogger);
context->Log("this log out to standard output\n");
}