33

次のようにクラスを定義したいMyStream

MyStream myStream;
myStream << 1 << 2 << 3 << std::endl << 5 << 6 << std::endl << 7 << 8 << std::endl;

出力を与える

[blah]123
[blah]56
[blah]78

基本的に、「[何とか]」を先頭に挿入し、終了 std::endlしないたびに挿入したいですか?

ここでの問題はロジック管理ではなく、 の処理の検出と過負荷ですstd::endl。これを行うエレガントな方法はありますか?

ありがとう!

編集: ロジック管理に関するアドバイスは必要ありません。の印刷を検出/過負荷にする方法を知る必要がありstd::endlます。

4

7 に答える 7

34

必要なことは、独自のストリーム バッファーを作成することです。ストリーム バッファーがフラッシュされると、プレフィックス文字とストリームのコンテンツが出力されます。

以下std::endlは、以下を引き起こすため動作します。

  1. '\n'ストリームに追加します。

  2. flush()ストリームでの呼び出し

  3. これはpubsync()、ストリーム バッファを呼び出します。

    1. これは仮想メソッドを呼び出しますsync()
    2. この仮想メソッドをオーバーライドして、必要な作業を行います。
#include <iostream>
#include <sstream>

class MyStream: public std::ostream
{
    // Write a stream buffer that prefixes each line with Plop
    class MyStreamBuf: public std::stringbuf
    {
        std::ostream&   output;
        public:
            MyStreamBuf(std::ostream& str)
                :output(str)
            {}
            ~MyStreamBuf() {
                if (pbase() != pptr()) {
                    putOutput();
                }
            }
   
        // When we sync the stream with the output. 
        // 1) Output Plop then the buffer
        // 2) Reset the buffer
        // 3) flush the actual output stream we are using.
        virtual int sync() {
            putOutput();
            return 0;
        }
        void putOutput() {
            // Called by destructor.
            // destructor can not call virtual methods.
            output << "[blah]" << str();
            str("");
            output.flush();
        }
    };

    // My Stream just uses a version of my special buffer
    MyStreamBuf buffer;
    public:
        MyStream(std::ostream& str)
            :std::ostream(&buffer)
            ,buffer(str)
        {
        }
};


int main()
{
    MyStream myStream(std::cout);
    myStream << 1 << 2 << 3 << std::endl << 5 << 6 << std::endl << 7 << 8 << std::endl;
}
> ./a.out
[blah]123 
[blah]56 
[blah]78
>
于 2010-02-06T11:38:14.330 に答える
18

クラスのオーバーロードされた演算子はMyStream、previous-printed-token-was-endl フラグを設定する必要があります。

次に、次のオブジェクトが印刷される場合は、その[blah]前に を挿入できます。

std::endlへの参照を受け取って返す関数std::ostreamです。ストリームにシフトされたことを検出するにはoperator<<、タイプとそのような関数の間でオーバーロードする必要があります。

MyStream& operator<<( std::ostream&(*f)(std::ostream&) )
{
    std::cout << f;

    if( f == std::endl )
    {
        _lastTokenWasEndl = true;
    }

    return *this;
}
于 2010-02-06T10:20:01.627 に答える
2

原則としてニールに同意。

iostream を拡張する唯一の方法であるため、バッファの動作を変更したいと考えています。endlこれを行います:

flush(__os.put(__os.widen('\n')));

widen単一の文字を返すため、そこに文字列を入れることはできません。putこれはputc仮想関数ではなく、たまにしかフックしませんoverflowflushバッファの を呼び出すat をインターセプトできますsyncoverflowすべての改行文字を編集または手動でsync編集して文字列に変換するときに、すべての改行文字を傍受して変更する必要があります。

basic_streambufはバッファ メモリへの直接アクセスを想定しているため、オーバーライド バッファ クラスの設計は面倒です。これにより、I/O 要求を既存の に簡単に渡すことができなくなりますbasic_streambuf。手足に出て、ストリーム バッファー クラスを知っていると仮定し、そこから派生させる必要があります。( cinandは、私が知る限り、cout使用が保証されているわけではありません。) 次に、 and を追加するだけです。(§27.5.2.4.5/3 および 27.5.2.4.2/7 を参照してください。) 置換を実行するには、追加のスペースが必要になる場合があるため、事前に割り当てるように注意してください。basic_filebufvirtual overflowsync

- また -

endl自分の名前空間でnew を宣言するか、まったく呼び出さendlれないマニピュレータを宣言するだけです!

于 2010-02-06T11:30:02.573 に答える
1

の動作を変更しようとする代わりに、std::endlおそらくフィルター処理用の streambuf を作成して、ジョブを実行する必要があります。James Kanze には、各出力行の先頭にタイムスタンプを挿入する方法を示す例があります。各行で必要な接頭辞に変更するには、わずかな変更のみが必要です。

于 2010-02-06T16:36:06.183 に答える
1

関数ポインタを使用します。C に慣れていない人にとっては恐ろしく聞こえるかもしれませんが、ほとんどの場合、はるかに効率的です。次に例を示します。

#include <iostream>

class Foo
{
public:
    Foo& operator<<(const char* str) { std::cout << str; return *this; }
    // If your compiler allows it, you can omit the "fun" from *fun below.  It'll make it an anonymous parameter, though...
    Foo& operator<<(std::ostream& (*fun)(std::ostream&)) { std::cout << std::endl; }
} foo;

int main(int argc,char **argv)
{
    foo << "This is a test!" << std::endl;
    return 0;
}

本当にしたい場合は、 endl のアドレスをチェックして、その他の void/void 関数を取得していないことを確認できますが、ほとんどの場合、それは価値があるとは思いません。それが役立つことを願っています。

于 2012-04-07T23:54:13.693 に答える
0

変更することはできませんstd::endl。その名前が示すように、C++ 標準ライブラリの一部であり、その動作は固定されています。end of line を受け取ったとき、ストリーム自体の動作を変更する必要があります。個人的には、これは努力する価値があるとは思いませんでしたが、この分野に足を踏み入れたい場合は、Standard C++ IOStreams & Localesという本を読むことを強くお勧めします。

于 2010-02-06T10:27:13.017 に答える