C ++ Stream:在换行符后插入字符串

时间:2014-02-26 13:08:52

标签: c++ stream

原始问题是:如何在换行后自动将某些内容插入到流中。只有在事后将某些内容(手动)插入到流中时才会发生插入。

下面是更详细的解释。


对于练习我正在编写自己的记录器类。此片段给出了基本的日志记录功能。

class Logger {
public:
    static std::ostream & log( LogLevels::LogLevel level );
};

// Use it:
Logger::log( LogLevels::WARNING ) << "Something serious happened!" << std::endl;

该示例打印类似

的内容
[ WARNING: 0 ] Something serious happened!

我想扩展此功能,以便在将新行插入流后,所有调试消息都会按日志标头的宽度缩进,直到再次调用Logger::log为止。最好通过示例来解释:

// Example (1)
Logger::log( LogLevels::WARNING ) << "Something happened!" 
                                  << std::endl 
                                  << "Some More information: " 
                                  << 42 
                                  << " (Still on the same line)"
                                  << std::endl;

Logger::log( LogLevels::INFO ) << "Merely a status code"
                               << std::endl
                               << "Which reads: " 
                               << 21 
                               << " (Also on the same line)"
                               << std::endl;

// Example (2)
std::ostream & os = Logger::log( LogLevels::WARNING );
os << "First line"
   << std::endl;

os << "Second line"
   << std::endl;

// Example (3)
// [...]
// Some code is executed
Logger::log( LogLevels::WARNING ) << "A new error" 
                                  << std::endl 
                                  << "The code strikes back"
                                  << std::endl 
                                  << "The return of the bugs"
                                  << std::endl ;

哪会产生:

[ WARNING: 0 ] Something hapened!  
               Some More information: 42 (Still on the same line)
[ INFO: 1 ] Merely a status code
            Which reads: 21 (Also on the same line)
[ WARNING: 2 ] First line
               Second line
// [...]
[ WARNING: 99998 ] A new error
                   The code strikes back
                   The return of the bugs

是否可以实现此行为?如果是,如何实现?

3 个答案:

答案 0 :(得分:2)

只需在ostream和ostream之间插入过滤streambuf 最终目标streambuf。像下面这样的东西应该做 诀窍:

class HeaderInserter : public std::streambuf
{
    std::streambuf* myDest;
    bool myIsAtStartOfLine;
protected:
    int overflow( int ch ) override
    {
        int retval = 0;
        if ( ch != traits_type::eof() ) {
            if ( myIsAtStartOfLine ) {
                std::string header = getHeader();
                myDest->sputn( header.data(), header.size() );
            }
            retval = myDest->sputc( ch );
            myIsAtStartOfLine = ch == '\n';
        }
        return retval;
    }
public:
    HeaderInserter( std::streambuf* dest )
        : myDest( dest )
        , myIsAtStartOfLine( true )
    {
    }
};

