我正在尝试使用现代字符串处理方法(如 std::string_view
或 GSL's string_span
)与 C API (DBus) 进行交互,该 C API (DBus) 将字符串视为空终止 const char*
s,例如
DBusMessage* dbus_message_new_method_call(
const char* destination,
const char* path,
const char* iface,
const char* method
)
string_view
和string_span
不保证它们的内容以空终止 - 因为跨度是 (char* start, ptrdiff_t length)
成双成对,这就是重点。但GSL还提供了zstring_view
,保证以空终止。 comments左右zstring_span
建议它是专门为处理旧版 API 和 C API 而设计的,但我一开始使用它就遇到了几个症结点:
将字符串文字表示为
string_span
是微不足道的:cstring_span<> bar = "easy peasy";
但将一个表示为
zstring_span
要求您将文字包装在辅助函数中:czstring_span<> foo = ensure_z("odd");
这使得声明变得更加嘈杂,而且文字(保证以 null 结尾)不能隐式转换为
zstring_span
似乎也很奇怪。 。ensure_z()
也不是constexpr
,与string_span
的构造函数和转换不同.std::string
也有类似的奇怪现象。 ,可隐式转换为string_span
,但不是zstring_span
,尽管std::string::data()
自 C++11 起,已保证返回空终止序列。再次强调,您必须调用ensure_z()
:zstring_span<> to_zspan(std::string& s) { return ensure_z(s); }
似乎存在一些常量正确性问题。上面的方法有效,但是
czstring_span<> to_czspan(const std::string& s) { return ensure_z(s); }
编译失败,出现无法从
span<char, ...>
转换的错误至span<const char, ...>
这是一个比其他点更小的点,但是返回
char*
的成员函数(您可以将其提供给 DBus 等 C API)称为assume_z()
。当zstring_span
的构造函数时,假设是什么? expects a null-terminated range ?
如果zstring_span
被设计为“将零终止的跨度转换为遗留字符串”,为什么它在这里的使用看起来如此麻烦?我是否滥用它?有什么我忽略的事情吗?
最佳答案
- it also seems odd that a literal (which is guaranteed to be null-terminated) isn't implicitly convertible to a
zstring_span
字符串文字的类型为 const char[...]
。类型中没有任何信息表明此const char
array 是一个以 null 结尾的字符串。这是具有相同类型的其他一些代码,但没有空终止,其中 ensure_z
很快就会失败。
const char foo_arr[4]{ 'o', 'd', 'd', '-' };
ensure_z(foo_arr);
两者"foo"
和foo_arr
类型为const char[4]
,但只有字符串文字以 null 结尾,而 foo_arr
不是。
请注意您的 ensure_z
的组合和czstring_span<>
可以编译,但是不起作用。 ensure_z
仅返回不带终止空字节的字符串。当您将其传递给czstring_span<>
时构造函数,那么构造函数将无法搜索空字节(被 ensure_z
截断)。
您需要将字符串文字转换为跨度并将其传递给构造函数:
czstring_span<> foo = ensure_span("odd");
- There's a similar oddity with
std::string
, which is implicitly convertible tostring_span
, but notzstring_span
好点。有一个 string_span
的构造函数这需要 std::string
,但是对于 zstring_span
只有一个采用内部实现类型的构造函数,a span<char>
。对于 span
有一个构造函数采用一个具有 .data()
的“容器”和.size()
- 其中std::string
实现。更糟糕的是:以下代码可以编译但无法运行:
zstring_span<> to_zspan(std::string& s) { return zstring_span<>{s}; }
您应该考虑在 GSL 存储库中提交问题以使类保持一致。我不确定隐式转换是否是一个好主意,所以我更喜欢 zstring_span
中的完成方式怎么样string_span
做到了。
- There seems to be some const-correctness issues.
这也是我的第一个想法 czstring_span<> to_czspan(const std::string& s) { return czstring_span<>{s}; }
编译但不起作用。另一个解决方案是一个新函数 ensure_cz
返回 span<const char, ...>
。您应该考虑提出问题。
assume_z()
empty()
的存在以及 as_string_span()
中的代码表明该类旨在能够处理空字符串范围。在这种情况下as_string_span
总是返回字符串而不终止空字节,ensure_z
将返回带有终止空字节的字符串,如果为空则失败,并且 assume_z
会假设 !empty()
并返回以空字节结尾的字符串。
但是唯一的构造函数采用非空字符范围,因此 empty()
永远不可能true
。我刚刚创建了一个 PR来解决这些不一致的问题。如果您认为应该进行更多更改,请考虑提出问题。
If
zstring_span
is designed "to convert zero-terminated spans to legacy strings", why does its use here seem so cumbersome? Am I misusing it? Is there something I'm overlooking?
在纯 C++ 代码中,我更喜欢 std::string_view
, zstring_span
仅适用于 C 互操作,这限制了它的使用。当然,您必须了解指南和指南支持库。鉴于我打赌zstring_span
很少被使用,并且您是极少数深入研究它的人之一。
关于c++ - 将 gsl::zstring_view 与 C API 结合使用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56874532/