2009-10-19 4 views
9

Ho un client di rete con un metodo di richiesta che accetta uno std::streambuf*. Questo metodo è implementato da boost::iostreams::copy inserendolo in una classe personalizzata di std::streambuf che sa come scrivere i dati su un'API di rete, che funziona perfettamente. Ciò significa che posso eseguire il flusso di un file nella richiesta senza che sia necessario leggerlo tutto in memoria.Stream da std :: string senza fare una copia?

Ci sono alcuni casi, tuttavia, in cui devono essere inviati grandi blocchi di dati che non sono in un file, quindi ho incluso un sovraccarico che accetta una stringa. Per evitare la duplicazione di tutto il codice di rete nel flusso, è sembrato ovvio che dovrei impostare uno streambuf che rappresenta la stringa e chiamare l'altro metodo. L'unico modo che ho potuto capire per fare questo lavoro è stato qualcosa di simile:

std::istringstream ss(data); 
send(ss.rdbuf()); 

Purtroppo, istringstream fa una copia dei dati, che in alcuni casi è di diversi megabyte. Ha perfettamente senso nel caso generale, naturalmente, se si consegna un riferimento const a un oggetto, non si vuole che l'oggetto, supponendolo, possa continuare ad usare quel riferimento.

ho lavorato intorno a questo con il seguente:

struct zerocopy_istringbuf 
    : public std::stringbuf 
{ 
    zerocopy_istringbuf(std::string const* s) 
     : std::stringbuf(std::ios::in) 
    { 
     char* p = const_cast<char*>(s->c_str()); 
     setg(p, p, p + s->length()); 
    } 
}; 

... 

send(&zerocopy_istringbuf(data)); 

Questo sembra funzionare bene, ma mi chiedo se è veramente necessario. Perché lo std::istringstream non ha un sovraccarico che prende uno std::string const *? C'è un modo migliore per farlo?

risposta

4

Il motivo per cui si verificano questi problemi è che std :: string non è particolarmente adatto a ciò che si sta facendo. Un'idea migliore è quella di utilizzare il vettore di char quando si passano dati grezzi. Se è possibile, cambierei tutto per usare il vettore, usando vector :: swap e riferimenti ai vettori come appropriati per eliminare tutta la tua copia. Se ti piacciono gli iostream/streambuf api, o se devi affrontare qualcosa che richiede uno streambuf, sarebbe banale creare il tuo streambuf che utilizza un vettore, come il tuo. Effettivamente farebbe la stessa cosa con gli stessi problemi elencati nelle altre risposte, ma non violerebbe il contratto della classe.

Altrimenti, penso che quello che hai è probabilmente il modo migliore per passare un istrings ovunque.

+0

Un'idea interessante. Una delle cose che mi piace dell'installazione esistente è che posso impostare un flusso di input o output personalizzato derivando da 'std :: stringbuf' e devo solo sostituire una o due semplici funzioni, underflow o' overflow' e ' sync', la classe base gestisce tutta la gestione del buffer per me. Sospetto che basare le cose su 'std :: vector' significherebbe che avrei bisogno di molto più codice. Potrei essere in grado di usare lo swap in ogni caso, anche se avrei bisogno che il chiamante "rinunci" alla stringa piuttosto che passare un riferimento costante. –

2

imho, la scelta migliore è una classe deprecata std :: strstream

+0

Grazie, non lo sapevo. L'unico problema è che vuole un 'char *' invece di 'const char *', ma è abbastanza facile da aggirare. –