Использование gsl::zstring_view с C API
Я пытаюсь использовать современные подходы к обработке строк (например, std::string_view
или GSL string_span
) для взаимодействия с 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
, который гарантированно завершается нулем. Комментарии вокруг 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*
(который вы бы подали в C API, как DBus) называетсяassume_z()
, Что предполагается, когда конструкторzstring_span
ожидает нулевой диапазон?
Если zstring_span
разработан так, чтобы "преобразовывать нулевые концы в устаревшие строки", почему его использование здесь кажется таким громоздким? Я неправильно это использую? Есть что-то, что я пропускаю?
1 ответ
- также кажется странным, что литерал (который гарантированно завершается нулем) не может быть неявно преобразован в
zstring_span
Строковый литерал имеет тип const char[...]
. В типе нет информации о том, что этоconst char
array - строка с завершающим нулем. Вот другой код с такими же типами, но без завершения нуля, где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
есть только конструктор, принимающий внутренний тип реализации, a span<char>
. Заspan
есть конструктор, принимающий "контейнер", имеющий .data()
а также .size()
- который std::string
орудия. Хуже того: следующий код компилируется, но не работает:
zstring_span<> to_zspan(std::string& s) { return zstring_span<>{s}; }
Вам следует подумать о том, чтобы зарегистрировать проблему в репозитории GSL, чтобы согласовать классы. Я не уверен, что неявные преобразования - хорошая идея, поэтому предпочитаю, как это делается вzstring_span
как string_span
Является ли.
- Кажется, есть некоторые проблемы с константной правильностью.
Также здесь моя первая идея 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
Проблема выглядит как ошибка кода, и вам, вероятно, следует подать ее как таковую.