Поддержка библиотеки C++

NDK поддерживает несколько библиотек времени выполнения C++. В этом документе представлена ​​информация об этих библиотеках, связанных с ними компромиссах и о том, как их использовать.

Библиотеки времени выполнения C++

Таблица 1. Среды выполнения и возможности NDK C++.

Имя Функции
библиотека libС++ Поддержка современного C++.
система new и delete . (Устарело в версии 18.)
никто Никаких заголовков, ограниченный C++.

libc++ доступен как в виде статической, так и в виде общей библиотеки.

библиотека libС++

libc++ LLVM — это стандартная библиотека C++, которая используется ОС Android начиная с Lollipop, а начиная с NDK r18 — единственный STL, доступный в NDK.

По умолчанию CMake использует любую версию C++ clang по умолчанию (в настоящее время C++14), поэтому вам необходимо установить для стандартного CMAKE_CXX_STANDARD соответствующее значение в файле CMakeLists.txt , чтобы использовать функции C++17 или более поздних версий. Дополнительные сведения см. в документации CMake для CMAKE_CXX_STANDARD .

ndk-build также оставляет решение clang по умолчанию, поэтому пользователи ndk-build должны использовать APP_CPPFLAGS для добавления -std=c++17 или чего-то еще, что они хотят.

Общая библиотека для libc++ — libc++_shared.so , а статическая — libc++_static.a . В типичных случаях система сборки будет обрабатывать использование и упаковку этих библиотек по мере необходимости пользователя. Для нетипичных случаев или при внедрении собственной системы сборки см. Руководство для сопровождающих систем сборки или руководство по использованию других систем сборки .

Проект LLVM находится под лицензией Apache v2.0 с исключениями LLVM. Дополнительную информацию смотрите в файле лицензии .

система

Среда выполнения системы относится к /system/lib/libstdc++.so . Эту библиотеку не следует путать с полнофункциональной libstdc++ от GNU. В Android libstdc++ — это просто new и delete . Используйте libc++ в качестве полнофункциональной стандартной библиотеки C++.

Системная среда выполнения C++ обеспечивает поддержку базового ABI среды выполнения C++. По сути, эта библиотека предоставляет new и delete . В отличие от других опций, доступных в NDK, здесь нет поддержки обработки исключений или RTTI.

Не существует поддержки стандартной библиотеки, за исключением оболочек C++ для заголовков библиотеки C, таких как <cstdio> . Если вам нужен STL, вам следует использовать один из других вариантов, представленных на этой странице.

никто

Существует также возможность отказаться от STL. В этом случае нет никаких требований по связыванию или лицензированию. Стандартные заголовки C++ недоступны.

Выбор среды выполнения C++

CMake

По умолчанию для CMake используется c++_static .

Вы можете указать c++_shared , c++_static , none или system , используя переменную ANDROID_STL в файле build.gradle уровня модуля. Дополнительные сведения см. в документации ANDROID_STL в CMake.

ndk-сборка

По умолчанию для ndk-build установлено значение none .

Вы можете указать c++_shared , c++_static , none или system , используя переменную APP_STL в файле Application.mk . Например:

APP_STL := c++_shared

ndk-build позволяет вам выбрать только одну среду выполнения для вашего приложения и может сделать это только в Application.mk .

Используйте clang напрямую

Если вы используете clang непосредственно в своей системе сборки, clang++ по умолчанию будет использовать c++_shared . Чтобы использовать статический вариант, добавьте -static-libstdc++ к флагам компоновщика. Обратите внимание: хотя по историческим причинам эта опция использует имя «libstdc++», это также верно и для libc++.

Важные соображения

Статические среды выполнения

Если весь собственный код вашего приложения содержится в одной общей библиотеке, мы рекомендуем использовать статическую среду выполнения. Это позволяет компоновщику встроить и удалить как можно больше неиспользуемого кода, что приводит к созданию максимально оптимизированного и минимального приложения. Он также позволяет избежать ошибок PackageManager и динамического компоновщика в старых версиях Android, которые затрудняют работу с несколькими общими библиотеками и повышают вероятность ошибок.

