O NDK é compatível com várias bibliotecas de ambiente de execução C++. Este documento traz informações sobre essas bibliotecas, como usá-las e as desvantagens de cada uma.
Bibliotecas de ambiente de execução C++
Tabela 1. Ambientes de execução e recursos do NDK C++.
Nome | Recursos |
---|---|
libc++ | Compatibilidade com C++ moderno. |
system | new e delete . (Obsoleto em r18.) |
none | Nenhum cabeçalho, C ++ limitado. |
A libc++ está disponível como biblioteca estática e compartilhada.
libc++
A libc++ do LLVM (link em inglês) é uma biblioteca C++ padrão usada pelo SO Android desde a versão Lollipop. Desde o NDK r18, ela é a única STL disponível no NDK.
Por padrão, o CMake usa qualquer versão de C++ definida como padrão pelo Clang (atualmente, o C++14).
Portanto, você precisará definir o CMAKE_CXX_STANDARD
padrão como o valor adequado
no arquivo CMakeLists.txt
para usar recursos do C++17 ou versões mais recentes. Acessar o CMake
documentação sobre CMAKE_CXX_STANDARD
para mais detalhes.
O ndk-build também deixa a decisão para o Clang por padrão. Então, os usuários do ndk-build
precisam usar APP_CPPFLAGS
para adicionar -std=c++17
ou qualquer outra opção.
A biblioteca compartilhada para libc++ é libc++_shared.so
, e a
estática é libc++_static.a
. Em casos típicos, o sistema de compilação processará
o uso e o empacotamento dessas bibliotecas conforme necessário para o usuário. Para casos atípicos
ou ao implementar seu próprio sistema de compilação, consulte o Guia de mantenedores
do sistema de compilação ou o guia para usar outros sistemas de compilação.
O projeto LLVM está sob a licença Apache v2.0 com exceções do LLVM. Para saber mais, consulte o arquivo de licença (link em inglês).
system
O ambiente de execução system refere-se a /system/lib/libstdc++.so
. Essa biblioteca não deve ser confundida com a libstdc ++ completa do GNU. No Android, libstdc ++ é apenas new
e delete
. Use a libc++ como uma biblioteca C++ padrão completa.
O ambiente de execução system do C++ oferece compatibilidade com a ABI básica do ambiente de execução C++.
Simplificando, essa biblioteca fornece new
e delete
. Diferentemente de outras opções disponíveis no NDK, não há compatibilidade com o gerenciamento de exceções ou com RTTI.
Não há compatibilidade com a biblioteca padrão além dos wrappers C++ para os cabeçalhos da biblioteca C, como <cstdio>
. Se quiser uma STL, use uma das outras opções apresentadas nesta página.
none
Há também a opção de não ter nenhuma STL. Não há requisitos de vinculação ou licenciamento nesse caso. Não há cabeçalhos C++ padrão disponíveis.
Selecionar um ambiente de execução C++
CMake
O padrão para o CMake é c++_static
.
É possível especificar c++_shared
, c++_static
, none
ou system
com a
variável ANDROID_STL
no arquivo build.gradle
do módulo. Para saber mais,
consulte a documentação do ANDROID_STL no
CMake.
ndk-build
O padrão para o ndk-build é none
.
Você pode especificar c++_shared
, c++_static
, none
ou system
com a
variável APP_STL
no arquivo Application.mk. Exemplo:
APP_STL := c++_shared
O ndk-build só permite selecionar um ambiente de execução para seu app e apenas em Application.mk.
Usar clang diretamente
Se você estiver usando clang diretamente no seu sistema de compilação, o clang++ usará
c++_shared
por padrão. Para usar a variante estática, adicione -static-libstdc++
às
sinalizações do vinculador. Observe que, embora a opção use o nome "libstdc++" por
motivos históricos, o uso de libc++ também está correto.
Considerações importantes
Ambientes de execução estáticos
Se todo o código nativo do seu app está em uma única biblioteca compartilhada, recomendamos o uso do ambiente de execução estático. Desse modo, o vinculador poderá incorporar e suprimir o máximo possível de código não usado, gerando um app otimizado e de tamanho reduzido. Isso também evita que o PackageManager e o vinculador dinâmico apresentem bugs em versões antigas do Android que dificultam o gerenciamento de várias bibliotecas compartilhadas e aumentam a propensão a erros.
Portanto, em C++, não é seguro definir mais de uma cópia da mesma função ou objeto em um único programa. Esse é um aspecto da Regra de uma definição (link em inglês) presente no padrão C++.
Ao usar um ambiente de execução estático (e bibliotecas estáticas em geral), é fácil violar essa regra acidentalmente. Por exemplo, o app a seguir viola essa regra:
# 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)
Nesse caso, a STL, incluindo dados globais e construtores estáticos, estará presente nas duas bibliotecas. O comportamento do ambiente de execução desse app é indefinido, e as falhas são muito comuns na prática. Outros possíveis problemas incluem:
- A memória alocada em uma biblioteca e liberada em outra causa vazamento de memória ou corrupção do heap.
- As exceções geradas em
libfoo.so
deixarão de ser detectadas emlibbar.so
, fazendo com que seu app falhe. - O buffer de
std::cout
não funciona corretamente.
Além dos problemas comportamentais envolvidos, vincular o ambiente de execução estático a várias bibliotecas duplicará o código em todas as bibliotecas compartilhadas, aumentando o tamanho do app.
Em geral, só será possível usar uma variante estática do ambiente de execução C++ se houver somente uma biblioteca compartilhada no seu app.
Ambientes de execução compartilhados
Caso seu app inclua várias bibliotecas compartilhadas, use
libc++_shared.so
.
No Android, a libc++ usada pelo NDK não é a mesma que faz parte
do SO. Isso dá aos usuários do NDK acesso aos recursos e correções
de bugs mais recentes da libc++, mesmo em versões antigas do Android. A desvantagem é que, se você
usar a libc++_shared.so
, será necessário incluí-la no app. Se estiver criando o
app no Gradle, isso será feito automaticamente.
As versões antigas do Android tinham bugs no PackageManager e no vinculador dinâmico
que faziam com que a instalação, a atualização e o carregamento de bibliotecas nativas não fossem feitos
de modo confiável. Principalmente se o app for destinado a uma versão do Android anterior à
4.3 (API de nível 18 do Android) e você usar libc++_shared.so
,
será necessário carregar a biblioteca compartilhada antes de qualquer outra biblioteca que dependa dela.
O projeto ReLinker oferece soluções para todos os problemas de carregamento de biblioteca nativa conhecidos e costuma ser uma opção melhor do que desenvolver soluções próprias.
Uma STL por app
Anteriormente, o NDK oferecia suporte às bibliotecas libstdc++ e STLport do GNU, além da libc++. Se o app depende de bibliotecas pré-criadas em um NDK diferente daquele usado para criar seu app, garanta que ele faça isso de maneira compatível.
Os apps não devem usar mais de um ambiente de execução C++. As várias STLs não são compatíveis entre si. Por exemplo, o layout de std::string
na libc++ não é o mesmo que em gnustl. Códigos escritos com uma STL não poderão usar objetos escritos com outra. Esse é apenas um exemplo.
Existem muitas outras incompatibilidades.
Essa regra não se aplica somente ao seu código. Todas as dependências precisam usar a mesma STL que você selecionou. Se você precisa de uma dependência terceirizada de software proprietário que usa a STL e não fornece uma biblioteca por STL, não é possível escolher a STL. Use a mesma STL como sua dependência.
É possível depender de duas bibliotecas incompatíveis entre si. Nessa situação, as únicas soluções são eliminar uma das dependências ou solicitar que o responsável forneça uma biblioteca compilada com a outra STL.
Exceções de C++
A libc++ é compatível com exceções de C++, mas elas são desativadas por padrão no ndk-build. Isso ocorre porque, anteriormente, as exceções C++ não estavam disponíveis no NDK. O CMake e os conjuntos de ferramentas autônomas têm exceções C++ ativadas por padrão.
Para ativar exceções em todo o app no ndk-build, adicione a seguinte linha ao arquivo Application.mk:
APP_CPPFLAGS := -fexceptions
Para ativar exceções em um único módulo do ndk-build, adicione a seguinte linha ao módulo correspondente no Android.mk:
LOCAL_CPP_FEATURES := exceptions
Como alternativa, você pode usar:
LOCAL_CPPFLAGS := -fexceptions
RTTI
Assim como as exceções, a libc++ é compatível com RTTI, mas esse recurso é desativado por padrão no ndk-build. A RTTI é ativada por padrão no CMake e nos conjuntos de ferramentas autônomos.
Para ativar a RTTI em todo o app no ndk-build, adicione a seguinte linha ao arquivo Application.mk:
APP_CPPFLAGS := -frtti
Para ativar a RTTI em um único módulo do ndk-build, adicione a seguinte linha ao módulo correspondente no Android.mk:
LOCAL_CPP_FEATURES := rtti
Como alternativa, você pode usar:
LOCAL_CPPFLAGS := -frtti