Какова современная передовая практика использования строк в кросс-платформенных API C и С++?

Похоже, мне, возможно, придется приступить к кросс-платформенному проекту, и часть его должна быть сделана на C или С++ (пока не решил, что вопрос о них и тот и другой). Я буду иметь дело в основном с текстовыми материалами и строками в целом.

Этот C/С++ будет иметь API, вызываемый из более высокоуровневого кода, зависящего от платформы.

Мой вопрос:, какие типы целесообразно использовать для работы со строками, в частности при объявлении публичных интерфейсов? Существуют ли рекомендуемые стандартные методы? Есть ли что избежать?

У меня мало опыта написания кода на C или С++, и даже это было в Windows, поэтому ничего подобного кросс-платформу здесь вообще. Поэтому то, что я действительно ищу, - это то, что поможет мне правильно и избежать глупых вещей, которые могут вызвать много боли.


Изменить 1: Чтобы дать немного больше информации о предполагаемом использовании. API будет потребляться:

  • Цель C на iPhone/iPad/Mac через NSString и друзей. API может быть статически связан, поэтому здесь не нужно беспокоиться о проблемах .so.dll.

  • Java через JNI на Android и других платформах Java

  • .NET через p/invoke из управляемого кода С# или изначально статически связан с использованием С++/CLI.

  • Есть несколько соображений об использовании lua как-то в этом контексте. Не знаю, имеет ли это какое-либо отношение к чему-либо, хотя.

Ответ 1

Правила

  • Используйте UTF-форматы для хранения строк, а не "кодовых страниц" или чего-то еще (UTF-16, вероятно, проще): я полностью забыл о проблемах с байтовым порядком; UTF-8, вероятно, путь).

  • Используйте строки с нулевым завершением, а не подсчитанные строки, поскольку они являются самыми легкими для доступа к большинству языков. Но будьте осторожны с переполнением буфера.
    Обновление через 6 лет: Я рекомендовал этот API для целей взаимодействия (так как многие из них уже используют нуль-завершение, и есть несколько способов представления подсчитанных строк), а не лучший из лучших точек зрения, Сегодня я бы сказал, что первая менее важна и рекомендуется использовать подсчитанные строки, а не строки с нулевым завершением, если вы можете это сделать.

  • Не пытайтесь использовать такие классы, как std::string, чтобы передавать строки в/из пользователя. Даже ваша собственная программа может сломаться после обновления вашего компилятора/библиотек (поскольку их детали реализации - это просто: деталь реализации), не говоря уже о том, что проблемы с не-С++-программами будут иметь проблемы. Обновление через 6 лет: Это строго связано с совместимостью языка и ABI с другими языками, а не с общим советом для разработки программ на С++. Если вы занимаетесь разработкой С++, кросс-платформенными или другими, используйте STL! т.е. следуйте этим рекомендациям, если вам нужно называть ваш код с других языков.

  • Избегайте выделения строк для пользователя, если это действительно больно для пользователя иначе. Вместо этого возьмите буфер и заполните его данными. Таким образом, вам не нужно принуждать пользователя использовать определенную функцию для освобождения данных. (Это также часто является преимуществом производительности, поскольку оно позволяет пользователю выделять небольшие буферы в стеке. Но если вы это сделаете, предоставить свою собственную функцию, чтобы освободить данные. предположим, что ваши malloc или new могут быть освобождены со своими free или delete - их часто не может быть.)

Примечание:

Просто, чтобы уточнить, "пусть пользователь выделяет буфер" и "использовать строки с завершающим NULL" не работают друг против друга. Вам все равно нужно получить длину буфера от пользователя, но при завершении строки вы включаете NULL. Моя задача заключалась не в том, что вы должны сделать функцию, похожую на scanf("%s"), что явно неприемлемо опасно - вам все равно нужна длина буфера от пользователя. т.е. делать в значительной степени то, что делает Windows в этом отношении.

Ответ 2

Этот C/С++ будет иметь API, вызываемый с более высокого уровня платформозависимый код.

Если вы подразумеваете, что эта библиотека должна быть DLL, которая может быть вызвана с других языков, например, с .NET-языков, то я настоятельно рекомендую использовать все публичные API как функции extern "C", которые имеют только типы POD как параметры и возвращаемые значения. То есть, предпочитайте /*const*/ char* над std::string. Помните, что С++, в отличие от простой C, не имеет стандартного ABI.

Ответ 3

Если вы хотите, чтобы десятиминутный молот имел дело со строками на C/С++, тогда проект IBM ICU для вас. http://site.icu-project.org/

В ICU есть все инструменты для работы со строками с действительно хорошей поддержкой юникода. Это впечатляющий и ухоженный продукт с открытым исходным кодом с благоприятной лицензией для коммерческих проектов.

Если вы хотите выпустить свой код как .dll/.so для вызова других, то вы, вероятно, захотите минимизировать свои внешние зависимости. В этом случае вы можете захотеть придерживаться стандартных библиотек или более легкого проекта.

Ответ 4

Очень распространенный способ вернуть строку вызывающему пользователю - это принять указатель буфера строки и количество символов в размере буфера. Полезным соглашением является возврат количества символов, скопированных в буфер, в качестве возвращаемого значения; это особенно важно, если вы обрабатываете размер буфера 0 как особый случай и возвращаете количество символов, которые требуются (включая нулевой терминатор).

int GetString(char * buffer, int buffersize);

В С++ удобно работать с std::string вместо этого, но это создает проблему: вы не можете полагаться на реализацию std::string для совместимости между скомпилированными частями программы, то есть между вашей основной программой и библиотеки. Предоставляя встроенную функцию в файле заголовка, вы можете гарантировать, что std::string создается в том же контексте, что и вызывающий, и обходит эту проблему.

inline std::string GetString()
{
    std::string result(GetString(NULL, 0), 0);
    GetString(&result[0], result.size());
    result.erase(result.size() - 1);
    return result;
}