2016-11-30 4 views
-3

私はC++(CおよびJava大学の教授から来ています)を学んでいますが、今日はジェネリックストリームから取り出したバイトをフィルタリングし、ストリーム。C++ストリーム内のバイトをフィルタリングするための最良の方法

私が入力をbase64エンコードし、出力をstdoutに書き込むクラスを作りたいとしましょう。私が書くでしょうbashで

echo "some input data" | base64 

C++では、私がこのように振る舞うだろうクラスMyB64Encoderを実装する:

std::cout << myB64EncoderObject << "some input data"; 

//Alternatively, is it possible to make it like this? 
std::cout << MyB64Encoder << "some input data"; 

事は、当然のことながら、myB64EncoderObjectがあり、あります内部状態および内部バッファを維持する。ブロッキングと過度のメモリ使用を防ぐために、小さなチャンクを読み込んで処理し、処理した直後にそれぞれのデータを出力できる必要があります。何のストリームの読み取りがない場合、オブジェクトがエラーをスローする必要があり

  • データを受信することができるようにオブジェクトが出力ストリームを待たなければなりません

    • はの世話をするためにいくつかのより多くの事があります(破損したパイプのようなもの)

    このような問題を効率的に解決するにはどうすればよいでしょうか?私はそれを現代のC++ 1xにどのように実装しますか?

  • +1

    ストリームをラップする(またはストリームバッファをラップする) –

    答えて

    0

    あなたはこのような何かを行うことができます。

    class MyEncoder 
    { 
    public: 
    private: 
        std::ostream* os = nullptr; 
    
        // This overload deals with: 
        // std::cout << myEncoder ... 
        friend MyEncoder& operator<<(std::ostream& os, MyEncoder& me) 
        { 
         // grab a reference to the target output stream 
         me.os = &os; 
         return me; 
        } 
    
        // This overload deals with: 
        // std::cout << MyEncoder() ... 
        friend MyEncoder& operator<<(std::ostream& os, MyEncoder&& me) 
        { 
         // the temporary is currently bound to the l-value parameter me 
         // so we can just pass this call on to the previous overload 
         return os << me; 
        } 
    
        // This overload deals with: 
        // myEncoder << <anything else> 
        template<typename T> 
        friend MyEncoder& operator<<(MyEncoder& me, T const& v) 
        { 
         // only encode if there is an output stream to send the data to 
         // this will only be set if one of the above overloads was called 
         if(!me.os) 
          throw std::runtime_error("no stream to receive encoded data"); 
    
         // do your encoding here 
         (*me.os) << "{encoded: " << v << "}"; 
    
         return me; 
        } 
    }; 
    

    は基本的にこれを達成するために:

    std::cout << MyEncoder() << "some data: " << 45; 
    //          ^calls operator<<(MyEncoder&, 45) 
    //      ^calls operator<<(MyEncoder&, "some data: ") 
    //  ^calls operator<<(std::cout, MyEncoder()) 
    

    呼び出しが左から右へ行きます。

    少し複雑に見えるかもしれませんが、基本的に3つの異なる呼び出し可能性をカバーしています。

    MyEncoder encoder; 
    std::cout << encoder; // MyEncoder& object 
    
    std::cout << MyEncoder(); // (temporary) MyEncoder&& object 
    
    encoder << "anything else" // A MyEncoder& receiving any other object 
    

    第2演算子は、内部std::ostream*を設定するためにオーバーロードされており、第三のオペレータは、実際の符号化を行うためにオーバーロードされています。このように振る舞う

    1

    既存のもの:

    std::cout << myB64EncoderObject << "some input data"; 
    

    I/O manipulatorsある(例えば、STD :: boolalpha、STD ::進、...。)。しかし、ストリーム上にフラグを設定するだけで、すでにどのように解釈するかが分かっています。

    あなたはその構文を維持したい場合は、より複雑なもの、すなわち、中間ラッパーに必要があります:

    class B64Wrapper { 
        std::ostream &os_; 
        B64Encoder &enc_; // only if your encoder is really stateful 
    
    public: 
        B64Wrapper() = delete; 
        B64Wrapper(B64Wrapper&&) = default; 
        B64Wrapper(B64Wrapper const&) = default; 
    
        B64Wrapper(std::ostream &os, B64Encoder &enc) : os_(os), enc_(enc) {} 
    
    
        template <typename T> 
        B64Wrapper& operator<< (B64Wrapper &self, T val) { 
         self.enc_.encode(os_, val); 
         return self; 
        } 
    }; 
    
    B64Wrapper operator<< (std::ostream &os, B64Encoder &enc) { 
        return B64Wrapper(os, enc); 
    } 
    

    (それでもB64Encoder::encode(std::ostream &, T value)メソッドを記述する必要があります注意してください)。

    エンコーダが実際にステートフルでない場合は、エンコーダに参照が不要で、同じ効果を得るためにグローバルインスタンスを持つ空のタグタイプとしてB64Encoderを宣言します。 operator<<過負荷。

    もう1つの方法は、入力をsputc/sputn/xsputnにエンコードするstd::basic_streambufの実装を記述することです。あなたが継承したものに応じて、他のものをすべてラップされたstreambufまたは基本クラスに転送することができます。

    関連する問題