【问题标题】:Unexpected behavior with boost::streams::output filterboost::streams::output 过滤器的意外行为
【发布时间】:2016-09-26 20:50:43
【问题描述】:

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

#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

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

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

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

谢谢!

编辑:尝试根据 Nikita 的建议添加解决方案,方法是添加 setPri 类以用作带有参数的 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
// 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

【问题讨论】:

    标签: c++ boost-iostreams


    【解决方案1】:

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

    语句out &lt;&lt; "test-1" &lt;&lt; endl; 不会过滤序列,它会将符号放入缓冲区以供进一步处理。之后pri=2; 和更多符号进入out &lt;&lt; "test-2" &lt;&lt; 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&lt;&lt; 来操作优先级。 解决方案草案:

    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
    

    【讨论】:

    • 这解释了这种行为,但我不确定这对我是否有意义。根据我在网上找到的材料“它的行为相当于调用 os.put('\n') (或 os.put(os.widen('\n')) 为字符以外的字符类型),然后os.flush()。”我希望 endl 会刷新输出,但显然它不会。谢谢!
    • 为了实现enable(0x0010) 的目标,您可以尝试为enable 创建用户定义的类型,并使用此类型作为参数重载operator&lt;&lt;。此重载将使用正确的 toupper_output_filter 实例完成所有 pop()/push()
    • 谢谢,我已经尝试过了,但仍然没有希望得到结果。请参阅我对我的 OP 的补充。
    • @HankB 检查更新。我添加了operator&lt;&lt; 想法的示例实现。
    • @HankB 你好!你有时间检查吗?有帮助吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-07-30
    • 2017-07-22
    相关资源
    最近更新 更多