2011-08-24 25 views
7

C++プログラムでは、stdin,stdout、およびstderrの3つのストリームがあります。これらをコンソールアプリケーションでオーバーライドし、フォームを使用するアプリケーションで使用できますか?C++ストリームをオーバーライド

たとえば、ある基底クラスではcout<< "..."がある場合、ビジュアル(Windowsフォームのような)に「リダイレクト」できますか?

+2

C++は "フォーム" を持っていません。 –

+3

いいえ、私はコンソールではなく視覚的なものを意味します – Bakudan

+0

通常、ストリーム動作をオーバーライドすることは、std :: streambufインターフェイスを実装することによって行われることに注意してください。それはむしろ複雑な作業なので、おそらく他の回答を使用するべきです。 – Basilevs

答えて

9

私は何をやって推薦することは、このような入出力ストリームをラップするクラスを持っている:

#include <iostream> 
#define LOG Log() 

class Log 
{ 
    public: 
     Log(){} 
     ~Log() 
     { 
     // Add an newline. 
     std::cout << std::endl; 
     } 


     template<typename T> 
     Log &operator << (const T &t) 
     { 
     std::cout << t; 
     return * this; 
     } 
}; 

その後、あなたはデータがどこに行く変更したい時はいつでも、あなただけのクラスの動作を変更します。ここ は、クラスを使用する方法である:

LOG << "Use this like an iostream."; 

[編集]ポテトたたきが示唆したように が、私はCOUT以外の何かに例を追加します:あなたは試してみてください理由については

#include <sstream> 
#define LOG Log() 

// An example with a string stream. 
class Log 
{ 
    private: 
     static std::stringstream buf; 
    public: 
     Log(){} 
     ~Log() 
     { 
     // Add an newline. 
     buf << std::endl; 
     } 


     template<typename T> 
     Log &operator << (const T &t) 
     { 
     buf << t; 
     return * this; 
     } 
}; 

// Define the static member, somewhere in an implementation file. 
std::stringstream Log::buf; 

これは主に文字列ストリームのようなものから継承するのではなく、主にLoggerが出力する場所を簡単に変更できるためです。たとえば、次の3つの異なる出力ストリームを持つことができ、実行時に間に交換するための静的メンバ変数を使用します。これは、簡単に伐採に対処する強力な方法を作成するために拡張することができ

class Log 
{ 
    private: 
     static int outputIndex = 0; 
     // Add a few static streams in here. 
     static std::stringstream bufOne; 
     static std::stringstream bufTwo; 
     static std::stringstream bufThree; 
    public: 
     // Constructor/ destructor goes here. 

     template<typename T> 
     Log &operator << (const T &t) 
     { 
     // Switch between different outputs. 
     switch (outputIndex) 
     { 
      case 1: 
       bufOne << t; 
       break; 
      case 2: 
       bufTwo << t; 
      case 3: 
       bufThree << t; 
      default: 
       std::cout << t; 
       break; 
     } 
     return * this; 
     } 

     static void setOutputIndex(int _outputIndex) 
     { 
      outputIndex = _outputIndex; 
     } 
}; 

// In use 
LOG << "Print to stream 1"; 
Log::setOutputIndex(2); 
LOG << "Print to stream 2"; 
Log::setOutputIndex(3); 
LOG << "Print to stream 3"; 
Log::setOutputIndex(0); 
LOG << "Print to cout"; 

。あなたは、ファイルストリームを追加し、何か他のものにiostreamインタフェースを置くためにはstd :: CERRなど

+1

私は(原則として)同意します。 –

+1

新しいostream派生クラスを作成して使用するほうが簡単ではないでしょうか? –

+0

これは基本的にゼロから始まります。例えば、既存のコードがフォーマットマニピュレータや 'endl'を使用する場合はどうでしょうか?また、この例では、 'cout'以外のものとのインターフェース方法については説明していません。 – Potatoswatter

1

