Как написать переносимый код на C ++? - PullRequest
27 голосов
/ 23 июня 2010

Что я должен иметь в виду при написании переносимого кода?Так как я новичок в C ++, я хочу практиковать это с самого начала.

Спасибо.

Ответы [ 12 ]

17 голосов
/ 23 июня 2010
  • научиться пользоваться стандартной библиотекой
  • читать книги (например, эта )
  • когда у вас есть опыт, научитесь использовать буст
14 голосов
/ 23 июня 2010

Храните специфичный для платформы код отдельно от повторно используемого кода, предпочтительно в другом файле, но, по крайней мере, в другой функции.Если вы начнете иметь #if WIN32 и #if CYGWIN и #if BSD повсюду, у вас будет кошмар обслуживания.

Затем скомпилируйте по крайней мере на двух разных платформах рано и часто.Типичные варианты: Visual C ++ в Windows и gcc в Linux.Поскольку ни системные библиотеки, ни компилятор не являются общими, вы поймаете непереносимый код, прежде чем он глубоко укоренится в вашем проекте.

13 голосов
/ 24 июня 2010

Что я должен иметь в виду при написании переносимого кода?

  1. Держите несколько компиляторов поблизости, регулярно тестируйте код на целевых платформах. Если вы работаете с кроссплатформенным программным обеспечением для Windows Windows / Linux, используйте Mingw, Visual Studio Express (то есть «компилятор Microsoft») и установку Linux с G ++ (или используйте виртуальную машину). Даже если ваш код идеален, у компилятора может возникнуть какая-то неожиданность. Например, определенные версии компилятора ms имеют ограничение на размеры строковых констант, которых нет у gcc.
  2. Не полагайтесь на размеры стандартных типов. Например, для msvc sizeof (wchar_t) составляет 2 байта. При установке Linux это может быть 4 байта. Используйте sizeof (если вам это нужно) или старайтесь не использовать размер любого типа в своем коде. И вы не должны предполагать, что указатель имеет размер 4 байта (передавая указатель пользовательских данных в сценарий вызова API) - это будет 8 байтов на 64-битной.
  3. Не используйте специфичные для компилятора прагмы, макросы и расширения. Например, избегайте "#pragma Once".
  4. Не используйте расширения для стандартной библиотеки (предоставляется разработчиком компилятора). Это более применимо к функциям библиотеки C, однако. Например, компилятор MS предоставляет несколько «безопасных» (например, strcpy_s) версий стандартных подпрограмм в стиле C. Который, конечно, не будет доступен на других платформах.
  5. Будьте очень осторожны, если вы решите использовать подпрограммы в стиле C (например, sprintf) в коде C ++. (Я знаю , что предполагается, что это плохая практика, но в некоторых сценариях это полезно). Они имеют несколько разные реализации, расширения и разное количество параметров. Например, sprintf может иметь разные дополнительные форматы, которые по-разному реализованы на разных платформах. Например, в прошлый раз, когда я проверял, «% S» ведет себя по-разному в msvc и gcc в подпрограмме vswprintf.
  6. Не полагайтесь на типы данных, специфичные для компилятора, такие как __int32. Весьма вероятно, что вам понадобится какой-то тип, который гарантированно будет иметь длину 4 байта (или что-то подобное) - используйте typedef в сочетании с условной компиляцией ("#ifdef WIN32"). ИЛИ использовать типы, предоставляемые кроссплатформенной библиотекой. Например, SDL предоставляет такие типы, как Uint8, Qt 4 имеет quint32 и т. Д. Это довольно распространенная практика.
  7. Избегайте прямых вызовов ОС. Используйте стандартные функции для доступа к файлам.
  8. Когда вам нужно использовать специфичные для ОС вызовы, используйте условную компиляцию (#ifdef WIN32 и т. Д.)
  9. Попробуйте использовать одинаковую систему сборки на всех платформах. На Linux нет MSBuild. Используйте gnumake, cmake, scons или qmake. Хотя в некоторых из этих систем вам придется кодировать флаги для разных компиляторов, везде можно будет использовать один и тот же скрипт. Например, он хорошо работает с SConstructs. А поддержка одного сценария сборки для всех платформ может быть проще, чем синхронизация изменений в разных системах сборки.
  10. Для всех операций, требующих взаимодействия с операционной системой (Gui, манипулирование файлами), используйте кроссплатформенные библиотеки. Qt - хороший выбор.
5 голосов
/ 23 июня 2010

Написание программ командной строки для начала. Когда вы будете готовы, найдите кроссплатформенный набор инструментов для работы с окнами, такой как Qt .

Если вы заинтересованы в написании многоязычного кода, используйте стороннюю библиотеку юникода, такую ​​как ICU , а не полагайтесь на библиотеки для конкретной платформы.

4 голосов
/ 23 июня 2010

Используйте типы STL, когда это возможно. Будьте осторожны с использованием системных зависимых типов и API. Например, не используйте такие типы, как UINT и DWORD в Windows.

Вы можете использовать такую ​​библиотеку, как boost, чтобы вам было легче писать переносимый код. Если вам нужен графический интерфейс, рассмотрите возможность использования кроссплатформенного инструментария, такого как Qt.

Иногда вам нужно будет написать код для конкретной платформы, и в таких случаях вы можете сделать что-то вроде этого:

#ifdef _WIN32
#include <windows.h>
#else
#include <unistd.h>
#endif
2 голосов
/ 23 июня 2010

Другие говорили это раньше, но вот мой взгляд на это:

1) Вам нужен C ++?Это не лучший язык для написания переносимого кода, потому что он близок к голому металлу.Java, Python, Perl, PHP или Javascript могут быть лучше для вас.

