簡體   English   中英

將 gsl::zstring_view 與 C API 結合使用

[英]Using gsl::zstring_view with C APIs

我正在嘗試使用現代字符串處理方法(如std::string_viewGSL 的string_span )與將字符串作為空終止const char*的C API( string_span )交互,例如

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評論表明它專為處理遺留和 C API 而設計,但我一開始使用它就遇到了幾個問題:

  1. 將字符串文字表示為string_span很簡單:

     cstring_span<> bar = "easy peasy";

    但是將一個表示為zstring_span需要您將文字包裝在輔助函數中:

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

    這使得聲明更加嘈雜,而且文字(保證以空字符結尾)不能隱式轉換為zstring_span似乎也很奇怪。 ensure_z()也不是constexpr ,不像構造函數和轉換string_span

  2. 有一個類似的古怪與std::string ,這是隱式轉換為string_span ,但不zstring_span ,即使std::string::data()已保證,因為C ++ 11返回一個空值終止序列。 同樣,您必須調用ensure_z()

     zstring_span<> to_zspan(std::string& s) { return ensure_z(s); }
  3. 似乎存在一些常量正確性問題。 以上工作,但

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

    編譯失敗,出現無法從span<char, ...>轉換為span<const char, ...>

  4. 這一點比其他點小,但返回char* (您將其提供給像assume_z()這樣的 C API)的成員函數稱為assume_z() zstring_span的構造zstring_span 需要一個以空值結尾的范圍時,假設是什么?

如果zstring_span旨在“將零終止的跨度轉換為遺留字符串”,為什么它在這里的使用看起來如此麻煩? 我濫用了嗎? 有什么我忽略的嗎?

  1. 文字(保證以空字符結尾)不能隱式轉換為zstring_span似乎也很奇怪

字符串文字的類型為const char[...] 類型中沒有信息表明此const char數組是空終止字符串。 這是其他一些具有相同類型的代碼,但沒有空終止,其中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只返回沒有終止空字節的字符串。 當您將其傳遞給czstring_span<>構造函數時,構造函數將無法搜索空字節(已被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它是如何完成的, zstring_span不是string_span是如何完成的。

  1. 似乎存在一些常量正確性問題。

同樣在這里我的第一個想法czstring_span<> to_czspan(const std::string& s) { return czstring_span<>{s}; } czstring_span<> to_czspan(const std::string& s) { return czstring_span<>{s}; }編譯,但不起作用。 另一個解決方案是一個新函數ensure_cz返回一個span<const char, ...> 您應該考慮提出問題。

  1. assume_z()

empty()的存在和as_string_span()的代碼表明該類旨在能夠處理空字符串跨度。 在這種情況下as_string_span總是返回字符串不終止空字節, ensure_z將與終止空字節會返回一個字符串,如果為空失敗,並assume_z會認為!empty()並終止空字節返回字符串。

但是唯一的構造函數采用非空的字符范圍,因此empty()永遠不可能為true 我剛剛創建了一個PR來解決這些不一致的問題。 如果您認為應該更改更多內容,請考慮提交問題。

如果zstring_span旨在“將零終止的跨度轉換為遺留字符串”,為什么它在這里的使用看起來如此麻煩? 我濫用了嗎? 有什么我忽略的嗎?

在純 C++ 代碼中,我更喜歡std::string_viewzstring_span僅用於 C 互操作,這限制了它的使用。 當然,您必須了解指南和指南支持庫。 鑒於我敢打賭zstring_span很少被使用,而且您是極少數深入研究它的人之一。

它的“麻煩”部分是因為它是有意為之。

這個:

zstring_span<> to_zspan(std::string& s) { return ensure_z(s); }

不是一個安全的操作。 為什么? 因為雖然s確實是 NUL 終止的,但實際的s完全有可能包含內部 NUL 字符。 這是您可以使用std::string做的合法事情,但是zstring_span和任何接受它的人都無法處理。 他們會截斷字符串。

相比之下,從這個角度來看, string_span/view轉換是安全的。 此類字符串的使用者采用大小合適的字符串,因此可以處理嵌入的 NUL。

因為zstring_span轉換是不安全的,所以應該有一些明確的表示正在做一些可能不安全的事情。 ensure_z表示該顯式符號。

另一個問題是 C++ 沒有機制來區分文字字符串參數和任何舊的const char*const char[]參數之間的區別。 由於裸const char*可能是也可能不是字符串文字,因此您必須假設它不是,因此使用更詳細的轉換。

此外,C++ 字符串文字可以包含嵌入的 NUL 字符,因此上述推理適用。

const問題似乎是一個代碼錯誤,您可能應該將其歸檔。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM