我可以用 0-copy 获取 std::stringstream 累积数据的原始指针吗?

Can I get the raw pointer for a std::stringstream accumulated data with 0-copy?

这是我想做的事情:

std::stringstream s;
s<<"Some "<<std::hex<<123<<" hex data"<<...;

有了这个s,我很想把它传下去,这很容易做到。但是,在某些时候,需要(概念上)将其传递给只接受描述内存区域的 const void */size_t 对的接口。

据我所知(愿意接受更正),在 C++17 及以下版本中无法使用 0-copy 做到这一点。必须使用 .str() 这将创建一个字符串副本,然后从那里获取它。

作为“hack”,这是我想出的:

struct stringstream_extractor_t {
    // this structure will be used to get access to pbase member function which is protected
    // the way we do that is to inherit from it
    template <typename T>
    struct accessor_t : public T {
        static const char* data(const accessor_t* pT) { return pT->pbase(); }
    };

    // get the return type for std::stringstream::rdbuf
    using bufferType = std::remove_pointer<decltype(((std::stringstream*)nullptr)->rdbuf())>::type;

    // having the std::stringstream::rdbuf result type, we can now create our accessor type
    // which will be able to call pbase inside it
    using accessorType = accessor_t<bufferType>;

    // this is where we deposit the data, in a const manner
    const std::string_view _data;

    // syntactic sugar: init the _data with the stuff from stream reference
    stringstream_extractor_t(std::stringstream& stream) : _data{getBuffer(stream), static_cast<size_t>(stream.tellp())} {}

    // syntactic sugar: init the _data with the stuff from stream pointer
    stringstream_extractor_t(std::stringstream* pStream) :
        _data{pStream ? getBuffer(*pStream) : nullptr, pStream ? static_cast<size_t>(pStream->tellp()) : 0} {}

    // this uses the accessor type to grab access to data
    static const char* getBuffer(const std::stringstream& stream) {
        // we get the buffer and we cast it to our accessor type. This is safe, as we do not
        // have any members inside it, just a static function
        const accessorType* pBuffer = reinterpret_cast<accessorType*>(stream.rdbuf());

        // grab the data now
        return accessorType::data(pBuffer);
    }

    // convenience functionality
    inline const char* data() const { return _data.data(); }
    inline size_t size() const { return _data.size(); }
};

这是它的使用方式,具有类似 C 的界面

std::stringstream s;
s << "Hi there! " << std::hex << 0xdeadbeef;

const stringstream_extractor_t e(s);
write(2, e.data(), e.size());

是的,我知道指针必须保持活动状态(std::stringstream 实例)以及所有生命周期影响这一事实。

是否有更舒适的非复杂方式来实现这个非常基本的事情:从具有移动语义的输出字符串流中获取缓冲区。

我显然遗漏了一些东西,这不应该这么难。

https://godbolt.org/z/G53P6oocb

在 C++ 20 中你可以这样做

#include <iostream>
#include <ios>
#include <sstream>


void c_style_func(const char* cp, std::size_t size) {
    std::cout << std::string_view  (cp,size) << "\n";
}

int main() {
    std::stringstream s;
    s << "Hi there! " << std::hex << 0xdeadbeef;
    auto view = s.view();
    c_style_func(view.data(), view.size());
}

使用标准 1411 的 § 29.8.2.4 页

basic_string_view<charT, traits> view() const noexcept; 11 Let sv be basic_string_view<charT, traits>. 12 Returns: A sv object referring to the basic_stringbuf’s underlying character sequence in buf: (12.1) — If ios_base::out is set in mode, then sv(pbase(), high_mark-pbase()) is returned. (12.2) — Otherwise, if ios_base::in is set in mode, then sv(eback(), egptr()-eback()) is returned. (12.3) — Otherwise, sv() is returned. 13 [Note: Using the returned sv object after destruction or invalidation of the character sequence underlying *this is undefined behavior, unless sv.empty() is true. —end note]