7

我正在尝试编写一个自定义std::ostream,它为写入它的每一行调用一个函数。也就是说,我希望以下代码能够按照注释中的说明工作:

my_output_stream s([] (const std::string& line) { 
    WriteLineToSomeOutput(line); 
});

s << "Hello world"; // do not invoke anything. The line has not ended yet!
s << ", from Alex" << std::endl; // here we invoke WriteLineToSomeOutput("hello world, from Alex")
s << "What's up"; // do not invoke anything. The line has not ended yet.
s << ", doc?!\nHow u doing?\n"; // Now we have two lines. We invoke WriteLineToSomeOutput("What's up, doc?!) and WriteLineToSomeOutput("How u doing?")

请注意,数据不会写入任何地方,也不会存储在任何地方。我需要流存储的唯一内容是正在聚合的当前行,直到我们遇到行尾。

即使使用 boost.Iostreams 库,我也没有找到任何简单的方法。我可以通过使用 STL 和 Boost 的一些内置工具来避免在这里编写自己的行标记器吗?

背景

该类my_output_stream将用于在我的应用程序中使用的外部库和日志库之间进行调整。外部库要求我提供一个 std::ostream。我想使用我的应用程序的日志框架记录外部库记录的每一行。

4

3 回答 3

3

如果我理解正确,您希望在行尾无条件刷新,并且在行尾。为此,您必须实现自己的streambuf;它可以基于 std::stringbuf,但如果您只关心输出,而不担心寻找,那么您自己做可能同样容易。

像下面这样的东西应该可以解决问题:

class LineBufferedOutput : public std::streambuf
{
    std::vector<char> myBuffer;
protected:
    int overflow( int ch ) override
    {
        myBuffer.push_back( ch );
        if ( ch == '\n' ) {
            //   whatever you have to do...
        }
        //  return traits::eof() for failure...
    }
};

我不确定您实施自己的标记器是什么意思;不涉及标记化。您确实必须查看每个字符,以便将其与 进行比较'\n',但仅此而已。

并且您忽略对sync().

于 2014-07-28T12:09:45.287 回答
0

我可能会先实现一个可流式设备,然后将其包装在 boost::iostream 中。看一下 boost::iostreams 并将其用作启动器:

#include <iosfwd>                           // streamsize, seekdir
#include <boost/iostreams/categories.hpp>   // seekable_device_tag
#include <boost/iostreams/positioning.hpp>  // stream_offset

#include <boost/function.hpp>

class MyDevice
{

  public:
    typedef boost::function<void()> Callback; // or whatever the signature should be
    typedef char                                   char_type;
    typedef boost::iostreams::seekable_device_tag  category;

    explicit MyDevice(Callback &callback);

    std::streamsize read(char* s, std::streamsize n);
    std::streamsize write(const char* s, std::streamsize n);
    std::streampos seek(boost::iostreams::stream_offset off, std::ios_base::seekdir way);

  private:
    MyDevice();
    Callback myCallback;
};

这将是基本声明。您需要在 .cpp 文件中定义每个功能的实现方式。这些功能之一可能实现如下:

std::streampos MyDevice::write(const char* s, std::streamsize n)
{
    // process written data
    // file callback
    myCallback();
    // etc
}

然后从其他地方使用,例如在您的主要功能中:

Callback callback; // some function
MyDevice device(callback);
boost::iostreams::stream<MyDevice> stream(device);
stream << data; // etc.
于 2014-07-28T11:05:58.403 回答
0

看来您只是在寻找行缓冲,而标准 ostream 已经这样做了(除非您特别要求它不要使用任何一个std::unitbufstd::flush操纵器)。

现在,长行可能会溢出输出缓冲区并触发“早期”刷新,但请记住,这无论如何都可能发生:如果输出到文件,操作系统将应用相同类型的缓冲策略,但实际上并非如此无论您在哪些块中冲洗长线。

如果输出到套接字,例如,那么您可以一次性发送缓冲区(如果您注意确保缓冲区足够大),但 TCP/IP 层可以根据调整自由地将流分解为包和网络硬件的限制。

于 2014-07-28T11:10:18.203 回答