2016-11-30 25 views
-3

我在學習C++(來自C和Java大學課程),今天我想編寫一個類來過濾從通用流中獲取的字節並將其輸出寫入另一個類流。C++篩選流中字節的最佳方法

爲了達成一致,假設我想創建一個base64編碼輸入並將輸出寫入標準輸出的類。

在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(如性病:: boolalpha,性病::十六進制,...)。但是,這些只是在流中設置標誌,它已經知道如何解釋。

如果你想保住你的語法,你需要更復雜的東西,即中間包裝:

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<<過載。

另一種方法是編寫std::basic_streambuf實現,該實現將輸入編碼爲sputc/sputn/xsputn。它可以將其他所有內容轉發到包裝的流緩衝區或基類,具體取決於您繼承的內容。