NDK поддерживает несколько библиотек среды выполнения C++. В этом документе содержится информация об этих библиотеках, компромиссах и способах их использования.
Библиотеки среды выполнения C++
Таблица 1. Среды выполнения и функции NDK C++.
| Название | Функции |
|---|---|
| libc++ | Поддержка современного C++. |
| system | new и delete. (Поддержка прекращена в r18.) |
| none | Без заголовков, ограниченный C++. |
libc++ доступна как статическая и общая библиотека.
libc++
libc++ от LLVM – это стандартная библиотека C++, которая используется в ОС Android начиная с версии Lollipop. В NDK r18 это единственная доступная библиотека STL.
По умолчанию CMake использует ту же версию C++, что и clang (сейчас это C++14). Чтобы использовать функции C++17 или более поздних версий, вам нужно задать для стандарта CMAKE_CXX_STANDARD подходящее значение в файле CMakeLists.txt. Подробную информацию можно найти в документации по CMake для CMAKE_CXX_STANDARD.
ndk-build также по умолчанию оставляет решение за clang, поэтому пользователям ndk-build следует использовать APP_CPPFLAGS, чтобы добавить -std=c++17 или что-то другое.
Общая библиотека для libc++ – libc++_shared.so, а статическая – libc++_static.a. В типичных случаях система сборки будет обрабатывать
использование и упаковку этих библиотек по мере необходимости для пользователя. Если вы используете собственную систему сборки или у вас нестандартный случай, ознакомьтесь с руководством для администраторов систем сборки или руководством по использованию других систем сборки.
Проект LLVM распространяется по лицензии Apache 2.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-build
По умолчанию для 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 ниже 4.3 (уровень 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++
Исключения 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
Как и исключения, 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