0

My goal is to have an iomanip inserter with parameter that can be used to determine if a message will be printed to the stream (or not.) The idea is that a static mask will include bits set for the categories of messages that should be streamed (and bits cleared for messages to be discarded.) The inserter will be used to specify what category (or categories) a message belongs to and if the mask anded with the presented categories is not zero, the message would be streamed out. I have this working but with file scope mask and categories. It seems to me that (at least the category) could be stored with the stream using xalloc() to provide an index and iword() to store/retrieve values at that index but that seems not to be working for me. I have read various Internet references for these functions and my expectation is that sequential calls to xalloc() should return increasing values. In the code below the value returned is always 4. My second puzzlement is where the storage for the iword() backing store is held. Is this static for ostream? Part of every ostream object?

Code follows

#include <iostream>
#include <sstream>
// from http://stackoverflow.com/questions/2212776/overload-handling-of-stdendl
//
// g++ -o blah blah.cpp
//
// Adding an iomanip with argument as in 
// http://stackoverflow.com/questions/20792101/how-to-store-formatting-settings-with-an-iostream
//

using namespace std;

// don't really want file scope variables... Can these be stored in stream?
static int pri=0;       // value for a message
static int mask=1;      // mask for enabled output (if pri&mask => output)

static int priIDX() {   // find index for storing priority choice
    static int rc = ios_base::xalloc();
    return rc;
}

class setPri // Store priority in stream (but how to retrieve when needed?)
{
    size_t _n;
public:
    explicit setPri(size_t n): _n(n) {}
    size_t getn() const {return _n;}
    friend ostream& operator<<(ostream& os, const setPri& obj)
    {
        size_t n = obj.getn();
        int ix = priIDX();
        pri = os.iword(ix) = n;     // save in stream (?) and to file scope variable
        os << "setPri(" << n << ") ix:" << ix << " ";       // indicate update
        return os;
    }
};

class MyStream: public ostream
{
    // Write a stream buffer that discards if mask & pri not zero
    class MyStreamBuf: public stringbuf
    {
        ostream&   output;
        public:
            MyStreamBuf(ostream& str)
                :output(str)
            {}

        // When we sync the stream with the output. 
        // 1) report priority mask (temporary)
        // 2) Write output if same bit set in mask and priority
        // 3) flush the actual output stream we are using.
        virtual int sync ( )
        {
            int ix = priIDX();

            int myPri(output.iword(ix));
            output << "ix:" << ix << " myPri:" << myPri << '\n';

            if( mask & pri) // can't use (myPri&mask)
                output << ' ' << str();
            str("");
            output.flush();
            return 0;
        }
    };

    // My Stream just uses a version of my special buffer
    MyStreamBuf buffer;
    public:
        MyStream(ostream& str)
            :buffer(str)
        {
            rdbuf(&buffer);
        }
};

int main()
{
    MyStream myStream(cout);
    myStream << setPri(1) << " this should output" << endl;
    myStream << setPri(2) << " this should not output" << endl;
    myStream << setPri(3) << " this should also output" << endl;
}

Note that in sync() the code tries to fetch the value from the stream but the returned value is always 0 as if it was not set to begin with.

In my searches to get to this point I have seen comments that it is not a good idea to subclass an ostream. Feel free to suggest a better alternative! (That I can understand. ;) )

Thanks!

HankB
  • 332
  • 4
  • 17
  • You have two streams. `setPri` operates on the wrapper stream; `sync` operates on the wrapped stream. – T.C. Sep 28 '16 at 17:58
  • since MyStream is derived from ostream and does not redefine iword(), shouldn't MyStream:: iword() forward to ostream:: iword() and isn't that the ostream for which iword() was assigned the new value? Are there really two distinct streams here or one that 'wraps' the other? Thanks – HankB Sep 28 '16 at 20:31
  • `setPri` sets the `iword` on `MyStream`. But `MyStreamBuf` reads the `iword` on `cout`. – T.C. Sep 28 '16 at 20:47
  • Also, `ostream(&buffer)` is extremely questionable at best because it passes a not-yet-constructed stream buffer to `ostream`'s constructor. Default construct the stream, and then use `rdbuf()` in the constructor body instead. – T.C. Sep 28 '16 at 20:49
  • Thanks - I've edited the example code to use rdbuf() instead of the initializer list. Now to figure out how to get the setPri initializer into the correct stream. – HankB Sep 29 '16 at 14:24

1 Answers1

2
static int priIDX() {   // find index for storing priority choice
    static int rc = ios_base::xalloc();
    return rc;
}

This will always return the same value, as your value is static. And therefore only initialized on the first call.

The storage for the iword data is dynamic and allocated separately by each stream object whenever something is stored there.

Bo Persson
  • 90,663
  • 31
  • 146
  • 203
  • Thanks, Bo. That's an embarrassing mistake. Now I understand the code a little better and that's the way it is supposed to work. The call to priIDX() is meant to return the same value every time so the same value in the array managed by xalloc() will be referenced. (Subsequent calls to xalloc() to return incrementing values.) – HankB Sep 28 '16 at 20:27
  • @HankB If this solved your issue, I'd encourage you to accept this answer for the good of any future reader. – Jonathan Mee Nov 26 '18 at 17:17