Тем не менее, в C++ небезопасно определять более одной копии одной и той же функции или объекта в одной программе. Это один из аспектов правила одного определения, присутствующего в стандарте C++.

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

# Application.mk
APP_STL := c++_static
# Android.mk

include $(CLEAR_VARS)
LOCAL_MODULE := foo
LOCAL_SRC_FILES := foo.cpp
include $(BUILD_SHARED_LIBRARY)

include $(CLEAR_VARS)
LOCAL_MODULE := bar
LOCAL_SRC_FILES := bar.cpp
LOCAL_SHARED_LIBRARIES := foo
include $(BUILD_SHARED_LIBRARY)

В этой ситуации STL, включая глобальные данные и статические конструкторы, будет присутствовать в обеих библиотеках. Поведение этого приложения во время выполнения не определено, и на практике сбои очень распространены. Другие возможные проблемы включают в себя:

  • Память, выделенная в одной библиотеке и освобожденная в другой, приводит к утечке памяти или повреждению кучи.
  • Исключения, возникшие в libfoo.so не перехватываются в libbar.so , что приводит к сбою вашего приложения.
  • Буферизация std::cout работает неправильно.

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

Как правило, вы можете использовать статический вариант среды выполнения C++ только в том случае, если в вашем приложении есть одна и только одна общая библиотека.

Общие среды выполнения

Если ваше приложение включает в себя несколько общих библиотек, вам следует использовать libc++_shared.so .

В Android библиотека libc++, используемая NDK, отличается от той, которая является частью ОС. Это дает пользователям NDK доступ к новейшим функциям libc++ и исправлениям ошибок даже при использовании старых версий Android. Компромисс заключается в том, что если вы используете libc++_shared.so , вы должны включить его в свое приложение. Если вы создаете свое приложение с помощью Gradle, это обрабатывается автоматически.

В старых версиях Android были ошибки в PackageManager и динамическом компоновщике, из-за которых установка, обновление и загрузка собственных библиотек были ненадежными. В частности, если ваше приложение предназначено для версии Android более ранней, чем Android 4.3 (уровень Android API 18), и вы используете libc++_shared.so , вы должны загрузить общую библиотеку перед любой другой библиотекой, которая от нее зависит.

Проект ReLinker предлагает обходные пути для всех известных проблем с загрузкой встроенных библиотек и обычно является лучшим выбором, чем написание собственных обходных решений.

Один STL на приложение

Исторически NDK поддерживал GNU libstdc++ и STLport в дополнение к libc++. Если ваше приложение зависит от готовых библиотек, которые были созданы на основе NDK, отличного от того, который использовался для создания вашего приложения, вам необходимо убедиться, что оно работает совместимым образом.

Приложение не должно использовать более одной среды выполнения C++. Различные STL несовместимы друг с другом. Например, расположение std::string в libc++ не такое, как в gnustl. Код, написанный для одного STL, не сможет использовать объекты, написанные для другого. Это всего лишь один пример; несовместимости многочисленны.

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

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

Исключения С++

Исключения C++ поддерживаются libc++, но по умолчанию они отключены в ndk-build. Это связано с тем, что исторически исключения C++ не были доступны в NDK. В CMake и автономных цепочках инструментов по умолчанию включены исключения C++.

Чтобы включить исключения во всем приложении в ndk-build, добавьте следующую строку в файл Application.mk :

APP_CPPFLAGS := -fexceptions

Чтобы включить исключения для одного модуля ndk-build, добавьте следующую строку к данному модулю в его Android.mk :

LOCAL_CPP_FEATURES := exceptions

Альтернативно вы можете использовать:

LOCAL_CPPFLAGS := -fexceptions

РТТИ

Как и в случае с исключениями, RTTI поддерживается libc++, но по умолчанию отключен в ndk-build. В CMake и автономных цепочках инструментов RTTI включен по умолчанию.

Чтобы включить RTTI во всем приложении в ndk-build, добавьте следующую строку в файл Application.mk :

APP_CPPFLAGS := -frtti

Чтобы включить RTTI для одного модуля ndk-build, добавьте следующую строку к данному модулю в его Android.mk :

LOCAL_CPP_FEATURES := rtti

Альтернативно вы можете использовать:

LOCAL_CPPFLAGS := -frtti