通常の方法を使用し<sstream>stringstream/istringstream/ostringstreamクラスを使用することである可能性があります。

最良の方法は、タイプistream &ostream &のパラメータにcincoutなどの使用を変更することです。それが本当に不可能な場合は、cin.rdbuf()を修正したいストリームを指すように変更できますが、それは非常にハック的で、マルチスレッドでは機能しません。

フォームからistringstreamにテキストをコピーし、既存のコードにistream &として渡します。完了したらstringstreamの結果をostream &と置き換えてcoutに置き換えて、フォームにコピーします。

istringstream &またはの引数を宣言しないでください。基本クラスを使用します。

std::basic_stringbuf<char>をサブクラス化し、syncunderflow仮想関数をオーバーライドして、ストリームを直接テキストフィールドに結び付けることができます。しかしそれはかなり進歩しており、価値がない可能性が高いです。その後

struct STDOUT_BLOCK : SLIST_ENTRY 
{ 
    char sz[]; 
}; 

class capturebuf : public std::stringbuf 
{ 
protected: 
    virtual int sync() 
    { 
     if (g_threadUI && g_hwndProgressDialog) { 
      // ensure NUL termination 
      overflow(0); 
      // allocate space 
      STDOUT_BLOCK* pBlock = (STDOUT_BLOCK*)_aligned_malloc(sizeof *pBlock + pptr() - pbase(), MEMORY_ALLOCATION_ALIGNMENT); 
      // copy buffer into string 
      strcpy(pBlock->sz, pbase()); 
      // clear buffer 
      str(std::string()); 
      // queue string 
      ::InterlockedPushEntrySList(g_slistStdout, pBlock); 
      // kick to log window 
      ::PostMessageA(g_hwndProgressDialog, WM_APP, 0, 0); 
     } 
     return __super::sync(); 
    } 
}; 

main()内側:ここ

+1

'cin.rdbuf()'は変更しないでください。そして、それは「ハッキリ」ではなく、標準ライブラリiostreamがどのように使用されるように設計されているかです。 –

+0

@Ben:グローバル変数 'cin'の基になるバッファポインタをローカル変数を指すように変更して、呼び出された関数がグローバルを介してローカルにアクセスできるようにします。それは非常にハッキリです。 – Potatoswatter

+0

この場合も、標準のiostreamは 'streambuf'オブジェクトを置き換えるように設計されており、パブリックAPIを提供しています。これが、標準ライブラリiostreamsで多態性がどのように実装されるかです。 –

2

は、私はWindows上のGUIにstd::coutをリダイレクトするために使用するコードです。もちろん、

capturebuf altout; 
std::cout.set_rdbuf(&altout); 

、あなたは、あなたのウィンドウにWM_APPメッセージを処理する必要がありますSListから文字列を引き出します。しかし、これはcoutリダイレクト部分を処理します。

jweyrichが正しくメモしているように、streambuf*を元に戻してからaltoutが範囲外になる必要があります。このコードは、そうします:

struct scoped_cout_streambuf_association 
{ 
    std::streambuf* orig; 
    scoped_cout_streambuf_association(std::streambuf& buf) 
     : orig(std::cout.rdbuf()) 
    { 
     std::cout.rdbuf(&buf); 
    } 

    ~scoped_cout_streambuf_association() 
    { 
     std::cout.rdbuf(orig); 
    } 
}; 

そしてmain内側:

capturebuf altout; 
scoped_cout_streambuf_association redirect(altout); 
+0

終了する前に元のrdbufを復元することを忘れないでください。そうしないと、クラッシュする可能性があります。 – jweyrich

+0

'set_rdbuf'という関数はなく、' ios :: rdbuf'の2つのオーバーロードだけです。 – Potatoswatter

+0

@Potatoswatter:Visual C++にはこのコードがありますが、このコードはWindows固有のものですが、一般的な考え方はかなり移植性がありますが詳細はありません。 –

関連する問題