繁体   English   中英

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

[英]C++ Stream: Insert string after newline

原始问题是:换行发生后,如何将某些内容自动插入流中。 仅当事后将(手动)插入流中时,才应进行插入。

以下是更详细的说明。


为了练习,我正在编写自己的记录器类。 此片段提供了基本的日志记录功能。

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

这种行为可以实现吗?如果可以,如何实现?

只需在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__ 以下标题只是缩进的任意数量的空格。 并且finish函数还将确保记录以'\\n'结尾并被刷新。

最后,给出一些更笼统的注释:首先,您不希望客户端代码调用Logger::log ,而是调用一些宏,以便将__FILE____LINE__自动传递到创建的实际对象中。 对象本身可能应该是一个临时对象,其析构函数调用上面的finish例程。 最后,如果您处于多线程环境中,则可能要针对具有实际上无限的缓冲区(例如std::vector<char> )的streambuf ,该缓冲区将忽略用户的任何刷新,然后在调用finish例程时,执行所有必要的操作以原子方式写入整个缓冲区,当然,要确保每个线程都具有上述所有组件的自己的实例。 (例如,如果您在Unix平台上,则必须保证Posix函数的write是原子的,因此您不需要任何锁。)

我认为更直观的界面是:

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

因此,用户仍然可以使用'\\n'std::endl到达下一行的开始,或者使用Logger::tabbedLine (我承认这是一个不好的名字)来实现所需的功能。

您可以将std::ostream子类化,并为operator<<添加重载,该重载会收到一个特殊的类,其中tabbedLinetabbedLine的静态实例。 您将需要在创建的流子类中“记住”所需的填充。

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

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

#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;
}

暂无
暂无

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

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