Obsługa biblioteki C++

Pakiet NDK obsługuje wiele bibliotek środowiska wykonawczego C++. Ten dokument zawiera na temat tych bibliotek, związanych z nimi kompromisów i sposobów korzystania z nich.

Biblioteki środowiska wykonawczego C++

Tabela 1. Środowiska wykonawcze i funkcje NDK C++.

Nazwa Funkcje
libc + Nowoczesna obsługa języka C++.
system newdelete. (Wycofane w wersji r18).
brak Bez nagłówków, ograniczony język C++.

libc++ jest dostępna zarówno jako biblioteka statyczna, jak i współdzielona.

libc++

libc++ LLVM to standard w C++ biblioteka używana w systemie operacyjnym Android od wersji Lollipop, a od NDK r18 to jedyny STL dostępny w NDK.

Domyślna wersja clangu jest domyślnie ustawiona na C++ (obecnie C++14). musisz więc ustawić standardową wartość CMAKE_CXX_STANDARD na odpowiednią wartość w pliku CMakeLists.txt, aby używać funkcji C++17 lub nowszych. Poznaj CMake dokumentacja usługi CMAKE_CXX_STANDARD .

ndk-build również powoduje domyślne reagowanie na dźwięk, więc użytkownicy powinni użyć elementu APP_CPPFLAGS, aby dodać element -std=c++17 lub cokolwiek innego.

Zasoby wspólne biblioteki libc++ to libc++_shared.so, a biblioteka statyczna biblioteka jest libc++_static.a. W typowych przypadkach system kompilacji używanie i opakowanie tych bibliotek zgodnie z potrzebami użytkownika. W nietypowych przypadkach lub gdy wdrażasz własny system kompilacji, przeczytaj artykuł Konserwacja do zarządzania systemem Przewodnik lub przewodnik po korzystaniu z innych systemów kompilacji.

Projekt LLVM jest objęty licencją Apache w wersji 2.0 z wyjątkami LLVM. Więcej informacje znajdziesz w pliku licencji.

system

Środowisko wykonawcze systemu odnosi się do /system/lib/libstdc++.so. Ta biblioteka nie powinna z kompleksową domeną GNU libstdc++. W Androidzie libstdc++ to po prostu new i delete. Aby utworzyć bibliotekę standardową C++, użyj biblioteki libc++.

Systemowe środowisko wykonawcze C++ zapewnia obsługę podstawowego interfejsu ABI środowiska wykonawczego C++. Zasadniczo ta biblioteka zawiera new i delete. W przeciwieństwie do dostępnych w NDK, nie jest obsługiwana obsługa wyjątków ani RTTI.

Nie ma biblioteki standardowej poza kodami języka C++ dla języka C takich jak <cstdio>. Jeśli chcesz używać protokołu STL, użyj jednej z inne opcje dostępne na tej stronie.

brak

Można też wybrać opcję bez STL. Nie ma żadnych połączeń ani licencji wymagania w tym przypadku. Brak dostępnych standardowych nagłówków w języku C++.

Wybieranie środowiska wykonawczego C++

CMake

Domyślna wartość w przypadku CMake to c++_static.

Możesz określić c++_shared, c++_static, none lub system za pomocą ANDROID_STL w pliku build.gradle na poziomie modułu. Aby dowiedzieć się więcej, zapoznaj się z dokumentacją dla ANDROID_STL w CMake.

ndk-build

Wartość domyślna dla ndk-build to none.

Możesz określić c++_shared, c++_static, none lub system za pomocą APP_STL w pliku Application.mk. Na przykład:

APP_STL := c++_shared

Polecenie ndk-build pozwala wybrać tylko jedno środowisko wykonawcze aplikacji i można to zrobić Application.mk.

Używaj clang bezpośrednio

Jeśli używasz clang bezpośrednio we własnym systemie kompilacji, clang++ będzie używać c++_shared. Aby używać wariantu statycznego, dodaj atrybut -static-libstdc++ do flagi tagu łączącego. Mimo że opcja ma nazwę „libstdc++”, w przypadku z powodów historycznych, działa to również w przypadku libc++.

Ważne informacje

Statyczne środowiska wykonawcze

Jeśli cały kod natywny aplikacji znajduje się w jednym udostępnionym zalecamy używanie statycznego środowiska wykonawczego. Dzięki temu tag łączący i usuń jak najwięcej nieużywanego kodu. i przy użyciu jak najmniejszej możliwości. Unika także metody PackageManager i zarządzania błędy łączące w starszych wersjach Androida, które sprawiają, że obsługa wielu plików i biblioteki są trudne i podatne na błędy.

Jednak w C++ nie jest bezpieczne zdefiniowanie więcej niż jednej kopii tej samej funkcję lub obiekt w jednym programie. To jeden z aspektów programu, Jedna reguła definicji dostępna w standardzie C++.

W przypadku statycznego środowiska wykonawczego (i ogólnie bibliotek statycznych) łatwo jest przypadkowo złam tę regułę. Na przykład poniższa aplikacja powoduje przerwanie tego reguła:

# 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)