使用指向最终目的地的指针创建其中一个 (std::cerr.rdbuf()或您已打开的std::filebuf, 然后使用指向它的std::ostream

我在自己的记录器类中使用它;我添加了其他功能 开始和完成一个新的记录器记录:之后的第一个输出 开始记录输出时间戳,加上__FILE__和。{ 我传下来的__LINE__;以下标题是 只需一个任意数量的空白区域即可缩进。而且 完成功能还将确保记录结束 一个'\n',然后刷新。

最后,一些更一般的评论:首先,你不想要 客户端代码调用Logger::log,而不是一些宏 为了自动将__FILE____LINE__传递给。{ 创建实际对象。对象本身应该是 一个临时的,其析构函数调用上面的完成例程。 最后,如果您处于多线程环境中,那么您将会这样做 可能想要实际定位streambuf 无限缓冲区(例如std::vector<char>),其中 忽略用户的任何刷新,然后执行任何操作 完成时以原子方式写入整个缓冲区所必需的 调用例程,当然确保每个线程都有它 所有上述组件的自己的实例。 (如果你在 例如,一个Unix平台,Posix函数write是 保证原子,所以你不需要任何锁。)

答案 1 :(得分:1)

我认为更直观的界面是:

Logger::log( LogLevels::WARNING ) << "Something happened!" 
    << Logger::tabbedLine 
    << "More Info";

因此,用户仍然可以使用'\n'std::endl来到下一行的开头,或使用Logger::tabbedLine(一个坏名字,我承认)来实现你的目标想。

您可以继承std::ostream并为operator<<添加一个重载,它接收一个特殊的类tabbedLine是此类型的静态实例。您将需要“记住”您创建的此子流子类中所需的填充。

Logger::log将返回此ostream子类的实例。

答案 2 :(得分:1)

您可以编写自己的流,流缓冲区和操纵器:

#include <iostream>
#include <iomanip>
#include <sstream>

// LogBuffer
// ============================================================================

class LogBuffer : public std::streambuf
{
    // Types
    // =====

    public:
    typedef typename std::streambuf buffer_type;
    typedef typename buffer_type::char_type char_type;
    typedef typename buffer_type::traits_type traits_type;
    typedef typename buffer_type::int_type int_type;
    typedef typename buffer_type::pos_type pos_type;
    typedef typename buffer_type::off_type off_type;

    // Construction/Destructiion
    // =========================

    public:
    LogBuffer(buffer_type& buffer)
    :   m_buffer(buffer), m_indent(0), m_warning_count(0)
    {}

    public:
    ~LogBuffer() {
        m_buffer.pubsync();
    }

    private:
    LogBuffer(LogBuffer const&); // No Copy.
    LogBuffer& operator=(LogBuffer const&); // No Copy.


    // Functionality
    // =============

    public:
    bool write(const std::string& s) {
        return m_buffer.sputn(s.data(), s.size()) == std::streamsize(s.size());
    }

    bool warning() {
        std::ostringstream out;
        out << "[ WARNING: " << m_warning_count++ << "] ";
        m_indent = out.str().size();
        return write(out.str());
    }
    bool insert_indent() {
        std::ostringstream out;
        out << std::setw(m_indent) << "";
        return write(out.str());
    }

    // Virtuell
    // ========

    protected:
    std::streamsize xsputn(const char_type* s, std::streamsize n) {
        return m_buffer.sputn(s, n);
    }

    int_type overflow(int_type ch) {
        if(ch == traits_type::eof()) return traits_type::eof();
        else return m_buffer.sputc(traits_type::to_char_type(ch));
    }

    int sync() {
        return m_buffer.pubsync();
    }

    private:
    buffer_type& m_buffer;
    unsigned m_indent;
    unsigned m_warning_count;
};



// LogStream
// ============================================================================

class LogStream : public std::ostream
{
    // Types
    // =====

    private:
    typedef std::ostream Base;
    typedef LogBuffer buffer_type;

    public:
    typedef std::ostream stream_type;
    typedef typename Base::char_type char_type;
    typedef typename Base::traits_type traits_type;
    typedef typename Base::int_type int_type;
    typedef typename Base::pos_type pos_type;
    typedef typename Base::off_type off_type;

    // Construction
    // ============

    public:
    LogStream()
    :   Base(&m_buffer), m_buffer(*std::clog.rdbuf())
    {}

    LogStream(stream_type& stream)
    :   Base(&m_buffer), m_buffer(*stream.rdbuf())
    {}

    private:
    LogStream(const LogStream&); // No copy.
    const LogStream& operator = (const LogStream&); // No copy.

    private:
    buffer_type m_buffer;
};



// Manipulator
// ===========

std::ostream& log_warning(std::ostream& stream) {
    LogBuffer* buffer = dynamic_cast<LogBuffer*>(stream.rdbuf());
    if(buffer) {
        if( ! buffer->warning())
            stream.setstate(std::ios_base::failbit);
    }
    return stream;
}


std::ostream& log_indent(std::ostream& stream) {
    LogBuffer* buffer = dynamic_cast<LogBuffer*>(stream.rdbuf());
    if(buffer) {
        if( ! buffer->insert_indent())
            stream.setstate(std::ios_base::failbit);
    }
    return stream;
}


int main() {
    LogStream log;
    log << log_warning
        << "First\n"
        << log_indent
        << "Second\n"
        << std::flush;
}