繁体   English   中英

使用前缀在C ++中重载operator <<

[英]Overloading operator<< in C++ with a prefix

我正在使用C ++编写一个具有以下语法的logger类:

Logger log("mylog.txt");
log << "a string" << " " << 1 << " " << 6.2 << "\n";

它打印:字符串1 6.2

这就是我班上的样子:

class Logger 
{
private:
    unique_ptr<ofstream> m_pOutStream;

public: 
    Logger(std::string sFile) : m_pOutStream(new ofstream(sFile, std::ios::app))
    {}

    template<typename T>
    Logger& operator<< (const T& data)
    {
        *m_pOutStream << data;
        return *this;
    }
};

它工作正常,但我还想为每一行添加一个前缀(例如时间戳)。 所以当我写:

Logger log("mylog.txt");
log << "a string" << " " << 1 << " " << 6.2 << "\n";

我想要显示这样的东西:

11:59:12 a string 1 6.2

我想到了几个解决方案:

1.保存列表/流中存储的每个输入,并使用额外的功能进行打印,然后清除列表/流:

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

2.保存列表/流中存储的每个输入,并在检测到“新行”字符后打印所有内容。 然后清除内部流/列表。

这两种解决方案都很不错,但我更喜欢将它们作为最后的手段使用。

有没有其他/更好的方法来实现我想要的?

您需要为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_); }
};

实际代码会更复杂,但请尝试实现以下逻辑。

添加一个member_variable 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;
}

更一般地说,您应该扫描嵌入换行的data ,并将时间也放在每个换行之后。

上面的伪代码掩盖了棘手的部分。 由于data可以是任何类型,因此last_char(data) (以及对嵌入式换行的输出的更一般扫描last_char(data)last_char(data) 实现它的一般方法可能是将data写入std::stringstream 然后,您可以扫描此字符串以获取换行符,最后将该字符串输出到*m_pOutStream

std::stringbuf LoggerStringBuf派生一个类,比如说LoggerStringBuf ,并在其构造函数中为它输出std::ofstream 覆盖虚拟std::stringbuf::sync()方法以从基本std::stringbuf::str()方法中检索std::string ,并在将其写入std::ofstream时为其添加时间戳。 这样,每次LoggerStringBuf对象因任何原因刷新到std::ofstream时都会生成一个新的时间戳,无论是由std::endl还是std::flush显式,还是由析构函数隐式地生成。

然后让您的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;
    }
};

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM