2013-04-21 42 views
3

對,我甚至不知道如何正確地制定這個;我覺得這是一個涉及到的問題。雖然有人可以幫助我。類輸出到幾個ostreams(文件和控制檯)

這就是我想做的事:

  1. 有一個單獨的類,我可以送的東西,像這樣。

    icl << "Blah blah blah" << std::endl; 
    
  2. 我希望能夠.attach繼承STD()類:: basic_ostream它。

  3. 那些類將能夠以自己的方式格式化輸出。可能會添加時間戳並寫入日誌,另一個可能會將其寫入控制檯,另一個可能會在遊戲中顯示它。

任何關心讓我開始正確的方向嗎?這是我非常想到的想法。

#include <vector> 

class OutputMan { 
    std::vector<std::basic_ostream&> m_Streams; 

public: 
    void attach(std::basic_ostream& os) { 
     m_Streams.push_back(os); 
    } 
} 

問題1:我需要什麼繼承和覆蓋發送

icl << "Blah!" << std::endl; 

對每一個數據流中m_Streams?

問題2:我該如何繼承std :: basic_ostream並創建一個將輸出更改爲(例如)將時間戳添加到其開頭的類?我也想讓這個類輸出到一個文件。

+0

問題1:Google運算符超載,您會看到很多示例。 – dutt 2013-04-21 19:40:23

+0

我很清楚運算符重載是如何工作的,但是這裏似乎有點複雜,因爲有大量的數據類型可以輸入OutputMan,就像一個整數。當然有更好的方法。 – 2013-04-21 19:41:48

+0

您可以爲可能需要特殊處理的類型使用具有特定模板重載/實例(不確定其稱謂)的模板。 – dutt 2013-04-21 19:44:02

回答

5

我想我會做點事情有點不同。我可能已經做了一些比必要更復雜的工作 - 恐怕我可能會因爲嘗試使用新的C++ 11功能而有點疲憊。總之,在與代碼:

#include <streambuf> 
#include <fstream> 
#include <vector> 
#include <iostream> 
#include <initializer_list> 

namespace multi { 
class buf: public std::streambuf { 
    std::vector<std::streambuf *> buffers; 
public: 
    typedef std::char_traits<char> traits_type; 
    typedef traits_type::int_type int_type; 

    buf(std::vector<std::ofstream> &buf) { 
     for (std::ofstream &os : buf) 
      buffers.push_back(os.rdbuf()); 
    } 

    void attach(std::streambuf *b) { buffers.push_back(b); } 

    int_type overflow(int_type c) { 
     bool eof = false; 
     for (std::streambuf *buf : buffers) 
      eof |= (buf -> sputc(c) == traits_type::eof()); 
     return eof ? traits_type::eof() : c; 
    } 
}; 

class stream : public std::ostream { 
    std::vector<std::ofstream> streams; 
    buf outputs; 
public: 
    stream(std::initializer_list<std::string> names) 
     : streams(names.begin(), names.end()), 
      outputs(streams), 
      std::ostream(&outputs) 
    { } 
    void attach(std::ostream &b) { 
     outputs.attach(b.rdbuf()); 
    } 
}; 
} 

int main() { 
    multi::stream icl({"c:\\out1.txt", "c:\\out2.txt"}); 
    icl.attach(std::cout); 

    icl << "Blah blah blah" << std::endl; 
} 

正如你所看到的,這已經接受操縱(這應該與任何機械手的工作,而不僅僅是std::endl)。如果你想要寫入多個文件(可以/可以作爲fstreams打開的東西),你可以在構造函數中指定儘可能多的這些名稱(當然,在你的系統限制的範圍內)。對於像std::coutstd::cerr這樣的文件,您不一定有文件名,您可以按照最初的設計使用attach

我想我應該補充一點,我並不完全滿意於此。這需要做一些相當嚴肅的重寫,但經過一番思考,我認爲「正確」的方式可能會讓multi::stream的ctor成爲一個可變參數模板,所以您可以執行如下操作: multi::stream icl("c:\\out1.txt", std::cout);,它會整理出如何根據其類型使用每個參數。我可能會很快更新這個答案以包含那個功能。

至於第二個問題,我寫了another answer,涵蓋了基本的想法,但可能有點過於詳細,所以你關心的部分可能會迷失在洗牌,可以這麼說 - - 處理不真正關心的行長度有相當多的邏輯(但是會像你想的那樣產生具有指定前綴的每個輸出行)。

+0

我不得不接受這個答案。非常精細,真的讓我很想去。非常感謝!PS:如果你在某個時候更新答案,請告訴我。我很好奇,看看如何工作。 – 2013-04-22 11:10:59

+0

雖然有一個問題。我不能在這個項目中使用C++ 11,所以相反,我使用boost/foreach.hpp作爲你的foreach循環。但是,我不能這樣做:'法師:: IO :: MultiStream ICL({「Mage3D.log」,「Game.log」});',什麼給?在C++ 11之前不是'std :: initializer_list'的一部分嗎? – 2013-04-22 11:31:38

+0

@JesseBrands:對不起。 'std :: initializer_list'在C++ 11中也是新的。 – 2013-04-22 12:57:36

4

您可能需要像這樣:

class OutputMan { 
    std::vector<std::ostream*> m_Streams; 

public: 
    void attach(std::ostream *os) { 
     m_Streams.push_back(os); 
    } 

    template <typename T> 
    OutputMan &operator<<(const T &t) { 

     for (int i=0; i<m_Streams.size(); i++) 
      *m_Streams[i] << t; 

     return *this; 
    } 
}; 

int main() { 
    ofstream file("test.txt"); 

    OutputMan x; 
    x.attach(&cout); 
    x.attach(&cerr); 
    x.attach(&file); 

    x << "Hello" << 123; 
} 

爲簡單起見我用std::ostream*。接受<<的東西我超負荷operator<<

 

注:如果你想OutputMan接受std::endl以及其他的東西,讀here

+0

這就是我想要的。將文件輸出連接到這個文件也可以嗎?問題2仍然沒有答案,但這是至少向前邁出的一大步。 – 2013-04-21 20:15:10

+0

@JesseBrands:是的,有可能,再次看到我更新的答案。 – deepmax 2013-04-21 20:41:13

+0

非常感謝你,你一直在幫助很大。 – 2013-04-21 20:52:32