0

I'm basically trying to derive from wfilebuf so I can both output to a file and intercept the output to print it to the console/debug window as well as illustrated here: http://savingyoutime.wordpress.com/2009/04/21/ and/or here: http://savingyoutime.wordpress.com/2009/04/22/40/

(ancient supporting ideas here: http://www.horstmann.com/cpp/streams.txt)

I've almost got it, but I can't seem to be able to both write to the underlying file AND peek at the input.

I overrode the sync() function similar to the second example but it seems that pbase() and pptr() are always NULL unless I set a buffer with setp(...), but this seems to break the file output. The file is always empty!

My crude attempt at this is below:

class LoggerBuffer : public wfilebuf {
// Functions
    public:
        LoggerBuffer();
        ~LoggerBuffer();
        void open(const wchar_t loggerFile[]);
        void close();
        int sync();
        int_type overflow(int_type c = EOF);
        void setState(int newState);
// Variables
    private:
        int currentState;
        static const int BUFFER_SIZE = 10;
        wchar_t buffer[BUFFER_SIZE];  
};

class LoggerStream : public wostream {
// Functions
    public:
         LoggerStream();
         ~LoggerStream();
         void open(const wchar_t loggerFile[] = 0);
         void close();
         void setState(int newState);
};

LoggerBuffer::LoggerBuffer() {
    wfilebuf::open("NUL", wios::out); currentState = 1;
}
LoggerBuffer::~LoggerBuffer() {
    wcout << "Destruction of LoggerBuffer" << endl;
}
void LoggerBuffer::open(const wchar_t loggerFile[]) {
    wcout << "LoggerBuffer Opening " << loggerFile << endl;
    close();
    wfilebuf* temp = wfilebuf::open(loggerFile, wios::out); //ios::out | ios::app | ios::trunc
    setp (buffer, buffer+(BUFFER_SIZE-1));
}
void LoggerBuffer::close() {
    wfilebuf::close();
}

int LoggerBuffer::sync() {
    wcout << "  Syncing ";
    int out_waiting = pptr() - pbase();
    wcout << out_waiting << " characters!";
    wcout << endl;
    wcout << "pptr(): " << (unsigned int)pptr() << endl;
    return wfilebuf::sync();
}
LoggerBuffer::int_type LoggerBuffer::overflow(int_type c) {
    wcout << "overflow! (" << (wchar_t)c << ")" << endl;
    if (c == EOF)
        return EOF;
    if (sync() == EOF)
        return EOF;
    return wfilebuf::overflow(c);
}
void LoggerBuffer::setState(int newState) {
    wcout << "New buffer state = " << newState << endl;
    currentState = newState;
}

LoggerStream::LoggerStream() : wostream(new LoggerBuffer), wios(0) {
}
LoggerStream::~LoggerStream() {
    delete rdbuf();
}
void LoggerStream::open(const wchar_t loggerFile[]) {
    wcout << "LoggerStream Opening " << loggerFile << endl;
    ((LoggerBuffer*)rdbuf())->open(loggerFile);
}
void LoggerStream::close() {
    ((LoggerBuffer*)rdbuf())->close();
}
void LoggerStream::setState(int newState) {
    wcout << "New stream state = " << newState << endl;
    ((LoggerBuffer*)rdbuf())->setState(newState);
}

Full disclosure: I asked a question regarding something similar earlier: Simple wostream logging class (with custom stream manipulators)

I think I have solved that problem though.

Any help is greatly appreciated! Thanks!

Community
  • 1
  • 1
  • I beleive composition would make a more reusable class than the one inherited here. Still can't find a error :( – Basilevs Nov 13 '10 at 12:37
  • Have you set appropriate codecvt for both your buffer locale and standard streams locales? Have you tried to output ASCII string? – Basilevs Nov 15 '10 at 06:45

1 Answers1

0

I'd use a filtering streambuf, that does no buffering of its own, instead passing data through to a real streambuf (i.e., one that does real buffering) for each of the destinations. This should simplify your code quite a bit and let you concentrate on the parts you really care about.

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
  • While nonbuffered streambufs are indeed easier to implement, they might have performance issues. – Basilevs Nov 13 '10 at 12:35
  • @Basilevs: If you were (for example) writing to an actual file without buffering, that would cause a problem -- but in this case, you're just writing to another buffer. With a decent compiler that can inline the function calls, more buffering won't (usually) do any good. – Jerry Coffin Nov 13 '10 at 15:37
  • THere is no inlining for virtual calls. Note also that implentation linked by you suppose call an overflow function on each byte of the stream. That is the overhead i mean - you should minimize a number of function calls if you want good performance on large streams of data. For that reason it is better to override xsputn. – Basilevs Nov 13 '10 at 20:48
  • @Basilevs: virtual calls can be inlined if the compiler can statically determine the type of the target. As always, I'd make it work first, then worry about making it faster if you find it's a bottleneck. – Jerry Coffin Nov 13 '10 at 21:13
  • What the point in deriving from streambuf and then using it in statically determined contexts only? STL buffers are all about interchangeability. You are right about optimizations. – Basilevs Nov 14 '10 at 04:31
  • @Basilevs: I'd look at things from the other direction. How often are you really likely to use there where the types of the target streambufs *can't* be determined statically? – Jerry Coffin Nov 14 '10 at 15:48
  • Anything derived from std::ostream? A pointer is used there to access a buffer implementation. – Basilevs Nov 15 '10 at 06:41