我可以用 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 实例)以及所有生命周期影响这一事实。
是否有更舒适的非复杂方式来实现这个非常基本的事情:从具有移动语义的输出字符串流中获取缓冲区。
我显然遗漏了一些东西,这不应该这么难。
在 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]
这是我想做的事情:
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 实例)以及所有生命周期影响这一事实。
是否有更舒适的非复杂方式来实现这个非常基本的事情:从具有移动语义的输出字符串流中获取缓冲区。
我显然遗漏了一些东西,这不应该这么难。
在 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]