7

I have class Writer that has two ofstream members.
Both streams are associated with the same output file. I'd like to use both streams in Writer::write method, but to make sure that each stream writes to the end of the real output file.

Code

class my_ofstream1:
    public ofstream
{
    // implement some functions.
    // using internal, extended type of streambuf
};

class my_ofstream2:
    public ofstream
{
    // implement some functions.
    // using internal, extended type of streambuf 
    // (not the same type as found in my_ofstream1)
};


class Writer
{
public:

    void open(string path)
    {
        f1.open(path.c_str(),ios_base::out); f2.open(path.c_str(),ios_base::out);
    }

    void close()
    {
        f1.close(); f2.close();
    }

    void write()
    {
        string s1 = "some string 1";
        string s2 = "some string 2";
        f1.write(s1.c_str(), s1.size());

        // TBD - ensure stream f2 writes to the end of the actual output file
        assert(f1.tellp() == f2.tellp());
        f2.write(s2.c_str(), s2.size());
    }

private:
    my_ofstream1 f1;
    my_ofstream1 f2;
};

void main()
{
    Writer w;
    w.open("some_file.txt");
    w.write();
    w.close();
}

Questions

How to ensure f2 is in sync with f1? meaning, before writing, stream offset of f2 must be in sync with stream offset of f1 and vice versa?
I can't use function std::ios::rdbuf since each ofstream uses special derived streambuf. so by using rdbuf() I'll lose the necessary functionality.
I tried using some of the techniques found in Synchronizing Streams topic but could not make it happen.

idanshmu
  • 5,061
  • 6
  • 46
  • 92
  • Look into `tie()`. It allows you to couple a stream (input or output) to an output stream, such that the output of the tied stream gets flushed. In my quick reading of `tie()`, though, it appears the synchronization is only one-way. Otherwise, it seems like you might consider an approach that funnels everything through a common class that serializes and writes everything via the true `ofstream`. – Joe Z Dec 24 '13 at 07:56
  • Is the "internal, extended type of streambuf" the same type for both streams? If so, just create both streams writing to the same streambuf object. – Jerry Coffin Dec 24 '13 at 08:11
  • Unfortunately, they are not of the same type. Of course, they are both of type streambuf but I must have two different objects since each special streambuf manipulate the data in a different way before writing it to the file. – idanshmu Dec 24 '13 at 08:14
  • 1
    Ultimately, all variants must serialize down to a stream of bytes that get written to the file. Can you make your various streambufs do that, and then send their payloads to a single, shared `ofstream` after serialization? I see no reason to push the actual file I/O that far up in the hierarchy. – Joe Z Dec 24 '13 at 09:02
  • Well, I agree the data manipulation is too far up in the hierarchy. but I would really like to address both ofstreams as a black box which I cannot modify. Once we exhaust all proposed solutions with no luck I guess I'll have to redesign. – idanshmu Dec 24 '13 at 09:13
  • 1
    @JoeZ There's no problem at the `ostream` level. It's at the `streambuf` level that he has to delegate: his custom streambufs should be decorators of a common final `filebuf` (and not do any buffering themselves). – James Kanze Dec 24 '13 at 09:36
  • Upon reflection, I think that James, [the authority](http://lists.boost.org/Archives/boost/att-49459/fltrsbf1.htm), is correct - you will need to share the filebuf, except change the attach functions in my answer so that they attach your own custom streambufs. The streambufs will have to contain a pointer to the filebuf so that they can do their processing and then delegate to filebuf's sputc, etc – user3125280 Dec 24 '13 at 12:24

2 Answers2

1

Is this not what you are looking for? This could be easily modified to work with ostreams rather the ofstreams, which is nicer - the actual issue is synchronisation of the buffers. In this code I have simply made the filebuf bf unbuffered and it works fine. Alternatively leave it buffered but include calls to pubsync when switching between my_ofstream's. I don't understand why ios:rdbuf is not available. Are you creating your own streambuf?

#include <iostream>
#include <fstream>
#include <assert.h>

using namespace std;

class my_ofstream1 : public ofstream
{
public:
    my_ofstream1& write (const char_type* s, streamsize n)
    {
        ofstream::write (s, n);
        //rdbuf()->pubsync();
        return *this;
    }

    void attach (filebuf* bf){
        ios::rdbuf(bf);
    }
};

class my_ofstream2 : public ofstream
{
public:
    my_ofstream2& write (const char_type* s, streamsize n)
    {
        ofstream::write (s, n);
        //rdbuf()->pubsync();
        return *this;
    }

    void attach (filebuf* bf){
        ios::rdbuf(bf);
    }
};


class Writer
{
    filebuf bf;
    my_ofstream1 f1;
    my_ofstream1 f2;

public:

    void open(string path)
    {
        bf.open(path.c_str(),ios_base::out);
        bf.pubsetbuf(0,0); //unbufferred
        f1.attach(&bf); f2.attach(&bf);
    }

    void close()
    {
        f1.close(); f2.close();
    }

    void write()
    {
        string s1 = "some string 1";
        string s2 = "some string 2";
        f1.write(s1.c_str(), s1.size());

        assert(f1.tellp() == f2.tellp());
        f2.write(s2.c_str(), s2.size());
    }


};

int main()
{
    Writer w;
    w.open("some_file.txt");
    w.write();
    w.close();

    return 0;
}   
user3125280
  • 2,779
  • 1
  • 14
  • 23
  • I assume James' answer is wrong too if each use a special stream_buf. Otherwise just use rdbuf to access the stream_buf and pubsetbuf with 0 as arguments to make it unbuffered. – user3125280 Dec 24 '13 at 09:40
  • 10x. As I wrote, I can't use function std::ios::rdbuf since each ofstream uses special derived streambuf. so by using rdbuf() I'll lose the necessary functionality. long story short, if both stream are using the same streambuf then both streams are have the same writing functionality. – idanshmu Dec 24 '13 at 11:33
  • ahh so we are both wrong - I'll see what I can dig up. In either case rdbuf should be there, just overload it for your streambuf. What do yuo mean by derived? Are they specialisations of basic_streambuf? Or do they inherit from some streambuf object? – user3125280 Dec 24 '13 at 11:56
  • They inherit form streambuf object and implement such function as open(),close(), overflow(), underflow(), etc. 10x for your time. I appreciate it. – idanshmu Dec 24 '13 at 12:23
  • They just need constructors like my_streambuf(strembuf* b) and then after processing intput, write it on to b (which will be the shared filebuf) problem solved, good luck :) – user3125280 Dec 24 '13 at 12:26
1

This looks like both of your classes use the filtering streambuf idiom. In any case, don't derive your classes from std::ofstream, but directly from ostream, and have them both use the same std::filebuf object. If you are using the filtering streambuf idiom, don't let your filtering streambuf's buffer; leave that to the final std::filebuf.

In other words, your "internal, extended type of streambuf" should contain a pointer to the final sink, which will be a filebuf (but your filtering streambufs don't need to know this). Functions like sync, they just pass on to the final destination, and they should never establish a buffer themselves, but pass everything on to the filebuf.

James Kanze
  • 150,581
  • 18
  • 184
  • 329