Supporto della libreria C++

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 in libbar.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