C++ ライブラリ サポート

NDK は複数の C++ ランタイム ライブラリをサポートしています。 このドキュメントでは、これらのライブラリに関する情報、関連するトレードオフ、およびその使用方法について説明します。

C++ ランタイム ライブラリ

表 1. NDK C++ ランタイムおよび機能。

名前 機能
libc++ C++17 サポート。
system newdelete。 (r18 では非推奨。)
none ヘッダーなし、限定的な C++。

libc++ は静的ライブラリおよび共有ライブラリの両方として利用できます。

libc++

LLVM の libc++ は Lollipop 以降 Android OS で使用されていた C++ 標準ライブラリであり、NDK r18 以降は NDK で唯一利用可能な STL です。

libc++ の共有ライブラリは libc++_shared.so であり、静的ライブラリは libc++_static.a です。

libc++ は、イリノイ大学の「BSD に類似した」 ライセンスと MIT ライセンスの双方の下に使用が許諾されています。 詳細については、ライセンス ファイルをご覧ください。

system

system ランタイムは /system/lib/libstdc++.so を指します。 このライブラリを GNU の全機能を備えた libstdc++ と混同しないでください。 Android では、libstdc++ は newdelete だけです。 全機能を備えた C++ 標準ライブラリには libc++ を使用します。

system C++ ランタイムでは基本的な C++ ランタイム ABI をサポートしています。 基本的に、このライブラリでは newdelete を使用できます。 NDK で利用できる他のオプションとは異なり、例外処理や RTTI のサポートはありません。

<cstdio> など、C ライブラリ ヘッダーの C++ ラッパー以外の標準ライブラリはサポートされていません。 STL が必要な場合は、このページに記載されている他のオプションのいずれかを使用してください。

none

STL を使用しないオプションもあります。 この場合、リンクまたはライセンス要件はありません。 利用できる C++ 標準ヘッダーはありません。

C++ ランタイムの選択

CMake を使用している場合、モジュール レベルの build.gradle ファイルの ANDROID_STL 変数で、表 1 のラインタイムを指定できます。 詳細については、CMake 変数の使用をご覧ください。

ndk-build を使用している場合、Application.mk ファイルの APP_STL 変数で、表 1 のランタイムを指定できます。 次に例を示します。

APP_STL := c++_shared

アプリに選択できるランタイムは 1 つだけで、Application.mk でのみ指定できます。

スタンドアロン ツールチェーンを使用する場合、ツールチェーンはデフォルトで共有 STL を使用します。 静的バリアントを使用するには、-static-libstdc++ をリンカーフラグに追加します。

重要な考慮事項

静的ランタイム

すべてのアプリケーションのネイティブ コードが単一の共有ライブラリにある場合、静的ランタイムを使用することをおすすめします。 これにより、リンカーは可能な限り未使用のコードをインライン化し、切り詰めることができるため、アプリケーションを最大限最適化し、小さくできます。 また、複数の共有ライブラリの処理が困難になり、エラーを引き起こしやすくなる Android の旧バージョンの PackageManager とダイナミック リンカーのバグを回避することもできます。

それでも、C++ では同じ関数またはオブジェクトの複数のコピーを単一のプログラムに定義することは安全ではありません。 これは C++ 標準に存在する 1 つの定義ルール の 1 要素です。

静的ランタイム(一般的には静的ライブラリ)を使用する場合は、意図せずこのルールに違反しがちです。 たとえば、次のアプリケーションはこのルールに違反しています。

# 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 が両方のライブラリにあります。 このアプリケーションのランタイム動作は定義されておらず、実際にはクラッシュがよく発生します。 その他の問題には、次のものがあります。

  • メモリが 1 つのライブラリに割り当てられ、他のライブラリで解放されると、メモリ漏洩またはヒープ破壊が発生する。
  • 例外が libbar.so で検出されずに libfoo.so で報告されると、アプリがクラッシュする。
  • std::cout のバッファリングが正しく動作しない。

関連する動作の問題の他、静的ランタイムを複数のライブラリにリンクすると、それぞれの共有ライブラリでコードが複製され、アプリケーションのサイズが増大するという問題も生じます。

一般に、アプリケーションに共有ライブラリが 1 つのみの場合に限り、C++ ランタイムの静的バリアントを使用できます。

共有ランタイム

アプリケーションに複数の共有ライブラリがある場合は、libc++_shared.so を使用します。

Android では NDK で使用する libc++ は OS の一部ではありません。 これにより、NDK ユーザーは Android の旧バージョンをターゲットにした場合でも、最新の libc++ 機能とバグの修正にアクセスできます。 このトレードオフとして、libc++_shared.so を使用している場合は自分でこれを APK に含める必要があります。 Gradle でアプリケーションをビルドする場合、これは自動的に処理されます。

Android の旧バージョンでは、PackageManager とダイナミック リンカーにバグがあり、これが原因でネイティブ ライブラリのインストール、更新、読み込みの信頼性が低くなります。 特に、アプリが Android 4.3(Android API レベル 18)より前の Android のバージョンをターゲットにしており、libc++_shared.so を使用している場合、共有ライブラリを、それに依存する他のすべてのライブラリよりも先に読み込む必要があります。

ReLinker プロジェクトでは、既知のすべてのネイティブ ライブラリの読み込みの問題に対する回避策を提供しており、通常は自分で回避策を記述するよりも適切な選択です。

アプリごとに 1 つの STL

これまで、NDK は libc++ の他に GNU libstdc++ と STLport をサポートしてきました。 アプリケーションが、そのビルドに使用されたものとは別の NDK を基にビルドされた、ビルド済みライブラリに依存している場合、その互換性を確認する必要があります。

1 つのアプリケーションで複数の C++ ランタイムを使用しないでください。 それぞれの STL 間には互換性がありません。 たとえば、libc++ と gnustl では std::string のレイアウトが異なります。 ある STL に対して記述されたコードで、別の STL に対して記述されたオブジェクトを使用することはできません。 これは単なる一例ですが、多数の非互換性があります。

このルールはコード以外にも適用されます。 依存関係には、すべて同じ STL を選択して使用する必要があります。 STL を使用し、STL ごとにライブラリを提供しない、クローズド ソースのサードパーティ依存関係を利用している場合は、STL を選択することはできません。 依存関係と同じ STL を使用する必要があります。

2 つの相互に互換性のないライブラリに依存することができます。 この状況では、いずれかの依存関係を削除するか、他の 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 が有効になっています。

ndk-build 内のアプリケーション全体で RTTI を有効にするには、以下の行を Application.mk ファイルに追加します。

APP_CPPFLAGS := -frtti

単一の ndk-build モジュールで RTTI を有効にするには、以下の行を Android.mk 内の指定のモジュールに追加します。

LOCAL_CPP_FEATURES := rtti

または、次の行を使用できます。

LOCAL_CPPFLAGS := -frtti