web-dev-qa-db-ja.com

C ++の動的バッファタイプ?

私はC++の初心者ではありませんが、これまでに真剣に取り扱ったことはほとんどないため、その機能に関する私の知識はかなり大雑把です。

私はC++で概念実証プログラムをすばやく作成しており、動的にサイズ変更可能なバイナリデータのバッファが必要です。つまり、ネットワークソケットからデータを受信しますが、データ量はわかりません(ただし、数MBを超えません)。私はそのようなバッファを自分で書くことができましたが、標準ライブラリにおそらくすでに何かがあるのに、なぜ気になるのでしょうか?私はVS2008を使用しているので、Microsoft固有の拡張機能で十分です。必要な操作は4つだけです。

  • バッファを作成する
  • バッファにデータを書き込む(ゼロで終了しないバイナリジャンク)
  • 書き込まれたデータを(その長さとともに)char配列として取得します。
  • バッファを解放する

クラス/関数セット/必要なものの名前は何ですか?

追加:数票がstd::vector。すべて順調ですが、数MBのデータを1バイトずつプッシュしたくありません。ソケットは、データを数KBの大きなチャンクで提供するため、一度にすべて書き込みたいと思います。また、ブロブ全体を一部のWin32 API関数に変更せずに渡す必要があるため、最後に単純なchar *としてデータを取得する必要があります。

24
Vilx-

_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_は連続していることが保証されています。

39
GManNickG
std::vector<unsigned char> buffer;

すべてのPush_backは最後に新しい文字を追加します(必要に応じて再割り当てします)。予想されるデータ量がおおよそわかっている場合は、reserveを呼び出して、割り当ての数を最小限に抑えることができます。

buffer.reserve(1000000);

次のようなものがあれば:

unsigned char buffer[1000];
std::vector<unsigned char> vec(buffer, buffer + 1000);
9

_std::string_はこのために機能します:

  • 埋め込まれたヌルをサポートします。
  • ポインタと長さを指定してappend()を呼び出すことにより、マルチバイトのデータチャンクを追加できます。
  • data()を呼び出すことでその内容をchar配列として取得でき、size()またはlength()を呼び出すことで現在の長さを取得できます。
  • バッファの解放はデストラクタによって自動的に処理されますが、clear()を呼び出して、バッファを破棄せずに内容を消去することもできます。
7
Wyzard

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()を書き込むことができます)。

6
sbk

この種の目的のために設計されたBoost basic_streambuf を見てみましょう。 Boostを使用できない(または使用したくない)場合は、_std::basic_streambuf_を検討します。これはよく似ていますが、使用する作業が少し増えます。どちらの方法でも、基本的にはその基本クラスから派生し、underflow()をオーバーロードして、ソケットからバッファーにデータを読み取ります。通常は_std::istream_をバッファーにアタッチするので、他のコードはキーボード(またはその他)からのユーザー入力とほぼ同じ方法でバッファーから読み取ります。

4
Jerry Coffin

STLからではないが、役に立つかもしれない代替案- Boost.Circular buffer

2
Sergei Kurenkov

std :: vector を使用します。これは、ストレージが連続していることを保証する成長する配列です(3番目のポイント)。

1
Xavier Nodet

「append()が表示されません」というコメントについては、最後に挿入するのも同じです。

vec.insert(vec.end、

0

Std :: vectorを使用する場合、生のメモリを管理するために使用しているだけです。必要だと思う最大のバッファをmallocして、これまでに読み取られた書き込みオフセット/合計バイト数を追跡​​することもできます(同じことです)。最後までたどり着くなら... reallocか、失敗する方法を選択してください。

私は知っている、それは非常にC++ yではないが、これは単純な問題であり、他の提案は不必要なコピーを導入する重い方法のように思われる。

0
Useless