2009-04-21 7 views
25

私は周りにグーグルであり、私はちょうどこれに簡単な答えを見つけることができませんでした。 STLが一般的であるように、それは単純でなければなりません。std :: ostreamから継承する方法は?

std :: ostreamから公開されているMyOStreamを定義します。何かが私のストリームに書き込まれるたびにfoo()を呼びたいとしましょう。

class MyOStream : public ostream { 
public: 
    ... 
private: 
    void foo() { ... } 
} 

私はostreamのパブリックインターフェイスが非仮想であることを理解しています。どうすればできますか? クライアントは、MyOStreamに演算子< <とwrite()とput()の両方を使用できるようにして、自分のクラスの拡張機能を使用します。

+3

STLは単純かもしれませんが、それはC++標準ライブラリの一部に過ぎません。 iostreamsライブラリはSTLとは何の関係もありません(一度は何だったのでしょうか)。 STLは基本的にcontianers + iterators +アルゴリズムです。 Iostreams、ロケール、そして全く異なる起源を持つもの、そして一般的には動作する痛みです) – jalf

答えて

19

残念ながら、簡単な質問ではありません。派生クラスはbasic_クラスです(basic_ostreamなど)。しかし、ストリームからの派生は、あなたが望むものではないかもしれません。代わりにストリームバッファから派生し、このクラスを使用して既存のストリームクラスをインスタンス化することができます。

全体の領域は複雑ですが、詳細についてはStandard C++ IOStreams and Localesという本があります。詳細は先にお読みください。

+0

私はこの本のコピーを見に行くつもりでしたが、あなたは私のことを救ってくれました。 +1 – Evan

+0

@anonウェブ上のストリームへの参照が見つかりません。それは何ですか? –

0

構成、継承ではありません。あなたのクラスはostream &を含み、それを(foo()を呼び出した後に)転送します。

+0

これが既存の<<演算子とどのように機能するかを示すコードを投稿してください。また、このような演算子が使用されるたびにfoo()が呼び出されることに注意してください。 –

+6

継承がそうでないように、合成が必ずしも最良の解決法であるとは限りません。 ostreamには十数のオーバーロードされた演算子が実装されていますが、小さな関数をクラスに追加するだけで誰もが実際にostreamのすべてのパブリックインターフェイスを書き直すとは思わないでしょう。 – Michael

4

これが正しい解決策であるかどうかわかりませんが、私はこのようにstd :: ostreamから継承しました。これは、std :: basic_streambufから継承したバッファを使用し、一度に64文字を取得します(またはフラッシュされた場合は少なくなります)、実際のデータ処理が行われる汎用のputChars()メソッドにそれらを送信します。また、ユーザーデータを提供する方法も示します。

#include <streambuf> 
#include <ostream> 
#include <iostream> 

//#define DEBUG 

class MyData 
{ 
    //example data class, not used 
}; 

class MyBuffer : public std::basic_streambuf<char, std::char_traits<char> > 
{ 

public: 

    inline MyBuffer(MyData data) : 
    data(data) 
    { 
     setp(buf, buf + BUF_SIZE); 
    } 

protected: 

    // This is called when buffer becomes full. If 
    // buffer is not used, then this is called every 
    // time when characters are put to stream. 
    inline virtual int overflow(int c = Traits::eof()) 
    { 
#ifdef DEBUG 
     std::cout << "(over)"; 
#endif 
     // Handle output 
     putChars(pbase(), pptr()); 
     if (c != Traits::eof()) { 
      char c2 = c; 
      // Handle the one character that didn't fit to buffer 
      putChars(&c2, &c2 + 1); 
     } 
     // This tells that buffer is empty again 
     setp(buf, buf + BUF_SIZE); 

     return c; 
    } 

    // This function is called when stream is flushed, 
    // for example when std::endl is put to stream. 
    inline virtual int sync(void) 
    { 
     // Handle output 
     putChars(pbase(), pptr()); 
     // This tells that buffer is empty again 
     setp(buf, buf + BUF_SIZE); 
     return 0; 
    } 

private: 

