2010-05-06 8 views
15

Je suis en train d'encapsuler la fonctionnalité de socket linux de l'espace utilisateur dans du C++ pour un système embarqué (oui, cela réinvente probablement la roue). Je veux offrir une implémentation en lecture et en écriture en utilisant un vecteur.Utilisation de read() directement dans un C++ std: vector

Faire l'écriture est assez facile, je peux simplement passer &myvec[0] et éviter la copie inutile. Je voudrais faire la même chose et lire directement dans un vecteur, plutôt que de lire dans un tampon char puis de copier tout cela dans un vecteur nouvellement créé.

Maintenant, je sais combien de données je veux lire, et je peux allouer de manière appropriée (vec.reserve()). Je peux également lire dans &myvec[0], bien que ce soit probablement une idée très mauvaise. Évidemment, cela ne permet pas à myvec.size de renvoyer quoi que ce soit de sensé. Est-il possible de faire ce que:

  1. ne se sent pas tout à fait dégueu d'une sécurité/C++ perspective
  2. ne comporte pas deux copies du bloc de données - une fois de noyau vers l'espace utilisateur et une fois de un tampon de style C char * dans un vecteur C++.
+0

Un vecteur de quoi? –

+0

Un vecteur de caractères (bien, octets)! – Joe

+0

Vous voulez probablement utiliser 'uint8_t' ou' unsigned char' pour les octets. – tzaman

Répondre

22

Utilisez resize() au lieu de reserve(). Cela va régler la taille du vecteur correctement - et après cela, &myvec[0] est, comme d'habitude, garanti pour pointer vers un bloc de mémoire contingente. Edit: L'utilisation de &myvec[0] en tant que pointeur sur le tableau sous-jacent pour la lecture et l'écriture est sûre et garantie par le standard C++. Voici ce que Herb Sutter has to say:

Alors pourquoi les gens demandent constamment si les éléments d'un std :: vecteur (ou std :: tableau) sont stockés jointive? La raison la plus probable est qu'ils veulent savoir s'ils peuvent cracher des pointeurs vers les internes pour partager les données, soit pour lire, soit pour écrire, avec d'autres codes qui traitent les tableaux C. C'est une utilisation valide, et une assez importante pour garantir dans la norme.

+0

C'est Peachy, et répond certainement au point 2 (testé et fonctionne aussi, merci! :-)), mais cette approche est-elle sûre et propre, ou existe-t-il un autre moyen? Je ne peux pas m'empêcher de penser que passer l'adresse du bloc de mémoire contigu à écrire semble dangereux? – Joe

+0

@ Joe: Dans les mots de Herb Sutter: "Cringe pas" - c'est parfaitement sûr. Voir mon édition ci-dessus et le lien vers l'entrée du blog de Herb Sutter. –

+0

Excellent! Merci :-) Comme toujours stackoverflow.com livre! – Joe

1

En supposant que c'est une structure POD, appelez resize plutôt que reserve. Vous pouvez définir un constructeur par défaut vide si vous ne voulez vraiment pas que les données soient supprimées avant de remplir le vecteur.

C'est un peu bas niveau, mais la sémantique de la construction des structures POD est délibérément trouble. Si memmove est autorisé à copier-construire, je ne vois pas pourquoi une lecture socket ne devrait pas.

EDIT: ah, octets, pas un struct. Eh bien, vous pouvez utiliser la même astuce, et définir une structure avec juste un char et un constructeur par défaut qui néglige de l'initialiser ... si je devine correctement que vous vous souciez, et c'est pourquoi vous vouliez appeler reserve au lieu de resize dans le première place.

1

Si vous souhaitez que le vecteur reflète la quantité de données lues, appelez le resize() deux fois. Une fois avant la lecture, donnez-vous de l'espace pour lire. Encore une fois après la lecture, pour définir la taille du vecteur au nombre d'octets réellement lus. reserve() n'est pas bon, car l'appel de réserve ne vous donne pas l'autorisation d'accéder à la mémoire allouée pour la capacité.

Le premier resize() mettra à zéro les éléments du vecteur, mais il est peu probable que cela génère une surcharge de performances. Si c'est le cas, vous pouvez essayer la suggestion de Potatoswatter, ou vous pouvez abandonner la taille du vecteur reflétant la taille des données lues, et juste resize() une fois, puis réutilisez-le exactement comme vous le feriez pour un tampon alloué en C Du point de vue des performances, si vous lisez depuis un socket en mode utilisateur, vous pouvez facilement gérer les données aussi rapidement que possible. Peut-être pas si vous vous connectez à une autre machine sur un LAN gigabit, ou si votre machine exécute fréquemment 100% CPU ou 100% de bande passante mémoire. Un peu de copie supplémentaire ou memsetting n'est pas un gros problème si vous allez éventuellement bloquer un appel read de toute façon.

Comme vous, je voudrais éviter la copie supplémentaire dans l'espace utilisateur, mais pas pour des raisons de performance, juste parce que si je ne le fais pas, je n'ai pas besoin d'écrire le code pour cela.

+0

Normalement, je ne m'inquièterais pas vraiment de la copie supplémentaire, mais je suis sur un système en temps réel avec peu de ressources. L'ensemble du processus de lecture/écriture est non-bloquant. Bien sûr, cela conduit évidemment à des questions sur les conteneurs STL dans un système en temps réel par rapport à l'allocation de mémoire statique, etc. – Joe

1

Je vais juste ajouter une petite précision, parce que la réponse était déjà donnée. resize() avec un argument supérieur à la taille actuelle ajoute des éléments à la collection et les initialise. Si vous créez

std::vector<unsigned char> v; 

puis redimensionnez

v.resize(someSize); 

Tous les caractères non signés seront initialisées à 0. BTW Vous pouvez faire la même chose avec un constructeur

std::vector<unsigned char> v(someSize); 

Ainsi, il peut théoriquement être un peu plus lent qu'un tableau brut, mais si l'alternative est de copier le tableau de toute façon, c'est mieux. La réserve ne prépare que la mémoire, de sorte qu'il n'y a pas de réaffectation nécessaire si de nouveaux éléments sont ajoutés à la collection, mais vous ne pouvez pas accéder à cette mémoire.

Vous devez obtenir une information sur le nombre d'éléments écrits sur votre vecteur. Le vecteur n'en saura rien.

Questions connexes