web-dev-qa-db-ja.com

cstdioストリームとiostreamストリーム?

ios_base::sync_with_stdio関数の存在を知りました。これにより、基本的に、C++で使用されるiostreamストリームとその一部であるcstdioストリーム間の同期をオフ(または既にオフにしている場合はオン)にすることができます。標準C。

さて、私はいつも、Cのstdoutstderr、およびstdinは、基本的にiostreamsクラスのC++のオブジェクトのセットにラップされていると思っていました。ただし、相互に同期する必要がある場合、これは、C++のiostreamクラスがnot Cのstdinなどのラッパーであることを示します。

私はこれにかなり混乱していますか?誰かがC++のiostreamとCのstdioがどのようになっているのかを明確にすることができますか異なる抽象化のレベルが異なるだけで、まったく同じことをしますか? 同じこと!?

それらを同期させる必要があるのはどうしてですか?私はいつもそれらは同じものだと思っていました。本質的に、一方が他方を包んでいます。

30
Tony The Lion

実際には、stdoutstderr、およびstdinはOSのファイルハンドラーです。また、CのFILE構造とC++のiostreamクラスは、どちらもこれらのファイルハンドラーのラッパーです。 iostreamクラスとFILE構造の両方に、ファイルからの入力またはファイルへの出力が正しく行われるようにするために相互に同期する必要がある独自のバッファーまたはその他のものがある場合があります。

3
Jurlie

さて、これが私が見つけたものです。

実際、I/Oは、最終的にはネイティブのシステムコールと関数によって実行されます。

ここで、MicrosoftWindowsを例にとってみましょう。 STDINSTDIOなどに実際に使用できるハンドルがあります( ここ を参照)。したがって、基本的に、C++ iostreamとCstdioはどちらもネイティブシステム関数を呼び出しますが、C++ iostreamはCのI/O関数をラップしません(最新の実装では)。ネイティブシステムメソッドを直接呼び出します。

また、私はこれを見つけました:

Stdin、stdout、およびstderrがリダイレクトされると、printf()やgets()などの標準C関数を変更せずに使用して、Win32コンソールと通信できます。しかし、C++ I/Oストリームはどうですか? cin、cout、cerr、およびclogは、Cのstdin、stdout、およびstderrと密接に関連しているため、同様に動作することが期待されます。これは半分正しいです。

C++ I/Oストリームには、実際にはテンプレートと非テンプレートの2つの種類があります。古い非テンプレートバージョンのI/Oストリームは、標準テンプレートライブラリ(STL)によって最初に定義され、現在ANSI C++標準に吸収されている新しいテンプレートスタイルのストリームに徐々に置き換えられています。 Visual C++ v5は両方のタイプを提供し、異なるヘッダーファイルをインクルードすることで2つから選択できます。 STL I/Oストリームは、新しくリダイレ​​クトされたstdioハンドルを自動的に使用して、期待どおりに機能します。ただし、非テンプレートI/Oストリームは期待どおりに機能しません。その理由を見つけるために、Visual C++ CD-ROMで便利に提供されているソースコードを調べました。

問題は、古いI/OストリームがUNIXスタイルの「ファイル記述子」を使用するように設計されていることです。ハンドルの代わりに整数が使用されます(stdinの場合は0、stdoutの場合は1など)。これはUNIX実装には便利ですが、Win32は互換性のある関数のセットを提供しないため、Win32CコンパイラはそのスタイルのI/Oを表すためにさらに別のI/O層を提供する必要があります。いずれの場合も、_open_osfhandle()を呼び出して新しいWin32ハンドルを(たとえば)stdoutに関連付けると、I/Oコードの他の層には影響しません。したがって、ファイル記述子1は、以前と同じ基礎となるWin32ハンドルを引き続き使用し、出力をcoutに送信しても目的の効果は得られません。

幸い、元のI/Oストリームパッケージの設計者はこの問題を予見し、クリーンで便利なソリューションを提供しました。基本クラスiosは、静的関数sync_with_stdio()を提供します。これにより、ライブラリは、標準I/O層での変更を反映するために、基になるファイル記述子を変更します。これはSTLI/Oストリームに厳密に必要というわけではありませんが、害はなく、新しい形式または古い形式のI/Oストリームで正しく機能するコードを記述できます。

ソース

したがって、sync_with_stdio()を呼び出すと、実際には基になるファイル記述子が変更されます。実際、古いC++ I/Oと、整数の代わりにハンドルを使用するWindows-32などのシステムとの互換性を確保するために設計者によって追加されました。

最新のC++テンプレートベースのSTLI/Oでは、sync_with_stdio()を使用する必要がないことに注意してください。

2

一方canもう一方のラッパーになります(そしてそれは両方の方法で機能します。あなたcouldstdioを使用してiostream関数を実装します。 。または、完全に独立して書くこともできます。

そしてsync_with_stdio有効になっている場合、2つのストリームが同期されることを保証します。ただし、本当に必要な場合は、無効にしても同期できます。

ただし、一方が他方のラッパーである場合でも、たとえば、一方が他方が共有しないバッファーを持っている可能性があるため、同期が必要です。

1
jalf

それらはare同じものですが、別々にバッファリングされる場合もあります。これは、このようにCとC++ I/Oの使用を混在させるコードに影響を与える可能性があります

_std::cout << "Hello ";
printf("%s", "world");
std::cout << "!\n";
_

これが機能するには、基になるストリームを何らかの方法で同期する必要があります。一部のシステムでは、これはmightパフォーマンスが低下する可能性があることを意味します。

したがって、この標準では、std::sync_with_stdio(false)を呼び出して、このようなコードは気にしないが、標準ストリームをできるだけ速く動作させることを望んでいると言うことができます違いが生じる場合)。多くのシステムでは、違いはありません。

1
Bo Persson