Использование 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, но я столкнулся с несколькими препятствиями, как только начал его использовать:

  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* (который вы бы подали в C API, как DBus) называется assume_z(), Что предполагается, когда конструктор zstring_span ожидает нулевой диапазон?

Если zstring_span разработан так, чтобы "преобразовывать нулевые концы в устаревшие строки", почему его использование здесь кажется таким громоздким? Я неправильно это использую? Есть что-то, что я пропускаю?

1 ответ

  1. также кажется странным, что литерал (который гарантированно завершается нулем) не может быть неявно преобразован в zstring_span

Строковый литерал имеет тип const char[...]. В типе нет информации о том, что этоconst chararray - строка с завершающим нулем. Вот другой код с такими же типами, но без завершения нуля, где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");
  1. Есть похожая странность с 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 Является ли.

  1. Кажется, есть некоторые проблемы с константной правильностью.

Также здесь моя первая идея 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_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 Проблема выглядит как ошибка кода, и вам, вероятно, следует подать ее как таковую.

Другие вопросы по тегам