Je ne suis pas vraiment un débutant en C++, mais j’ai eu peu de relations sérieuses avec cela dans le passé, donc ma connaissance de ses installations est plutôt sommaire.
J'écris un programme de validation de concept rapide en C++ et j'ai besoin d'un tampon de données binaires de taille dynamique. C'est-à-dire que je vais recevoir des données d'une prise réseau et je ne sais pas combien il y en aura (mais pas plus de quelques Mo). Je pourrais écrire un tel tampon moi-même, mais pourquoi ne pas déranger si la bibliothèque standard a probablement déjà quelque chose? J'utilise VS2008, donc une extension spécifique à Microsoft me convient parfaitement. Je n'ai besoin que de quatre opérations:
Quel est le nom de la classe/fonction/quel que soit ce dont j'ai besoin?
Ajouté: Plusieurs votes vont à std::vector
. Tout va bien, mais je ne veux pas pousser plusieurs Mo de données octet par octet. Le socket me fournira des données en gros morceaux de quelques ko. J'aimerais donc les écrire tous en même temps. De plus, à la fin, j'aurai besoin de récupérer les données sous forme d'un simple caractère *, car je devrai transmettre l'intégralité du blob à certaines fonctions de l'API Win32 non modifiées.
Vous voulez un std::vector
:
std::vector<char> myData;
vector
allouera et désallouera automatiquement sa mémoire pour vous. Utilisez Push_back
pour ajouter de nouvelles données (vector
se redimensionnera si nécessaire) et l'opérateur d'indexation []
pour récupérer les données.
Si, à un moment quelconque, vous pouvez deviner la quantité de mémoire dont vous aurez besoin, je suggère d'appeler reserve
afin que les Push_back
suivants ne soient pas obligés de redistribuer autant.
Si vous voulez lire une partie de la mémoire et l'ajouter à votre tampon, le plus simple serait probablement quelque chose comme:
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
a maintenant toutes les données lues, lisant morceau par morceau. Cependant, nous copions deux fois.
Nous essayons plutôt quelque chose comme ceci:
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;
}
}
Ce qui se lit directement dans la mémoire tampon, au prix d’une surallocation occasionnelle.
Cela peut être rendu plus intelligent, par exemple. doubler la taille du vecteur pour chaque redimensionnement afin d'amortir les redimensionnements, comme le fait implicitement la première solution. Et bien sûr, vous pouvez reserve()
un tampon beaucoup plus grand à l’avance si vous connaissez a priori la taille probable du tampon final, afin de minimiser les redimensionnements.
Les deux sont laissés comme un exercice pour le lecteur. :)
Enfin, si vous devez traiter vos données comme un tableau brut:
some_c_function(myData.data(), myData.size());
std::vector
est garanti d'être contigu.
std::vector<unsigned char> buffer;
Chaque Push_back ajoutera un nouveau caractère à la fin (réallocation si nécessaire). Vous pouvez appeler réserve pour réduire le nombre d’allocations si vous savez approximativement le volume de données que vous attendez.
buffer.reserve(1000000);
Si vous avez quelque chose comme ça:
unsigned char buffer[1000];
std::vector<unsigned char> vec(buffer, buffer + 1000);
std::string
fonctionnerait pour ceci:
append()
avec un pointeur et une longueur.data()
et la longueur actuelle en appelant size()
ou length()
.clear()
pour effacer son contenu sans le détruire.Encore un vote pour std :: vector. Code minimal, ignore la copie supplémentaire du code de 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);
En ce qui concerne l’appel de fonctions WinAPI - utilisez & buffer [0] (oui, c’est un peu maladroit, mais c’est comme ça) pour passer aux arguments char *, buffer.size () comme longueur.
Et une note finale, vous pouvez utiliser std :: string au lieu de std :: vector, il ne devrait y avoir aucune différence (sauf que vous pouvez écrire buffer.data () au lieu de & buffer [0] si votre tampon est une chaîne)
Je voudrais jeter un oeil à Boost basic_streambuf , qui est conçu pour ce genre de but. Si vous ne pouvez pas (ou ne voulez pas) utiliser Boost, je considérerais std::basic_streambuf
, qui est assez similaire, mais qui demande un peu plus de travail. Quoi qu'il en soit, vous dérivez essentiellement de cette classe de base et surchargez underflow()
pour lire les données du socket dans la mémoire tampon. Normalement, vous attacherez un std::istream
à la mémoire tampon, de sorte que les autres codes se liront de la même manière que les entrées utilisateur au clavier (ou peu importe).
Une alternative qui ne provient pas de STL mais qui pourrait être utile - Boost.Circular buffer
Utilisez std :: vector , un tableau en pleine croissance garantissant que le stockage est contigu (votre troisième point).
En ce qui concerne votre commentaire "Je ne vois pas un append ()", ineserting à la fin est la même chose.
vec.insert (vec.end,
Si vous utilisez std :: vector, vous ne l'utilisez que pour gérer la mémoire brute pour vous. Vous pouvez simplement malloc
le plus grand tampon dont vous pensez avoir besoin, et garder une trace du décalage d'écriture/nombre total d'octets lus jusqu'à présent (c'est la même chose). Si vous arrivez à la fin ... soit realloc
ou choisissez un moyen d'échouer.
Je sais, ce n’est pas très C++ y, mais c’est un problème simple et les autres propositions semblent être des moyens assez lourds d’introduire une copie inutile.