Поток из std::string без копирования?

У меня есть сетевой клиент с методом запроса, который принимает std::streambuf*. Этот метод реализован путем boost::iostreams::copy-переноса его в пользовательский класс, производный от std::streambuf, который знает, как записывать данные в сетевой API, что прекрасно работает. Это означает, что я могу передавать файл в запрос без необходимости считывать его все в память.

Однако в некоторых случаях необходимо отправлять большие блоки данных, которых нет в файле, поэтому я включил перегрузку, которая принимает строку. Чтобы избежать дублирования всего сетевого кода в потоке, казалось очевидным, что я должен установить streambuf, представляющий строку, и вызвать другой метод. Единственный способ, которым я мог понять, чтобы сделать эту работу, был примерно таким:

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

К сожалению, istringstream делает копию данных, которая в некоторых случаях составляет несколько мегабайт. Конечно, в общем случае это имеет смысл, если вы передаете константную ссылку на какой-то объект, который вам не нужен, при условии, что он может продолжать использовать эту ссылку.

Я работал над этим со следующим:

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));

Кажется, это работает просто отлично, но мне интересно, действительно ли это необходимо. Почему у std::istringstream нет перегрузки, принимающей std::string const *? Есть лучший способ сделать это?


person Tim Sylvester    schedule 19.10.2009    source источник


Ответы (2)


Причина, по которой у вас возникла эта проблема, заключается в том, что std::string не совсем подходит для того, что вы делаете. Лучшей идеей является использование вектора char при передаче необработанных данных. Если это возможно, я бы просто изменил все, чтобы использовать вектор, используя vector:: swap и ссылки на векторы, как уместно, чтобы исключить все ваше копирование. Если вам нравится API-интерфейс iostreams/streambuf или если вам приходится иметь дело с чем-то, что использует streambuf, было бы тривиально создать свой собственный streambuf, использующий вектор, как ваш. Это будет эффективно делать то же самое, что и вы, с теми же проблемами, что и в других ответах, но вы не будете нарушать контракт класса.

В противном случае, я думаю, что то, что у вас есть, вероятно, является лучшим способом продвижения вперед, за исключением передачи istringstream повсюду.

person Doug T.    schedule 19.10.2009
comment
Интересная идея. Одна из вещей, которые мне нравятся в существующей настройке, это то, что я могу настроить пользовательский поток ввода или вывода, производный от std::stringbuf, и мне нужно только переопределить одну или две простые функции, потеря значимости или overflow и sync, базовый класс обрабатывает все управление буфером для меня. Я подозреваю, что базирование вещей на std::vector означало бы, что мне потребуется значительно больше кода. В любом случае я мог бы использовать swap, хотя мне нужно было бы, чтобы вызывающая сторона отказалась от строки, а не передала постоянную ссылку. - person Tim Sylvester; 19.10.2009

imho, лучший выбор - устаревший класс std::strstream

person Stan Sage    schedule 07.07.2010
comment
Спасибо, я не знал об этом. Единственная проблема заключается в том, что он хочет char* вместо const char*, но это достаточно легко обойти. - person Tim Sylvester; 09.07.2010