2) Если вам нужен C ++, не пытайтесь писать полностью переносимый код, в любом случае это практически невозможно.Вместо этого решите заранее, какие платформы вы хотите поддерживать.Например: Linux, MacOS X, Windows

3) Убедитесь, что вы постоянно тестируете свой код на всех выбранных платформах.Не просто основывайтесь на Windows, а ожидайте, что просто скомпилируйте версию для Linux «когда это будет сделано».Ежедневно компилируйте на всех платформах и проверяйте их на наличие проблем.

2 голосов
/ 23 июня 2010

Некоторые рекомендации:

  1. Держите бизнес-конец кода и графический интерфейс отдельно.
  2. Избегайте использования костылей, специфичных для компилятора (#pragma и т. Д.)
  3. Используйте обычные выражения, которые не изменят поведение с компилятором / платформой, вместо хитрых манипуляций с битами.
  4. Если это касается оборудования, оно принадлежит драйверу устройства.
  5. Используйте заголовки типов данных, такие как types.h (uint32_t и т. Д.).
  6. Используйте уровень абстракции операционной системы, чтобы не вызывать вызовы операционной системы напрямую.

Иногда вам приходится выбирать между эффективностью и производительностью, чтобы получить мобильность. Например, если ваш код требует доступа к полям из буфера, вы всегда можете привести упакованную структуру к указателю буфера. Но это ужасно непереносимо. Поэтому вместо этого вам нужно использовать именованные указатели, рассчитанные со смещением - иногда с кодом обработки выравнивания границ. Не симпатичный, но портативный. К счастью, вы можете скрыть многие из этих вещей с разумным использованием интерфейсов классов.

Не весь код должен быть написан таким образом. Если вы разрабатываете свое приложение очень модульным способом с четко определенными границами ответственности, то 90-95% кода могут быть переносимы безболезненно. Затем просто выделите 5-10% в очень локализованной области, которую необходимо настроить для новой платформы.

2 голосов
/ 23 июня 2010

Неосторожный программист может попасть во множество ловушек, которые мы можем попытаться классифицировать.Но позвольте мне сначала сказать вам: как абсолют, это невозможно.

Проблема в том, что даже стандартный код может не переноситься из-за конкретной проблемы компилятора.

Теперь вот основныекатегории, которые я могу придумать.

Расширения компилятора

Как, например, использование массивов переменных:

void func(int const n)
{
  int array[n];
}

Это не стандарт, но многие компиляторы поддерживают его, потому что это просто практично.

Стандартные расширения библиотек

Многие реализации стандартных библиотек предоставляют std::hash_map, который никогда не был указан,Если вы используете его в своем коде, он не является переносимым.

Современная тенденция заключается в том, чтобы внедрить этот материал в пространство имен std::tr1, чтобы программисты знали, что это расширение.

ТакжеИмейте в виду, что многие определяют typedef или макросы, которые не являются общими (например, PRETTY_FUNCTION).Стандарт не определяет макрос, и очень мало typedef.

Для конкретной платформы

Например, размер и выравнивание int или doubleне указано в стандарте.Если вы выполняете бит-тиддлинг и ожидаете, что он будет 32-битным, вы будете ввернуты на 64-битных платформах даже без изменения компилятора.

API платформы

НашПрограммы предназначены для компиляции и часто предназначены для взаимодействия с компьютером, на котором они работают:

  • для доступа к оборудованию
  • для доступа к файловой системе
  • для доступа к экрану

Вам нужно найти кроссплатформенные переносимые API или свернуть свои собственные.Проверьте некоторые библиотеки в списке ниже.

Библиотеки

Большинство хорошо написанных библиотек в основном переносимы, просто убедитесь, что они поддерживают:

  • интересующие вас компиляторы
  • платформы, которые вас интересуют

Хорошие библиотеки включают в себя:

  • Apache (коллекция библиотек)
  • Повышение
  • Qt (для графики)
  • ICU (для обработки Unicode)

Остальные, которые вам нужно просмотреть ... и это занимаетвремя.

Я не думаю, что там есть идеальный ответ.Но поскольку идеальная переносимость невозможна, вам нужно решить, какие компиляторы и платформу вы хотите поддерживать.

Для платформы вы должны начать с Windows и одного варианта Linux.Для компиляторов выберите любые два (с Comeau, если вы можете себе это позволить).

1 голос
/ 23 июня 2010

Если вы можете, скомпилируйте весь ваш код как минимум с двумя разными компиляторами.

1 голос
/ 23 июня 2010

Независимый от ОС код на C ++ удивительно сложен.Рассмотрим этот тривиальный пример:

#include <iostream>
int main(int argc, char** argv) {
  std::cout << argv[0] << std::endl;
}

Это совершенно допустимый C ++, но он не переносим, ​​потому что он не принимает аргументы командной строки Unicode в Windows.Правильная версия для Windows будет выглядеть следующим образом:

#include <iostream>
int wmain(int argc, wchar_t** argv) {
  std::wcout << argv[0] << std::endl;
}

Конечно, это снова непереносимо, работает только в Windows и нестандартно.Так что на самом деле вы даже не можете написать переносимую main() функцию на C ++, не прибегая к условной компиляции.

...