I need some guidance or pointers understanding how to implement a custom ostream. My requirements are:
- A class with a ‘<<‘ operator for several data types.
- The intention is to send output to database. Each “line” should go to a separate record.
- Each record most important field would be the text (or blob), but some other fields such as time, etc. can be mostly deduced automatically
- buffering is important, as I don’t want to go to database for every record.
First, does it worth deriving from ostream? What do I get by deriving from ostream? What if my class simply implements few operator<< methods (including some custom data types). Which functionality do I get from ostream?
Assuming what I want is a class derived from ostream, I need some guidance understanding the relationship between the ostream and the streambuf classes. Which one do I need to implement? Looking at some samples, it appears that I don’t need to derive from ostream at all, and just give the ostream constructor a custom streambuf. Is that true? is that the canonical approach?
Which virtual functions at the custom streambuf do i need to implement? I’ve seen some samples (including this site: here and here, and few more), some override the sync method, and other override the overflow method. Which one should I override? Also, looking at the stringbuf and filebuf sources (Visual Studio or GCC) both those buffer classes implement many methods of the streambuf.
If a custom class derived from streambuf is required, would there be any benefit deriving from stringbuf (or any other class) instead of directly from streambuf?
As for “lines”. I would like at least when my users of the class using the ‘endl’ manipulator to be a new line (i.e. record in database). Maybe – depends on effort – every ‘\n’ character should be considered as a new record as well. Who do my custom ostream and/or streambuf get notified for each?
A custom destination for ostream means implementing your own ostreambuf. If you want your streambuf to actually buffer (i.e. don’t connect to the database after each character), the easiest way to do that is by creating a class inheriting from
std::stringbuf. The only function that you’ll need to override is thesync()method, which is being called whenever the stream is flushed.You can then create a
std::ostreamusing your buffer:Most people advised against redirecting the stream to a database, but they ignored my description that the database basically has a single blob field where all text is going to.
In rare cases, I might send data to a different field. This can be facilitated with custom attributes understood by my stream. For example:
The code above will create a record in the database with:
process_id()is a method returning a structureProcessID. Then, in the implementation of my ostream, I have anoperator<<(ProcessID const& pid), which stores the process ID until it gets written. Works great!