Std :: ostream which calls a callback for each line

I am trying to write a custom std::ostream that calls a function for every line written on it. That is, I would like the following code to work as described in the comments:

 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 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 up, doc?!) and WriteLineToSomeOutput("How u doing?") 

Please note that data is not recorded anywhere and is not saved anywhere. The only thing I need to store the stream is the current line, which is aggregated until we meet the end of the line.

I did not find any easy way to do this, even when using the boost.Iostreams library. Can I not write my own string tokenizer here using some of the built-in STL and Boost tools?

Background

The my_output_stream class will be used to adapt between the external library and the logging library used in my application. The external library requires me to provide std :: ostream. And I want to register every row that is registered with an external library using my application registration infrastructure.

+6
source share
3 answers

If I understand correctly, you want to unconditionally clear the end of the line and only at the end of the line. To do this, you must implement your own streambuf; it may be based on std::stringbuf , but if you are only worried about exiting and not worrying about searching, it is probably just as easy to do it yourself.

Something like the following should do the trick:

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

I'm not sure what you mean by implementing your own tokenizer; tokenization is not used there. You have to look at each character to compare it with '\n' , but that’s it.

And you ignore any explicit sync() requests.

+3
source

I would probably start by implementing a streaming device and then wrap it in boost :: iostream. Take a look at boost :: iostreams and use this as a starter:

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

This will be the main declaration. You will need to define in your .cpp file how each function will be implemented. One of these functions can be implemented as follows:

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

Then for use from other sources, for example. in your main function:

 Callback callback; // some function MyDevice device(callback); boost::iostreams::stream<MyDevice> stream(device); stream << data; // etc. 
0
source

It seems you are just looking for line buffering, and the standard ostream already does this (unless you specifically ask it not to use the std::unitbuf or std::flush manipulators).

Now long lines can overflow the output buffer and trigger an “early” flash, but keep in mind that this can happen anyway: if the output refers to a file, the OS will use the same buffering strategies and it doesn’t value in which pieces you discard long lines.

If the output refers to a socket, for example. then you can send the buffer at a time (if you make sure that the buffer is large enough), but the TCP / IP layer may break the flow in packets, depending on the settings and limitations of your network equipment.

0
source

Source: https://habr.com/ru/post/972951/


All Articles