2010-05-06 11 views
15

Sto completando la funzionalità dello spazio utente linux in alcuni C++ per un sistema embedded (sì, è probabile che si reinvii nuovamente la ruota).Utilizzo di read() direttamente in uno std C++: vector

Voglio offrire un'implementazione di lettura e scrittura utilizzando un vettore.

Fare la scrittura è abbastanza semplice, posso solo passare &myvec[0] ed evitare copie non necessarie. Mi piacerebbe fare lo stesso e leggere direttamente in un vettore, piuttosto che leggere in un buffer di caratteri, quindi copiare tutto ciò in un vettore appena creato.

Ora, so quanti dati voglio leggere e posso allocare in modo appropriato (vec.reserve()). Posso anche leggere in &myvec[0], anche se questa è probabilmente una IDEA MOLTO CATTIVA. Ovviamente questo non consente a myvec.size di restituire qualcosa di sensato. C'è un modo di fare questo che:

  1. completamente non sentono schifo da una sicurezza/C++ prospettiva
  2. non comporta due copie del blocco di dati - una volta dal kernel per spazio utente e una volta da un buffer di stile C char * in un vettore C++.
+0

Un vettore di cosa? –

+0

Un vettore di caratteri (beh, byte)! – Joe

+0

Probabilmente vorrete usare 'uint8_t' o' unsigned char' per byte. – tzaman

risposta

22

Utilizzare resize() anziché reserve(). Questo imposterà correttamente la dimensione del vettore - e dopo questo, &myvec[0] è, come al solito, garantito per puntare a un blocco di memoria.

Modifica: l'utilizzo di &myvec[0] come puntatore all'array sottostante sia per la lettura che per la scrittura è sicuro e garantito dallo standard C++. Ecco cosa Herb Sutter has to say:

Allora perché la gente continuamente chiedere se gli elementi di uno std :: vector (o std :: array) vengono memorizzati in modo contiguo? La ragione più probabile è che vogliono sapere se riescono a tirar fuori i puntatori agli interni per condividere i dati, sia per leggere che per scrivere, con altro codice che si occupa di array C. Questo è un uso valido e abbastanza importante da garantire nello standard.

+0

Questo è peachy, e certamente risponde al punto 2 (testato e funziona anche, grazie! :-)), ma questo approccio è sicuro e pulito, o c'è un altro modo? Non posso fare a meno di pensare che passare l'indirizzo del blocco di memoria contiguo dei vettori per scriverlo sembra pericoloso? – Joe

+0

@Joe: Nelle parole di Herb Sutter: "Cringe not" - è perfettamente sicuro. Vedi la mia modifica sopra e il link al blog di Herb Sutter. –

+0

Eccellente! Grazie :-) Come sempre stackoverflow.com offre! – Joe

1

Supponendo che sia una struct POD, chiamare resize piuttosto che reserve. È possibile definire un costruttore predefinito vuoto se davvero non si desidera che i dati vengano azzerati prima di riempire il vettore.

È un livello piuttosto basso, ma la semantica della costruzione delle strutture POD è volutamente torbida. Se memmove è autorizzato a copiarli, non vedo perché una lettura da socket non dovrebbe.

MODIFICA: ah, byte, non una struttura. Bene, puoi usare lo stesso trucco e definire una struct con solo un char e un costruttore predefinito che trascura di inizializzarlo ... se sto indovinando correttamente che ti interessa, ed è per questo che volevi chiamare reserve invece di resize nel primo posto.

1

Se si desidera che il vettore rifletta la quantità di dati letti, chiamare resize() due volte. Una volta prima della lettura, per darti spazio per leggere. Ancora una volta dopo la lettura, per impostare la dimensione del vettore sul numero di byte effettivamente letti. reserve() non va bene, dato che chiamare la riserva non ti dà il permesso di accedere alla memoria allocata per la capacità.

Il primo resize() azzererà gli elementi del vettore, ma è improbabile che questo crei un sovraccarico di prestazioni. Se lo fa, potresti provare il suggerimento di Potatoswatter, oppure potresti rinunciare alla dimensione del vettore che riflette la dimensione dei dati letti, e invece solo a resize() una volta, quindi riutilizzarlo esattamente come si farebbe con un buffer allocato in C

Per quanto riguarda le prestazioni, se si sta leggendo da un socket in modalità utente, molto probabilmente si possono gestire facilmente i dati così velocemente come entrano. Forse non se ci si connette a un'altra macchina su una LAN gigabit, o se la macchina esegue frequentemente CPU al 100% o larghezza di banda di memoria del 100%. Un po 'di copia aggiuntiva o memsetting non è un grosso problema se alla fine si bloccherà comunque su una chiamata read.

Come te, vorrei evitare la copia extra nello spazio utente, ma non per motivi di prestazioni, solo perché se non lo faccio, non devo scrivere il codice per questo ..

+0

Normalmente non mi interesserebbe veramente la copia extra, ma sono su un sistema in tempo reale con risorse limitate. L'intero processo di lettura/scrittura è non bloccante. Ovviamente, ciò porta ovviamente a domande sui contenitori STL in un sistema in tempo reale rispetto all'allocazione di memoria statica ecc .... – Joe

1

Aggiungerò solo un breve chiarimento, perché la risposta è già stata fornita. ridimensiona() con argomento maggiore della dimensione corrente aggiungerà elementi alla raccolta e di default - inizializzali. Se si crea

std::vector<unsigned char> v; 

e quindi ridimensionare

v.resize(someSize); 

tutti i caratteri non firmati avranno inizializzato a 0. Btw Si può fare lo stesso con un costruttore

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

Quindi, in teoria, può essere un po 'più lento di un array raw, ma se l'alternativa è copiare l'array comunque, è meglio.

Prenota solo prepara la memoria, in modo che non sia necessaria alcuna riallocazione, se nuovi elementi vengono aggiunti alla raccolta, ma non è possibile accedere a tale memoria.

È necessario ottenere informazioni sul numero di elementi scritti nel vettore. Il vettore non ne saprà nulla.

Problemi correlati