HankB
HankB

Reputation: 332

Unexpected behavior with boost::streams::output filter

I'm trying to implement an output filter for logging and have modified some example code with unexpected results. The code is

#include <ctype.h>                        // toupper
#include <boost/iostreams/categories.hpp> // output_filter_tag
#include <boost/iostreams/operations.hpp> // put
#include <boost/iostreams/filtering_stream.hpp>

// cobbled from http://www.boost.org/doc/libs/1_48_0/libs/iostreams/doc/concepts/output_filter.html#examples
//
// g++ [-DTEST] -o t-pri t-pri.cpp

using namespace std;
namespace io = boost::iostreams;

int pri=4;

struct toupper_output_filter {
    typedef char                   char_type;
    typedef io::output_filter_tag  category;

    template<typename Sink>
    bool put(Sink& snk, char c) 
    { 
        if(pri<3)
            return io::put(snk, /* toupper((unsigned char) c)*/ c); 
        else
            return 0;
    }
};

int main(int argc, char**argv)
{
    boost::iostreams::filtering_ostream out;

    out.push(toupper_output_filter());
    cout << "pri: " << pri << endl;
    out.push(cout);

    out << "test-1" << endl;
#ifdef TEST
    pri=2;
    out << "test-2" << endl;
#endif
    return 0;
}

The unexpected behavior is encountered when the TEST macro is defined:

hbarta@itws007:~/Documents/C++/t-pri$ g++ -o t-pri t-pri.cpp
hbarta@itws007:~/Documents/C++/t-pri$ ./t-pri
pri: 4
hbarta@itws007:~/Documents/C++/t-pri$ g++ -DTEST -o t-pri t-pri.cpp
hbarta@itws007:~/Documents/C++/t-pri$ ./t-pri
pri: 4
test-1
test-2
hbarta@itws007:~/Documents/C++/t-pri$ 

It seems as if the expression 'if(pri<3)' is evaluated once when the structure member function is first being called. I expect it to be evaluated every time something is streamed to 'out.'

As an aside, I'm working toward something that logs to the console (or perhaps a file) and has the capability to filter based on bitmap. IOW, a mask would be defined and bits set in it to enable specific output statements to actually write something. Code could look like (where mask is anded with enable)

<sometype> mask(0x0101);
out << enable(0x0010) << "log message" << endl; // not output
out << enable(0x0100) << "another log message" << endl; // is output

This seems like a common sort of thing that a developer might want to do but I am unable to find an example to copy. I'm working toward a solution and ran into this.

Thanks!

Edit: Trying to add to the solution per Nikita's suggestion by adding the setPri class to use as an iomanip with an argument. Still not working as expected All output is cached until the program exits and then the last setPri() insertion takes effect. Is that how Boost iostreams should work?

#include <boost/iostreams/categories.hpp> // output_filter_tag
#include <boost/iostreams/operations.hpp> // put
#include <boost/iostreams/filtering_stream.hpp>

using namespace std;
namespace io = boost::iostreams;

// cobbled from http://www.boost.org/doc/libs/1_48_0/libs/iostreams/doc/concepts/output_filter.html#examples
//
// g++ -o to_upper to_upper.cpp
//
// Adding an iomanip with argument as in
// http://stackoverflow.com/questions/20792101/how-to-store-formatting-settings-with-an-iostream


// don't really want file scope variables...
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();
        pri = n;
        os << "setPri(" << n << ")";        // indicate update
        return os;
    }
};

struct toupper_output_filter {
    typedef char                   char_type;
    typedef io::output_filter_tag  category;

    template<typename Sink>
    bool put(Sink& snk, char c) 
    { 
       if(pri & mask) // Should this char be sent to output?
            return io::put(snk, c);
        else
            return 0;
    }
};

int main(int argc, char**argv)
{
    boost::iostreams::filtering_ostream out;

    out.push(toupper_output_filter());
    out.push(cout);

    out << setPri(1) << " test-1" << endl;
    out << setPri(2) << " test-2" << endl;
    out << setPri(3) << " test-3" << endl;
    return 0;
}

result is

setPri(1) test-1
setPri(2) test-2
setPri(3) test-3

Upvotes: 2

Views: 127

Answers (1)

Nikita
Nikita

Reputation: 6427

The problem here is that toupper_output_filter applied to the output sequence after the pri variable changed to 2.

Statement out << "test-1" << endl; doesn't filter the sequence, it puts symbols into the buffer for the further procession. After that pri=2; and more symbols goes to the buffer at out << "test-2" << endl;. Before leaving the scope out filters it's buffer and outputs final symbols sequence. At this point pri is 2 and all the lines printed.

To fix the issue you could remove global state:

struct toupper_output_filter {
    typedef char                   char_type;
    typedef io::output_filter_tag  category;

    int pri;
    toupper_output_filter (int logLevel)
    { 
      pri = logLevel;
    }

    template<typename Sink>
    bool put(Sink& snk, char c) 
    { 
        if(pri<3)
            return io::put(snk, /* toupper((unsigned char) c)*/ c); 
        else
            return 0;
    }
};

And use it:

int main(int argc, char**argv)
{
    boost::iostreams::filtering_ostream out;

    int pri = 4;
    out.push(toupper_output_filter(pri));
    cout << "pri: " << pri << endl;
    out.push(cout);

    out << "test-1" << endl;
    out.pop();
    out.pop();

#ifdef TEST
    pri=2;

    out.push(toupper_output_filter(pri));
    out.push(cout);
    out << "test-2" << endl;
#endif
    return 0;
}

Update: The main idea of setPri class is to remove the static state and to manipulate priority with operator<<. Draft for the solution:

static int mask=1;      // mask for enabled output (if pri&mask => output)

struct toupper_output_filter {
    typedef char                   char_type;
    struct category : boost::iostreams::output_filter_tag {};

    int pri;
    toupper_output_filter(int n)
    {
      pri = n;
    }

    template<typename Sink>
    bool put(Sink& snk, char c) 
    { 
       if(pri & mask) // Should this char be sent to output?
            return io::put(snk, c);
        else
            return 0;
    }
};

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 boost::iostreams::filtering_ostream& operator<<(boost::iostreams::filtering_ostream& out, const setPri& obj)
    {
      if (!out.empty())
      {
        out.pop();
        out.pop();
      }
      out.push(toupper_output_filter(obj.getn()));
      out.push(cout);
      return out;
    }
};

int main(int argc, char**argv)
{
    boost::iostreams::filtering_ostream out;

    out << setPri(1) << " test-1" << endl;
    out << setPri(2) << " test-2" << endl;
    out << setPri(3) << " test-3" << endl;
    return 0;
}

Output is:

 test-1
 test-3

Upvotes: 2

Related Questions