web-dev-qa-db-ja.com

std :: ostreamから継承する方法は?

私はぐるぐる回っています、そして私はこれに対する簡単な答えを見つけることができません。そして、STLが一般的にそうであるように、それは単純でなければなりません。

Std :: ostreamからパブリックに継承するMyOStreamを定義したいと思います。ストリームに何かが書き込まれるたびにfoo()を呼び出したいとしましょう。

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

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

35
Michael

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

エリア全体は複雑ですが、それについての優れた本があります 標準C++ IOStreamsとロケール 、先に進む前に確認することをお勧めします。

23
anon

私は同じことをする方法について頭を回転させていました、そして私はそれが実際にはそれほど難しくないことを知りました。基本的には、ostreamオブジェクトとstreambufオブジェクトをサブクラス化し、それ自体をバッファーとしてostreamを構築します。 std :: streambufからの仮想overflow()は、ストリームに送信されるすべての文字に対して呼び出されます。あなたの例に合うように、私はfoo()関数を作成して呼び出しました。

    #include <iostream>

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

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


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

        }
    };

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

        return 0;
    }

EDIT:古いコードは間違った初期化順序を使用していました。目に見える副作用はありませんでしたが、streambufオブジェクトはostreamオブジェクトに渡す前に初期化する必要があります。 C++は親を左から右に初期化するので、コードを正しくするためにstd :: streambufを左に移動しました。

32
Ben

同様の効果を達成するためのもう1つの実用的なハックは、テンプレートと構成を使用することです。

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

これが正しい解決策かどうかはわかりませんが、この方法で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;
}
5
Henrik Heino

継承ではなく構成。クラスには、ostream&が含まれ、「ラップ」され、(foo()を呼び出した後に)転送されます。

0
tpdi