W tej sytuacji STL, w tym dane globalne i konstruktory statyczne, będzie dostępna w obu bibliotekach. Działanie tej aplikacji w czasie działania to nieokreślonych, a w praktyce zdarzają się one bardzo często. Inne możliwe problemy uwzględnij:

  • Pamięć jest przydzielona w jednej bibliotece, a zwolniona w drugiej, przez co pamięć jest przydzielona w jednej bibliotece wyciek lub uszkodzenie stosu.
  • Wyjątki zgłoszone w regionie libfoo.so, które nie zostały wykryte w regionie libbar.so, powodując do awarii.
  • Buforowanie pliku std::cout nie działa prawidłowo.

Oprócz problemów z działaniem połączenie statycznego środowiska wykonawczego w kilka zduplikują kod w każdej z bibliotek wspólnych, co zwiększy rozmiar Twojej aplikacji.

Ogólnie możesz używać statycznego wariantu środowiska wykonawczego C++ tylko wtedy, gdy go masz i tylko jedną bibliotekę współdzieloną.

Współdzielone środowiska wykonawcze

Jeśli Twoja aplikacja zawiera wiele bibliotek udostępnionych, użyj libc++_shared.so

W Androidzie biblioteka libc++ używana w NDK różni się od biblioteki, która jest częścią systemu operacyjnego. Daje to użytkownikom NDK dostęp do najnowszych funkcji libc++ i błędów nawet w przypadku kierowania na starsze wersje Androida. A kompromis jest taki, że używasz języka libc++_shared.so, musisz uwzględnić go w swojej aplikacji. Jeśli tworzysz z użyciem Gradle. Jest to obsługiwane automatycznie.

Starsze wersje Androida zawierały błędy w Menedżerze pakietów i dynamicznym narzędziu łączącym które spowodowało instalację, aktualizację i wczytywanie bibliotek natywnych zawodny. Zwłaszcza, jeśli Twoja aplikacja jest kierowana na starszą wersję Androida niż w Androidzie 4.3 (poziom interfejsu API Androida 18) i używasz libc++_shared.so, musi wczytywać tę bibliotekę przed innymi bibliotekami, które jej wymagają.

Projekt ReLinker zapewnia obejścia wszystkich znanych problemów z ładowaniem biblioteki natywnej. Zazwyczaj jest to to lepsze rozwiązanie niż pisanie własnych rozwiązań.

Jeden STL na aplikację

Dawniej NDK obsługiwało pakiety GNU libstdc++ i STLport, a nie tylko libc++. Jeśli Twoja aplikacja zależy od gotowych bibliotek, które zostały skompilowane w oparciu o pakiet NDK różni się od tej użytej do skompilowania aplikacji, musisz upewnić się, że robi to w zgodny sposób.

Aplikacja nie powinna używać więcej niż jednego środowiska wykonawczego C++. Różne języki STL są nie są zgodne. Przykład: układ strony std::string w libc++ i w Ggnustl to nie to samo. Kod napisany przeciwko jednemu kodowi STL nie można używać obiektów zapisanych przeciwko innym. To tylko jeden przykład. występuje wiele niezgodności.

Ta reguła wykracza poza Twój kod. Wszystkie zależności muszą korzystać z tego samego wybrany język STL. Jeśli korzystasz z usług zewnętrznej firmy open source używająca języka STL i nie podaje biblioteki według tego języka, nie trzeba nie ma wyboru w STL. Musisz użyć tego samego protokołu STL co zależność.

Może się zdarzyć, że będziesz korzystać z 2 niekompatybilnych bibliotek. W w tej sytuacji jedynym rozwiązaniem jest porzucenie jednej z zależności lub poproszenie aby udostępnić bibliotekę stworzoną na tle innych STL.

Wyjątki C++

Wyjątki dla języka C++ są obsługiwane przez libc++, ale są domyślnie wyłączone w ndk-build. Dzieje się tak, ponieważ wyjątki dotyczące języka C++ nie były wcześniej dostępne w NDK. CMake i samodzielne łańcuchy narzędzi mają domyślnie włączone wyjątki w języku C++.

Aby włączyć wyjątki w całej aplikacji w narzędziu ndk-build, dodaj funkcję ten wiersz do pliku Application.mk:

APP_CPPFLAGS := -fexceptions

Aby włączyć wyjątki dla pojedynczego modułu ndk-build, dodaj następujący wiersz do danego modułu w pliku Android.mk:

LOCAL_CPP_FEATURES := exceptions

Możesz też użyć:

LOCAL_CPPFLAGS := -fexceptions

RTTI

Podobnie jak w przypadku wyjątków, protokół RTTI jest obsługiwany przez libc++, ale jest domyślnie wyłączony ndk-build. CMake i samodzielne łańcuchy narzędzi mają domyślnie włączoną funkcję RTTI.

Aby włączyć RTTI w całej aplikacji w narzędziu ndk-build, dodaj następujący tekst do pliku Application.mk:

APP_CPPFLAGS := -frtti

Aby włączyć RTTI dla pojedynczego modułu ndk-build, dodaj następujący wiersz do danego modułu w pliku Android.mk:

LOCAL_CPP_FEATURES := rtti

Możesz też użyć:

LOCAL_CPPFLAGS := -frtti