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

Unexpected behavior with boost::streams::output filter

本文关键字:意外 过滤器 streams output boost      更新时间:2023-10-16

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

#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 '时都对它进行评估。

作为题外话,我正在研究一些记录到控制台(或者可能是一个文件)的东西,并具有基于位图过滤的能力。现在,将定义一个掩码并在其中设置位,以使特定的输出语句能够实际写入内容。代码可能看起来像这样(其中mask为enable)

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

这似乎是一种常见的事情,一个开发人员可能想要做,但我无法找到一个例子来复制。我正在寻找一个解决方案,然后遇到了这个。

谢谢!

编辑:试图通过添加setPri类来使用一个参数来添加到尼基塔建议的解决方案中。所有输出都被缓存,直到程序退出,然后最后一次setPri()插入生效。Boost iostreams应该是这样工作的吗?

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

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

这里的问题是pri变量变为2后,toupper_output_filter应用于输出序列。

语句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类的主要思想是删除静态状态并使用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