    // For EOF detection 
    typedef std::char_traits<char> Traits; 

    // Work in buffer mode. It is also possible to work without buffer. 
    static const size_t BUF_SIZE = 64; 
    char buf[BUF_SIZE]; 

    // This is the example userdata 
    MyData data; 

    // In this function, the characters are parsed. 
    inline void putChars(const char* begin, const char* end){ 
#ifdef DEBUG 
     std::cout << "(putChars(" << static_cast<const void*>(begin) << 
      "," << static_cast<const void*>(end) << "))"; 
#endif 
     //just print to stdout for now 
     for (const char* c = begin; c < end; c++){ 
      std::cout << *c; 
     } 
    } 

}; 

class MyOStream : public std::basic_ostream< char, std::char_traits<char> > 
{ 

public: 

    inline MyOStream(MyData data) : 
    std::basic_ostream< char, std::char_traits<char> >(&buf), 
    buf(data) 
    { 
    } 

private: 

    MyBuffer buf; 

}; 

int main(void) 
{ 
    MyData data; 
    MyOStream o(data); 

    for (int i = 0; i < 8; i++) 
     o << "hello world! "; 

    o << std::endl; 

    return 0; 
} 
19

Live Example別の作業と同様の効果を達成するために、ハックは、私は同じことを行う方法のまわりで私の頭を回転させた

class LoggedStream { 
public: 
    LoggedStream(ostream& _out):out(_out){} 
    template<typename T> 
    const LoggedStream& operator<<(const T& v) const {log();out << v;return *this;} 
protected: 
    virtual void log() = 0; 
    ostream& out; 
}; 

class Logger : LoggedStream { 
    void log() { std::cerr << "Printing" << std::endl;} 
}; 

int main(int,char**) {LoggedStream(std::cout) << "log" << "Three" << "times";} 
+0

これは 'hex'や' endl'のようなものをサポートしていません – shoosh

+0

どうしてですか? 'T'は' std :: hex'の型を取得します。 –

+6

@ElazarLeibovich: 'std :: hex'では動作しますが、' std :: endl'や 'std :: flush'では動作しません。これは、Tを適切な関数型に解決できないためです。 'LoggedStream'に以下を追加すると、問題が解決されます:' LoggedStream const&operator <<(std :: ostream&(* F)(std :: ostream&))const {F(out);これを返す。 } ' –

12

テンプレートおよび組成物を使用することであると私はそれがだがわかりました実際にはそれほど難しくありません。 基本的には、ostreamとstreambufオブジェクトをサブクラス化し、ostreamをバッファとして構築します。ストリームに送信されるすべての文字に対してstd :: streambufからの仮想オーバーフロー()が呼び出されます。あなたの例に合うように私はちょうどfoo()関数を作り、それを呼び出しました。

struct Bar : std::ostream, std::streambuf 
{ 
    Bar() : std::ostream(this) {} 

    int overflow(int c) 
    { 
     foo(c); 
     return 0; 
    } 


    void foo(char c) 
    { 
     std::cout.put(c); 

    } 
}; 

void main() 
{ 
    Bar b; 
    b<<"Look a number: "<<std::hex<<29<<std::endl; 
} 

ああ、主な機能は本当の主な機能ではないという事実を無視してください。

+2

完璧に動作します!それは古い質問ですが、受け入れられた答えでなければなりません。 – ZXcvbnM

+0

私はZXcvbnMに同意します。これは受け入れられた回答でなければなりません。受け入れられた回答には有用な参照が含まれていますが、実際には解決策はありません。 Benは簡単な作業ソリューションを提供します。 +1。 – sgbirch

+0

最後に、私はそれを長年探してから解決策を見つけました。これは、人々が言っ​​たように受け入れられた答えでなければなりません。 – user0103

関連する問題