2016-09-26 5 views
2

Я пытаюсь реализовать выходной фильтр для ведения журнала и изменил код примера с неожиданными результатами. КодНеожиданное поведение с 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; 
} 

неожиданное поведение возникает, когда ТЕСТ макрокоманда определена:

[email protected]:~/Documents/C++/t-pri$ g++ -o t-pri t-pri.cpp 
[email protected]:~/Documents/C++/t-pri$ ./t-pri 
pri: 4 
[email protected]:~/Documents/C++/t-pri$ g++ -DTEST -o t-pri t-pri.cpp 
[email protected]:~/Documents/C++/t-pri$ ./t-pri 
pri: 4 
test-1 
test-2 
[email protected]:~/Documents/C++/t-pri$ 

Кажется, что выражение «если (PRI < 3)» вычисляется один раз, когда элемент структуры функция сначала вызывается. Я ожидаю, что он будет оцениваться каждый раз, когда что-то потокочет на «выход».

Как в стороне, я работаю над тем, что записывается на консоль (или, возможно, файл), и имеет возможность фильтровать на основе растрового изображения. IOW, будет определена маска и биты, установленные в ней, чтобы позволить конкретным операторам вывода фактически что-то писать. Код может выглядеть следующим образом (где маска с операцией AND включить)

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

Это похоже на общий вид вещи, которые разработчик может захотеть сделать, но я не могу найти пример для копирования. Я работаю над решением и сталкивался с этим.

Спасибо!

Редактировать: Пытается добавить к решению предложение Никиты, добавив класс setPri для использования в качестве iomanip с аргументом. Все еще не работает, как ожидалось. Все выходные данные кэшируются до выхода программы, а затем вступает в действие последняя вставка 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 

ответ

2

Проблема здесь состоит в том, что toupper_output_filter применяется к выходной последовательности после того, как переменная pri изменена на 2.

Заявление out << "test-1" << endl; не фильтрует последовательность, оно помещает символы в буфер для дальнейшей обработки. После этого pri=2; и больше символов поступает в буфер по адресу out << "test-2" << endl;. Перед тем, как покинуть область out, он фильтрует буфер и выводит окончательную последовательность символов. На данный момент pri - 2 и все напечатанные строки.

Чтобы устранить эту проблему можно устранить глобальное состояние:

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

Update: Основная идея 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 
+0

Это объясняет поведение, но я не уверен, что это имеет смысл для меня.Согласно материалу, я нахожу его в строке «Его поведение эквивалентно вызову os.put ('\ n') (или os.put (os.widen ('\ n')) для типов символов, отличных от char), и затем os.flush(). " Я бы ожидал, что endl сбросит выход, но, тем не менее, это не так. Спасибо! – HankB

+0

Чтобы достичь цели 'enable (0x0010)', вы можете попытаться создать пользовательский тип для 'enable' и overload [' operator << '] (http://en.cppreference.com/w/cpp/language/операторов) с этим типом в качестве параметра. Эта перегрузка будет делать все 'pop()'/'push()' с соответствующим экземпляром 'toupper_output_filter'. – Nikita

+0

Спасибо, я пошла на это и все еще не надеялась на результаты. Пожалуйста, ознакомьтесь с моими дополнениями к моему OP. – HankB