我正在尝试使用现代的字符串处理方法(例如std::string_view
或GSL的string_span
(与C API(DBU(进行交互,该方法将字符串作为null终止的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
,保证将被终止。围绕zstring_span
的评论表明,它是针对与Legacy和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
也有类似的奇数,即使std::string::data()
保证以自C 11以来可以返回零端端的序列,它也可以隐式转换为string_span
,但不能转换为zstring_span
。同样,您必须致电ensure_z()
:zstring_span<> to_zspan(std::string& s) { return ensure_z(s); }
似乎存在一些const校正问题。以上有效,但
czstring_span<> to_czspan(const std::string& s) { return ensure_z(s); }
无法编译,由于无法从
span<char, ...>
转换为span<const char, ...>
,错误这是一个比其他点要小的点,但是返回
char*
的成员函数(您将馈送到CAPI(如DBU((称为assume_z()
。当zstring_span
的构造函数期望无效终止范围时,假设是什么?
如果zstring_span
的设计"将零终端的跨度转换为传统字符串",为什么它的使用看起来很麻烦?我在滥用吗?我忽略了什么吗?
- 似乎很奇怪的是,字面的(保证为null终止(不会隐式转换为
zstring_span
字符串文字为const char[...]
类型。该类型中没有任何信息,该const char
数组是一个null终止字符串。这是其他类型相同类型的其他代码,但是如果没有零终止,则ensure_z
将快速失败。
const char foo_arr[4]{ 'o', 'd', 'd', '-' };
ensure_z(foo_arr);
"foo"
和foo_arr
均为const char[4]
类型,但仅终止字符串,而foo_arr
不是。
请注意,您的ensure_z
和czstring_span<>
编译的组合,但行不通。ensure_z
仅返回字符串,而无需终止null字节。当您将其传递给czstring_span<>
构造函数时,构造函数将未能搜索null字节(由ensure_z
切断(。
您需要将字符串文字转换为跨度并将其传递给构造函数:
czstring_span<> foo = ensure_span("odd");
- 与
std::string
有类似的奇数,该奇数可隐式转换为string_span
,但不能zstring_span
好点。string_span
有一个构造函数,它需要std::string
,但是对于zstring_span
,只有一个构造函数将内部实现类型(span<char>
(采用。对于span
,有一个构造函数,采用具有.data()
和.size()
的"容器" - std::string
实现。更糟糕的是:以下代码编译但将行不通:
zstring_span<> to_zspan(std::string& s) { return zstring_span<>{s}; }
您应该考虑在GSL存储库中提交问题以使课程对齐。我不确定隐式转换是否是个好主意,所以我更喜欢在zstring_span
中如何完成string_span
的方法。
- 似乎存在一些const纠正问题。
也在这里我第一个czstring_span<> to_czspan(const std::string& s) { return czstring_span<>{s}; }
编译的想法,但不起作用。另一个解决方案是返回span<const char, ...>
的新功能ensure_cz
。您应该考虑提交问题。
assume_z()
empty()
的存在和as_string_span()
中的代码表明该类旨在能够处理空字符串跨度。在这种情况下, as_string_span
总是会返回字符串而不终止null字节, ensure_z
将用终止null字节返回字符串,如果为空,则失败,并且assume_z
假设!empty()
并以终止null byte返回字符串。
但是,唯一的构造函数正在采用非空字符,因此empty()
永远不可能是true
。我刚刚创建了一个PR来解决这些不一致之处。如果您认为应该更改更多,请考虑提交问题。
如果
zstring_span
的设计"将零终端的跨度转换为传统字符串",为什么它的使用看起来很麻烦?我在滥用吗?我忽略了什么吗?
在纯C 代码中,我更喜欢std::string_view
,zstring_span
仅用于C Interop,这限制了其使用。当然,您必须了解指南和指南支持库。鉴于我敢打赌,很少使用zstring_span
,而您是很少有人深入研究它的人之一。
它是"笨拙的"部分是因为它是为了。
这个:
zstring_span<> to_zspan(std::string& s) { return ensure_z(s); }
是不是安全操作。为什么?因为虽然s
的确被终止了,但实际的s
完全有可能包含内部nul字符。您可以使用std::string
来做合法的事情,但是zstring_span
和任何人都无法处理。他们会截断字符串。
相比之下,从这个角度来看,string_span/view
转换是安全的。这样的字符串的消费者采用大小的字符串,因此可以处理嵌入式的nul。
由于zstring_span
转换是不安全的,因此应该有一些明确的表示可能正在做一些可能不安全的事情。ensure_z
表示该明确的符号。
另一个问题是C 没有任何机制来说明字符串参数与任何旧的const char*
或const char[]
参数之间的差异。由于裸露的const char*
可能是字符串字面的字符串,因此您必须假设它不是,因此使用更详细的转换。
另外,c 字符串文字可以包含嵌入式的nul字符,因此上述推理适用。
const
问题似乎是一个代码错误,您可能应该以此为代码。