私はC++の初心者ではありませんが、これまでに真剣に取り扱ったことはほとんどないため、その機能に関する私の知識はかなり大雑把です。
私はC++で概念実証プログラムをすばやく作成しており、動的にサイズ変更可能なバイナリデータのバッファが必要です。つまり、ネットワークソケットからデータを受信しますが、データ量はわかりません(ただし、数MBを超えません)。私はそのようなバッファを自分で書くことができましたが、標準ライブラリにおそらくすでに何かがあるのに、なぜ気になるのでしょうか?私はVS2008を使用しているので、Microsoft固有の拡張機能で十分です。必要な操作は4つだけです。
クラス/関数セット/必要なものの名前は何ですか?
追加:数票がstd::vector
。すべて順調ですが、数MBのデータを1バイトずつプッシュしたくありません。ソケットは、データを数KBの大きなチャンクで提供するため、一度にすべて書き込みたいと思います。また、ブロブ全体を一部のWin32 API関数に変更せずに渡す必要があるため、最後に単純なchar *としてデータを取得する必要があります。
_std::vector
_ が必要です:
_std::vector<char> myData;
_
vector
は自動的にメモリの割り当てと割り当て解除を行います。 _Push_back
_を使用して新しいデータを追加し(vector
は必要に応じてサイズが変更されます)、インデックス演算子_[]
_を使用してデータを取得します。
いつでも必要なメモリ量を推測できる場合は、reserve
を呼び出すことをお勧めします。これにより、後続の_Push_back
_がそれほど再割り当てする必要がなくなります。
メモリのチャンクを読み取り、それをバッファに追加する場合、おそらく最も簡単なものは次のようになります。
_std::vector<char> myData;
for (;;) {
const int BufferSize = 1024;
char rawBuffer[BufferSize];
const unsigned bytesRead = get_network_data(rawBuffer, sizeof(rawBuffer));
if (bytesRead <= 0) {
break;
}
myData.insert(myData.end(), rawBuffer, rawBuffer + bytesRead);
}
_
myData
にすべての読み取りデータが含まれ、チャンクごとに読み取られます。ただし、2回コピーしています。
代わりに、次のようなことを試みます。
_std::vector<char> myData;
for (;;) {
const int BufferSize = 1024;
const size_t oldSize = myData.size();
myData.resize(myData.size() + BufferSize);
const unsigned bytesRead = get_network_data(&myData[oldSize], BufferSize);
myData.resize(oldSize + bytesRead);
if (bytesRead == 0) {
break;
}
}
_
時々オーバーアロケーションを犠牲にして、バッファーに直接読み込みます。
これは、例えば、よりスマートにすることができます。最初のソリューションが暗黙的に行うように、各サイズ変更のベクトルサイズを2倍にすると、サイズ変更してサイズ変更されます。そしてもちろん、サイズ変更を最小限に抑えるために、最終的なバッファの推定サイズについて事前に知識がある場合は、事前にはるかに大きなバッファをreserve()
することができます。
どちらも読者のための演習として残されています。 :)
最後に、データをraw配列として扱う必要がある場合:
_some_c_function(myData.data(), myData.size());
_
_std::vector
_は連続していることが保証されています。
std::vector<unsigned char> buffer;
すべてのPush_backは最後に新しい文字を追加します(必要に応じて再割り当てします)。予想されるデータ量がおおよそわかっている場合は、reserveを呼び出して、割り当ての数を最小限に抑えることができます。
buffer.reserve(1000000);
次のようなものがあれば:
unsigned char buffer[1000];
std::vector<unsigned char> vec(buffer, buffer + 1000);
_std::string
_はこのために機能します:
append()
を呼び出すことにより、マルチバイトのデータチャンクを追加できます。data()
を呼び出すことでその内容をchar配列として取得でき、size()
またはlength()
を呼び出すことで現在の長さを取得できます。clear()
を呼び出して、バッファを破棄せずに内容を消去することもできます。Std :: vectorに対するもう1つの投票。最小限のコード、GManのコードの余分なコピーをスキップします。
std::vector<char> buffer;
static const size_t MaxBytesPerRecv = 1024;
size_t bytesRead;
do
{
const size_t oldSize = buffer.size();
buffer.resize(oldSize + MaxBytesPerRecv);
bytesRead = receive(&buffer[oldSize], MaxBytesPerRecv); // pseudo, as is the case with winsock recv() functions, they get a buffer and maximum bytes to write to the buffer
myData.resize(oldSize + bytesRead); // shrink the vector, this is practically no-op - it only modifies the internal size, no data is moved/freed
} while (bytesRead > 0);
WinAPI関数の呼び出しについては、&buffer [0]を使用します(そうです、少し扱いにくいですが、そうです)char *引数に、長さとしてbuffer.size()を渡します。
最後に、std :: vectorの代わりにstd :: stringを使用できます。違いはありません(バッファが文字列の場合、&buffer [0]の代わりにbuffer.data()を書き込むことができます)。
この種の目的のために設計されたBoost basic_streambuf を見てみましょう。 Boostを使用できない(または使用したくない)場合は、_std::basic_streambuf
_を検討します。これはよく似ていますが、使用する作業が少し増えます。どちらの方法でも、基本的にはその基本クラスから派生し、underflow()
をオーバーロードして、ソケットからバッファーにデータを読み取ります。通常は_std::istream
_をバッファーにアタッチするので、他のコードはキーボード(またはその他)からのユーザー入力とほぼ同じ方法でバッファーから読み取ります。
STLからではないが、役に立つかもしれない代替案- Boost.Circular buffer
std :: vector を使用します。これは、ストレージが連続していることを保証する成長する配列です(3番目のポイント)。
「append()が表示されません」というコメントについては、最後に挿入するのも同じです。
vec.insert(vec.end、
Std :: vectorを使用する場合、生のメモリを管理するために使用しているだけです。必要だと思う最大のバッファをmalloc
して、これまでに読み取られた書き込みオフセット/合計バイト数を追跡することもできます(同じことです)。最後までたどり着くなら... realloc
か、失敗する方法を選択してください。
私は知っている、それは非常にC++ yではないが、これは単純な問題であり、他の提案は不必要なコピーを導入する重い方法のように思われる。