重载运算符<&书信电报;在C++;带前缀 我在C++中使用日志记录类,它有以下语法: Logger log("mylog.txt"); log << "a string" << " " << 1 << " " << 6.2 << "\n";

重载运算符<&书信电报;在C++;带前缀 我在C++中使用日志记录类,它有以下语法: Logger log("mylog.txt"); log << "a string" << " " << 1 << " " << 6.2 << "\n";,c++,operator-overloading,C++,Operator Overloading,我想到了几个解决方案: 1.将每个输入保存在列表/流中,并使用额外功能打印并清除列表/流: Logger log("mylog.txt"); log << "a string" << " " << 1 << " " << 6.2 << "\n"; log.logd(); // <- this prints and then clears the internal stream/list. Logger日志(“mylo

我想到了几个解决方案:

1.将每个输入保存在列表/流中,并使用额外功能打印并清除列表/流:

Logger log("mylog.txt");
log << "a string" << " " << 1 << " " << 6.2 << "\n";
log.logd(); // <- this prints and then clears the internal stream/list.
Logger日志(“mylog.txt”);

log您需要为Logger引入一个额外的包装器类,它知道行是开始还是追加

class Appender
{
    Appender(Logger& logger) : os_(os) { }
    Appender& operator <<(const T& x) { os_ << x; return *this; }
};

class Logger
{
    Appender operator <<(const T& x) { os_ << timestamp() << x; return Appender(os_); }
};
类追加器
{
Appender(记录器和记录器):os_z(os){}

Appender&operator实际代码将更加复杂,但请尝试实现以下逻辑

添加一个成员变量bool last char was newline,并在如下代码中使用它:

template<typename T>
Logger& operator<< (const T& data)
{
    if (last_char_was_newline) {
        *m_pOutStream << current_time_string();
        last_char_was_newline = false;
    }
    *m_pOutStream << data;
    if (last_char(data) == '\n') {
        last_char_was_newline = true;
    }
    return *this;
}
模板

Logger&operator从
std::stringbuf
派生一个类,比如说
LoggerStringBuf
,并在其构造函数中给它一个对输出
std::ofstream
的引用。重写虚拟
std::stringbuf::sync()
方法,从基
std::stringbuf::str()检索
std::string
方法,并在将其写入
std::ofstream
时为其加上时间戳前缀。这样,每当您的
LoggerStringBuf
对象出于任何原因刷新到
std::ofstream
时,无论是通过
std::endl
std::flush
显式刷新,还是通过其destru隐式刷新,都会生成一个新的时间戳克托尔

然后让您的
Logger
类从
std::ostream
派生,并用
LoggerStringBuf
对象初始化它。然后您可以将输入值流式传输到
Logger
中,它们将缓存在
LoggerStringBuf
对象中,直到刷新到
std::ofstream
。此时您可以预处理根据需要挂起时间戳

例如:

class LoggerStringBuf : public std::stringbuf
{
private:
    std::ostream &m_OutStream;

protected:
    virtual int sync()
    {
        int ret = std::stringbuf::sync();

        std::string s = str();
        str("");

        // note sure if the string includes non-flushing
        // line breaks.  If needed, you can use std::getline()
        // to break up the string into multiple lines and 
        // write a timestamp for each line...
        //
        m_OutStream << "[timestamp] " << s << std::endl;

        return ret;
    };

public:
    LoggerStringBuf(std::ostream &OutStream)
        : std::stringbuf(std::ios_base::out), m_OutStream(OutStream)
    {
    }

    ~LoggerStringBuf()
    {
        sync();
    }
};

class Logger : public std::ostream
{
private:
    std::ofstream m_OutStream;
    LoggerStringBuf m_Buf;

public: 
    Logger(const std::string &sFile)
        : std::ostream(0), m_OutStream(sFile, std::ios::app), m_Buf(m_OutStream)
    {
        init(&m_Buf);
    }

    template<typename T>
    std::ostream& operator<< (const T& data)
    {
        return static_cast<std::ostream&>(*this) << data;
    }
};
类LoggerStringBuf:public std::stringbuf { 私人: std::奥斯特雷姆和摩托扩流; 受保护的: 虚拟整数同步() { int ret=std::stringbuf::sync(); std::string s=str(); str(“”); //请注意,如果管柱包括非冲洗管柱,请确保 //换行符。如果需要,可以使用std::getline() //将字符串拆分为多行,然后 //为每行写一个时间戳。。。 //
m_超出了旁白,但重载的
运算符不应该使用标志来指示最后一个字符输出是否为换行符。当您被调用并设置标志时,首先输出前缀。该标志应默认为
true
,以便您也在输出的第一行之前打印前缀。根据我的经验,最好不要o将“行”概念与“日志条目”分开概念和时间戳项。@MOLBNILO对日志文件中的不同行具有不同的格式,这使得处理这些行的编写工具更加困难。@CONCONINTERIST我的建议不需要保存任何输出。它只需要一个变量,
bool last\u char\u was\u newline;
返回
Appender&Logger::operatorProxy类的ode>看起来是一个优雅的解决方案,但是请注意,OP希望在每一行上打印时间戳,因此调用
Logger会复杂得多…您的代码基本上是无用的。您需要在le处处理
std::string
char
std::endl
重载AST,提供了良好的类型演绎。我明白了,就像我说的,我试图展示一般逻辑,而不是确切的代码。我可以编写伪代码,但我决定用C++语法来显示它。是的,这个想法似乎不错,但是用一般的方式实现并不微不足道。我已经添加了一个关于如何用一般方式来做它的额外想法。使用
std::stringstram
。这两行代码在LoggerStringBuf::sync()的实现中的作用是什么?
std::string s=str();str(“”;
@dgellow on cppreference.com.
sync()调用
将缓存数据写入目标输出。该代码从基类检索缓存数据,然后重置基类中的缓存,因此,如果以后再次调用
sync()
,则不会记录相同的数据。
class Appender
{
    Appender(Logger& logger) : os_(os) { }
    Appender& operator <<(const T& x) { os_ << x; return *this; }
};

class Logger
{
    Appender operator <<(const T& x) { os_ << timestamp() << x; return Appender(os_); }
};
template<typename T>
Logger& operator<< (const T& data)
{
    if (last_char_was_newline) {
        *m_pOutStream << current_time_string();
        last_char_was_newline = false;
    }
    *m_pOutStream << data;
    if (last_char(data) == '\n') {
        last_char_was_newline = true;
    }
    return *this;
}
class LoggerStringBuf : public std::stringbuf
{
private:
    std::ostream &m_OutStream;

protected:
    virtual int sync()
    {
        int ret = std::stringbuf::sync();

        std::string s = str();
        str("");

        // note sure if the string includes non-flushing
        // line breaks.  If needed, you can use std::getline()
        // to break up the string into multiple lines and 
        // write a timestamp for each line...
        //
        m_OutStream << "[timestamp] " << s << std::endl;

        return ret;
    };

public:
    LoggerStringBuf(std::ostream &OutStream)
        : std::stringbuf(std::ios_base::out), m_OutStream(OutStream)
    {
    }

    ~LoggerStringBuf()
    {
        sync();
    }
};

class Logger : public std::ostream
{
private:
    std::ofstream m_OutStream;
    LoggerStringBuf m_Buf;

public: 
    Logger(const std::string &sFile)
        : std::ostream(0), m_OutStream(sFile, std::ios::app), m_Buf(m_OutStream)
    {
        init(&m_Buf);
    }

    template<typename T>
    std::ostream& operator<< (const T& data)
    {
        return static_cast<std::ostream&>(*this) << data;
    }
};