这就是我想做的:
std::stringstream s;
s<<"Some "<<std::hex<<123<<" hex data"<<...;
有了这个
s
,我非常想把它传递出去,这是很容易做到的。但是,在某些时候,需要(概念上)将其传递到仅接受描述内存区域的 const void */size_t
对的接口。
据我所知(希望得到纠正),在 C++17 及以下版本中没有办法使用 0 拷贝来做到这一点。必须使用
.str()
这将创建一个字符串副本,然后从那里获取它。
作为一个“黑客”,这就是我想出的:
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
view() const noexcept; 11 令 sv 为 basic_string_view 。 12 返回: 一个 sv 对象,引用 buf 中 basic_stringbuf 的底层字符序列: (12.1) — 如果 ios_base::out 设置为 mode,则返回 sv(pbase(), high_mark-pbase())。 (12.2) — 否则,如果 ios_base::in 设置为 mode,则返回 sv(eback(), egptr()-eback())。 (12.3) — 否则,返回 sv()。 13 [注意:在底层字符序列被破坏或失效后使用返回的 sv 对象 *这是未定义的行为,除非 sv.empty() 为 true。 ——尾注]
2024 年补充:
与std::string_view.data
相比,
std::string::data
不受保证,并且通常 不是 null 终止。因此,上面的用法仍然安全,因为它不假设 NTS,但是请注意,如果您使用任何通常只采用 char*
的函数。