不复制地从std::string中流式传输?

10

我有一个网络客户端,其中一个请求方法需要传入一个 std::streambuf*。这个方法是通过将其复制到一个自定义的 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 *参数的函数?有更好的方法来做到这一点吗?

2个回答

4
您遇到这些问题的原因是std :: string并不适合您所做的事情。更好的想法是在传递原始数据时使用char向量。如果可能的话,我会将所有内容都更改为使用向量,使用vector :: swap和向量引用作为适当的操作来消除所有副本。如果您喜欢iostreams / streambuf api,或者必须处理需要streambuf的内容,则可以轻松创建自己的streambuf,该streambuf使用一个像您的向量的向量。它实际上会执行与其他答案中列出的相同问题相同的操作,但您不会违反类的约定。
否则,我认为您所拥有的东西可能是在无处不在地传递istringstream的最佳方法。

一个有趣的想法。我喜欢现有设置的一件事是,我可以通过从std::stringbuf派生来设置自定义输入或输出流,并且我只需要覆盖一个或两个简单的函数,即underflowoverflowsync,基类会为我处理所有缓冲区管理。我怀疑基于std::vector的东西意味着我需要更多的代码。不过我可能仍然可以使用swap,尽管我需要让调用者“放弃”字符串而不是传递常量引用。 - Tim Sylvester

2

在我看来,最好的选择是一个已经被废弃的类std::strstream


谢谢,我不知道这个。唯一的问题是它需要一个 char* 而不是一个 const char*,但这很容易解决。 - Tim Sylvester

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接