[英]Using gsl::zstring_view with C APIs
我正在嘗試使用現代字符串處理方法(如std::string_view
或GSL 的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_view
和string_span
不保證它們的內容是空終止的——因為跨度是(char* start, ptrdiff_t length)
對,這就是重點。 但GSL還提供了一種zstring_view
,其被保證是空終止。 關於zstring_span
的評論表明它專為處理遺留和 C API 而設計,但我一開始使用它就遇到了幾個問題:
將字符串文字表示為string_span
很簡單:
cstring_span<> bar = "easy peasy";
但是將一個表示為zstring_span
需要您將文字包裝在輔助函數中:
czstring_span<> foo = ensure_z("odd");
這使得聲明更加嘈雜,而且文字(保證以空字符結尾)不能隱式轉換為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*
(您將其提供給像assume_z()
這樣的 C API)的成員函數稱為assume_z()
。 當zstring_span
的構造zstring_span
需要一個以空值結尾的范圍時,假設是什么?
如果zstring_span
旨在“將零終止的跨度轉換為遺留字符串”,為什么它在這里的使用看起來如此麻煩? 我濫用了嗎? 有什么我忽略的嗎?
- 文字(保證以空字符結尾)不能隱式轉換為
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_z
和czstring_span<>
組合可以編譯,但它不起作用。 ensure_z
只返回沒有終止空字節的字符串。 當您將其傳遞給czstring_span<>
構造函數時,構造函數將無法搜索空字節(已被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
它是如何完成的, zstring_span
不是string_span
是如何完成的。
- 似乎存在一些常量正確性問題。
同樣在這里我的第一個想法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, ...>
。 您應該考慮提出問題。
assume_z()
empty()
的存在和as_string_span()
的代碼表明該類旨在能夠處理空字符串跨度。 在這種情況下as_string_span
總是返回字符串不終止空字節, ensure_z
將與終止空字節會返回一個字符串,如果為空失敗,並assume_z
會認為!empty()
並終止空字節返回字符串。
但是唯一的構造函數采用非空的字符范圍,因此empty()
永遠不可能為true
。 我剛剛創建了一個PR來解決這些不一致的問題。 如果您認為應該更改更多內容,請考慮提交問題。
如果
zstring_span
旨在“將零終止的跨度轉換為遺留字符串”,為什么它在這里的使用看起來如此麻煩? 我濫用了嗎? 有什么我忽略的嗎?
在純 C++ 代碼中,我更喜歡std::string_view
, zstring_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.