boost::streams::output 过滤器的意外行为

Unexpected behavior with boost::streams::output filter

我正在尝试实现一个用于日志记录的输出过滤器,并修改了一些示例代码,结果出现了意外。密码是

#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;
}

定义 TEST 宏时遇到意外行为:

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$ 

好像表达式'if(pri<3)'在第一次调用结构体成员函数时被求值一次。我希望每次有内容流式传输到 'out.'

时都会对其进行评估

顺便说一句,我正在努力开发一些可以记录到控制台(或者可能是文件)并且能够基于位图进行过滤的东西。 IOW,将定义一个掩码并在其中设置位,以使特定的输出语句能够实际写入某些内容。代码可能看起来像(掩码与启用一起使用)

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

这似乎是开发人员可能想要做的常见事情,但我找不到要复制的示例。我正在努力寻找解决方案并 运行 解决这个问题。

谢谢!

编辑:尝试根据 Nikita 的建议添加 setPri class 以用作带有参数的 iomanip。仍未按预期工作 所有输出都被缓存,直到程序退出,然后最后一次 setPri() 插入生效。这就是 Boost iostream 的工作方式吗?

#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
// 


// 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;
}

结果是

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

这里的问题是 toupper_output_filterpri 变量更改为 2 后应用于输出序列。

语句out << "test-1" << endl;不过滤序列,它将符号放入缓冲区以供进一步处理。之后 pri=2; 和更多符号进入 out << "test-2" << endl; 处的缓冲区。在离开范围之前 out 过滤它的缓冲区并输出最终的符号序列。此时 pri2 并打印所有行。

要解决此问题,您可以删除全局状态:

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;
    }
};

并使用它:

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;
}

更新: setPri class 的主要思想是去除静态并用 operator<< 操纵优先级。 解决方案草案:

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;
}

输出为:

 test-1
 test-3