L'NDK supporta più librerie di runtime C++. Questo documento fornisce informazioni su queste librerie, sui relativi compromessi e su come utilizzarle.
Librerie di runtime C++
Tabella 1. Runtime e funzionalità NDK C++.
Nome | Funzionalità |
---|---|
libc++ | Supporto di C++ moderno. |
Pixel | new e delete . (Obsoleto nella versione r18.) |
none | Nessuna intestazione, con C++ limitato. |
libc++ è disponibile sia come libreria statica sia come libreria condivisa.
libc++
libc++ di LLVM è la libreria standard di C++ utilizzata dal sistema operativo Android a partire da Lollipop, mentre a partire da NDK r18 è l'unica libreria STL disponibile nell'NDK.
Imposta come predefinito qualsiasi versione del clang C++ utilizzata per impostazione predefinita (attualmente C++14),
quindi dovrai impostare CMAKE_CXX_STANDARD
standard sul valore appropriato
nel file CMakeLists.txt
per utilizzare le funzionalità C++17 o successive. Per ulteriori dettagli, consulta la documentazione di CMake per CMAKE_CXX_STANDARD
.
Inoltre, ndk-build lascia la decisione al clang per impostazione predefinita, quindi gli utenti di ndk-build
devono usare APP_CPPFLAGS
per aggiungere -std=c++17
o qualsiasi altra cosa che preferiscono.
La libreria condivisa per libc++ è libc++_shared.so
, mentre la libreria statica è libc++_static.a
. In casi tipici, il sistema di build gestisce l'utilizzo e la pacchettizzazione di queste librerie in base alle esigenze dell'utente. Per casi atipici o quando implementi il tuo sistema di build, consulta la Guida per i manutentori del sistema di build o la guida per l'utilizzo di altri sistemi di build.
Il progetto LLVM è soggetto alla licenza Apache v2.0 con eccezioni LLVM. Per ulteriori informazioni, consulta il file di licenza.
di infotainment
Il runtime del sistema si riferisce a /system/lib/libstdc++.so
. Questa libreria non deve essere confusa con la versione completa di libstdc++ di GNU. Su Android, libstdc++ è solo new
e delete
. Utilizza libc++ per una libreria standard di C++ completa.
Il runtime C++ del sistema fornisce supporto per l'ABI di base del runtime C++.
Essenzialmente, questa libreria fornisce new
e delete
. A differenza delle altre opzioni disponibili nell'NDK, non è previsto alcun supporto per la gestione delle eccezioni o RTTI.
Non esiste supporto di librerie standard oltre ai wrapper C++ per le intestazioni della libreria C come <cstdio>
. Se desideri un STL, devi utilizzare una delle altre opzioni presentate in questa pagina.
Nessuna selezione
È anche possibile non avere un codice STL. In questo caso non ci sono requisiti di collegamento o licenza. Nessuna intestazione C++ standard disponibile.
Selezione di un runtime C++
Marca
Il valore predefinito per CMake è c++_static
.
Puoi specificare c++_shared
, c++_static
, none
o system
utilizzando la variabile ANDROID_STL
nel file build.gradle
a livello di modulo. Per ulteriori informazioni, consulta la documentazione relativa a ANDROID_STL in CMake.
build-ndk
Il valore predefinito di ndk-build è none
.
Puoi specificare c++_shared
, c++_static
, none
o system
utilizzando la variabile APP_STL
nel file Application.mk. Ecco alcuni esempi:
APP_STL := c++_shared
ndk-build ti consente di selezionare un solo runtime per la tua app e può farlo solo in Application.mk.
Usa direttamente il clang
Se utilizzi clang direttamente nel tuo sistema di build, clang++ utilizzerà
c++_shared
per impostazione predefinita. Per utilizzare la variante statica, aggiungi -static-libstdc++
ai flag linker. Tieni presente che, sebbene l'opzione utilizzi il nome "libstdc++" per motivi storici, è corretto anche per libc++.
Considerazioni importanti
Runtime statici
Se tutto il codice nativo dell'applicazione è contenuto in un'unica libreria condivisa, ti consigliamo di utilizzare il runtime statico. In questo modo, il linker può incorporare e eliminare il maggior numero possibile di codice inutilizzato, ottenendo l'applicazione più ottimizzata e più piccola possibile. Inoltre, evita i bug di PackageManager e del linker dinamico nelle versioni precedenti di Android che rendono difficile e soggetto a errori la gestione di più librerie condivise.
Detto questo, in C++ non è sicuro definire più di una copia della stessa funzione o dello stesso oggetto in un singolo programma. Questo è un aspetto della regola unica di definizione presente nello standard C++.
Quando si utilizza un runtime statico (e librerie statiche in generale), è facile violare accidentalmente questa regola. Ad esempio, la seguente applicazione infrange questa regola:
# 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)
In questa situazione, il codice STL, inclusi i costruttori di dati e statici globali, sarà presente in entrambe le librerie. Il comportamento di runtime di questa applicazione non è definito e, in pratica, gli arresti anomali sono molto comuni. Altri possibili problemi includono:
- Memoria allocata in una libreria e liberata nell'altra, causando perdite di memoria o danneggiamento dell'heap.
- Le eccezioni sollevate in
libfoo.so
che non vengono rilevate inlibbar.so
causano l'arresto anomalo dell'app. - Il buffering di
std::cout
non funziona correttamente.
Oltre ai problemi comportamentali coinvolti, il collegamento del runtime statico a più librerie duplicherà il codice in ogni libreria condivisa, aumentando le dimensioni della tua applicazione.
In generale, puoi utilizzare una variante statica del runtime C++ solo se nell'applicazione è presente una sola libreria condivisa.
Runtime condivisi
Se la tua applicazione include più librerie condivise, devi utilizzare
libc++_shared.so
.
Su Android, libc++ utilizzato dall'NDK non è uguale a quello che fa parte del sistema operativo. In questo modo gli utenti di NDK possono accedere alle ultime funzionalità e correzioni di bug libc++ anche se scelgono come target versioni precedenti di Android. Tuttavia, se utilizzi libc++_shared.so
, devi includerlo nella tua app. Se stai creando la tua applicazione con Gradle, questo viene gestito automaticamente.
Le versioni precedenti di Android presentavano bug in PackageManager e nel linker dinamico, che rendevano inaffidabili l'installazione, l'aggiornamento e il caricamento delle librerie native. In particolare, se la tua app ha come target una versione di Android precedente ad Android 4.3 (livello API Android 18) e utilizzi libc++_shared.so
, devi caricare la libreria condivisa prima di qualsiasi altra libreria che dipende da questa libreria.
Il progetto ReLinker offre soluzioni per tutti i problemi di caricamento delle librerie native noti e di solito è una scelta migliore rispetto alla creazione di soluzioni alternative personalizzate.
Un STL per app
Storicamente, l'NDK supportava GNU libstdc++ e STLport oltre a libc++. Se la tua applicazione dipende da librerie predefinite create sulla base di un NDK diverso da quello utilizzato per creare l'applicazione, dovrai assicurarti che lo faccia in modo compatibile.
Un'applicazione non deve utilizzare più di un runtime C++. I vari STL non sono compatibili tra loro. Ad esempio, il layout di std::string
in libc++ non è uguale a quello in gnustl. Il codice scritto su un STL
non potrà utilizzare gli oggetti scritti su un altro. Questo è solo un esempio.
Le incompatibilità sono numerose.
Questa regola va oltre il tuo codice. Tutte le dipendenze devono utilizzare lo stesso STL selezionato. Se dipendi da una dipendenza di terze parti da un'origine chiusa che utilizza il codice STL e non fornisce una libreria per STL, non puoi scegliere questo tipo di codice. Devi utilizzare lo stesso STL della dipendenza.
È possibile che tu dipenda da due librerie reciprocamente incompatibili. In questa situazione, le uniche soluzioni sono eliminare una delle dipendenze o chiedere al manutentore di fornire una libreria basata sull'altro STL.
Eccezioni C++
Le eccezioni C++ sono supportate da libc++, ma sono disabilitate per impostazione predefinita in ndk-build. Questo perché storicamente le eccezioni C++ non erano disponibili nell'NDK. Per impostazione predefinita, le eccezioni C++ sono abilitate per CMake e per le Toolchain autonome.
Per abilitare le eccezioni nell'intera applicazione in ndk-build, aggiungi la seguente riga al file Application.mk:
APP_CPPFLAGS := -fexceptions
Per abilitare le eccezioni per un singolo modulo ndk-build, aggiungi la seguente riga al modulo in questione nel relativo Android.mk:
LOCAL_CPP_FEATURES := exceptions
In alternativa, puoi utilizzare:
LOCAL_CPPFLAGS := -fexceptions
RTTI
Come accade con le eccezioni, RTTI è supportato da libc++, ma è disabilitato per impostazione predefinita in ndk-build. RTTI è attivo per impostazione predefinita per CMake e per le Toolchain autonome.
Per attivare RTTI nell'intera applicazione in ndk-build, aggiungi la seguente riga al file Application.mk:
APP_CPPFLAGS := -frtti
Per abilitare RTTI per un singolo modulo ndk-build, aggiungi la seguente riga al modulo specificato nel relativo Android.mk:
LOCAL_CPP_FEATURES := rtti
In alternativa, puoi utilizzare:
LOCAL_CPPFLAGS := -frtti