Какова современная передовая практика использования строк в кросс-платформенных 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;
}