重载运算符<<在C ++中带有前缀

时间:2016-05-27 19:21:09

标签: c++ operator-overloading

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

v1.9.1

它打印: 字符串1 6.2

这就是我班上的样子:

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

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

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";

我想到了几个解决方案:

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

11:59:12 a string 1 6.2

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

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

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

3 个答案:

答案 0 :(得分:1)

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

答案 1 :(得分:1)

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

添加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)(以及对嵌入换行的输出的更一般扫描)是非平凡的。实现它的一般方法可能是将data写入std::stringstream。然后,您可以扫描此字符串以获取换行符,最后将该字符串输出到*m_pOutStream

答案 2 :(得分:1)

std::stringbuf派生一个类,比如LoggerStringBuf,并在其构造函数中为其输出std::ofstream提供一个引用。覆盖虚拟std::stringbuf::sync()方法以从基础std::string方法检索std::stringbuf::str(),并在将其写入std::ofstream时为其添加时间戳。这样,每当LoggerStringBuf对象因任何原因(std::ofstreamstd::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;
    }
};