使用C API使用GSL :: ZSTRING_VIEW

Using gsl::zstring_view with C APIs

本文关键字:使用 VIEW ZSTRING GSL API      更新时间:2023-10-16

我正在尝试使用现代的字符串处理方法(例如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_viewstring_span不能保证它们的内容是零终止的 - 因为跨度为 (char* start, ptrdiff_t length)对,这在很大程度上是重点。但是GSL还提供了一个zstring_view保证将被终止。围绕zstring_span的评论表明,它是针对与Legacy和C API一起工作的,但是一旦我开始使用它,我就遇到了几个粘附点:

  1. 表示字符串字面为string_span是微不足道的:

    cstring_span<> bar = "easy peasy";
    

    ,但代表一个zstring_span需要您将字面的插件包裹在辅助功能中:

    czstring_span<> foo = ensure_z("odd");
    

    这使声明变得更加嘈杂,而且似乎很奇怪的是,字面的(保证为null终止(不会隐式转换为zstring_spanensure_z()也不是constexpr,与string_span的构造函数不同。

  2. std::string也有类似的奇数,即使std::string::data()保证以自C 11以来可以返回零端端的序列,它也可以隐式转换为string_span,但不能转换为zstring_span。同样,您必须致电ensure_z()

    zstring_span<> to_zspan(std::string& s) { return ensure_z(s); }
    
  3. 似乎存在一些const校正问题。以上有效,但

    czstring_span<> to_czspan(const std::string& s) { return ensure_z(s); }
    

    无法编译,由于无法从span<char, ...>转换为span<const char, ...>

  4. ,错误
  5. 这是一个比其他点要小的点,但是返回char*的成员函数(您将馈送到CAPI(如DBU((称为assume_z()。当zstring_span的构造函数期望无效终止范围时,假设是什么?

如果zstring_span的设计"将零终端的跨度转换为传统字符串",为什么它的使用看起来很麻烦?我在滥用吗?我忽略了什么吗?

  1. 似乎很奇怪的是,字面的(保证为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_zczstring_span<>编译的组合,但行不通。ensure_z仅返回字符串,而无需终止null字节。当您将其传递给czstring_span<>构造函数时,构造函数将未能搜索null字节(由ensure_z切断(。

您需要将字符串文字转换为跨度并将其传递给构造函数:

czstring_span<> foo = ensure_span("odd");
  1. 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的方法。

  1. 似乎存在一些const纠正问题。

也在这里我第一个czstring_span<> to_czspan(const std::string& s) { return czstring_span<>{s}; }编译的想法,但不起作用。另一个解决方案是返回span<const char, ...>的新功能ensure_cz。您应该考虑提交问题。

  1. 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_viewzstring_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问题似乎是一个代码错误,您可能应该以此为代码。