C++ 라이브러리 지원

NDK는 여러 C++ 런타임 라이브러리를 지원합니다. 이 문서는 이러한 라이브러리, 관련 절충점, 사용 방법에 관한 정보를 제공합니다.

C++ 런타임 라이브러리

표 1. NDK C++ 런타임 및 기능

이름 기능
libc++ C++17 지원
시스템 newdelete (r18에서 지원 중단됨)
없음 헤더 없음. 제한된 C++

libc++는 정적 라이브러리와 공유 라이브러리 두 가지를 모두 사용할 수 있습니다.

libc++

LLVM의 libc++는 Lollipop 이후 Android OS에서 사용되는 C++ 표준 라이브러리이며 NDK r18부터는 NDK에서 사용할 수 있는 유일한 STL입니다.

libc++의 공유 라이브러리는 libc++_shared.so이며 정적 라이브러리는 libc++_static.a입니다.

libc++는 일리노이 대학교 'BSD-Like' 라이선스 및 MIT 라이선스에 따라 이중 라이선스를 부여받았습니다. 자세한 내용은 라이선스 파일을 참조하세요.

시스템

시스템 런타임은 /system/lib/libstdc++.so를 참조합니다. 이 라이브러리를 GNU의 모든 기능을 갖춘 libstdc++와 혼동해서는 안 됩니다. Android에서 libstdc++는 newdelete뿐입니다. 모든 기능을 갖춘 C++ 표준 라이브러리에는 libc++를 사용하세요.

시스템 C++ 런타임은 기본 C++ 런타임 ABI를 지원합니다. 기본적으로 이 라이브러리에서는 newdelete를 제공합니다. NDK에서 사용할 수 있는 다른 옵션과 대조적으로 예외 처리나 RTTI는 지원되지 않습니다.

<cstdio>와 같은 C 라이브러리 헤더용 C++ 래퍼 외에는 표준 라이브러리 지원이 없습니다. 따라서 STL을 원한다면 이 페이지에 제시된 다른 옵션 중 하나를 사용해야 합니다.

없음

STL이 없는 옵션도 있습니다. 이런 상황에서는 링크 또는 라이선스 요구사항이 없으며 이용할 수 있는 C++ 표준 헤더도 없습니다.

C++ 런타임 선택

CMake를 사용한다면 모듈 수준 build.gradle 파일의 ANDROID_STL 변수로 표 1의 런타임을 지정할 수 있습니다. 자세히 알아보려면 CMake 변수 사용을 참조하세요.

ndk-build를 사용한다면 Application.mk 파일의 APP_STL 변수로 표 1의 런타임을 지정할 수 있습니다. 예:

APP_STL := c++_shared

앱의 런타임은 한 개만 선택할 수 있으며 Application.mk에서만 선택할 수 있습니다.

독립 실행형 도구 모음 사용 시 도구 모음에서는 기본적으로 공유 STL을 사용합니다. 정적 변형을 사용하려면 링커 플래그에 -static-libstdc++를 추가하세요. 이 옵션은 'libstdc++'라는 이름을 사용하지만 libc++도 사용할 수 있습니다.

중요한 고려사항

정적 런타임

애플리케이션의 모든 네이티브 코드가 단일 공유 라이브러리에 포함되어 있으면 정적 런타임을 사용하는 것이 좋습니다. 정적 런타임을 사용하면 링커가 가능한 한 사용되지 않은 코드를 인라인 처리하거나 제거하여 가장 작고 최적화된 애플리케이션을 만들 수 있습니다. 또한 다중 공유 라이브러리 처리를 어렵게 만들고 오류를 자주 발생시키던 이전 Android 버전의 PackageManager 및 동적 링커 버그를 피할 수 있습니다.

그렇지만 C++에서 단일 프로그램에 동일한 함수나 객체를 둘 이상 정의하는 것은 안전하지 않습니다. 이는 C++ 표준에 제시된 단일 정의 규칙의 한 측면입니다.

정적 런타임(및 일반적으로 정적 라이브러리)을 사용할 때 이 규칙을 무심결에 간과하기 쉽습니다. 예를 들어 다음 애플리케이션은 이 규칙에 위배됩니다.

# 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이 양쪽 라이브러리에 표시됩니다. 이 애플리케이션의 런타임 동작은 정의되지 않았으며 실제로는 비정상 종료가 매우 흔하게 일어납니다. 그밖에 발생할 수 있는 문제는 다음과 같습니다.

  • 한 라이브러리에 할당된 메모리가 다른 라이브러리에서 해제되어 메모리 누수 또는 힙 손상이 발생합니다.
  • libfoo.so에서 발생한 예외가 libbar.so에서 포착되지 않아 앱이 비정상 종료됩니다.
  • std::cout의 버퍼링이 제대로 작동하지 않습니다.

관련 동작 문제 외에도 정적 런타임을 여러 라이브러리에 연결하면 각 공유 라이브러리의 코드가 중복되어 애플리케이션의 크기가 커집니다.

일반적으로 애플리케이션에 공유 라이브러리가 한 개만 있으면 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 프로젝트에서는 알려진 모든 네이티브 라이브러리 로드 문제 해결 방법을 제공하므로 이 프로젝트를 선택하는 것이 자체 해결 방법을 작성하는 것보다 더 낫습니다.

앱당 하나의 STL

예전부터 NDK는 libc++뿐만 아니라 GNU libstdc++ 및 STLport도 지원했습니다. 애플리케이션이 애플리케이션을 빌드하는 데 사용한 것과 다른 NDK에서 미리 빌드된 라이브러리를 사용한다면 애플리케이션이 호환 가능한 방식으로 라이브러리를 사용하도록 해야 합니다.

애플리케이션에서 둘 이상의 C++ 런타임을 사용하면 안 됩니다. 다양한 STL은 서로 호환되지 않습니다. 예를 들어 libc++의 std::string 레이아웃은 gnustl의 레이아웃과 동일하지 않습니다. 한 STL에서 작성된 코드는 다른 STL에서 작성된 객체를 사용할 수 없습니다. 이런 상황은 하나의 예에 불과하며 비호환성 사례는 수도 없이 많습니다.

이 규칙은 작성한 코드에만 적용되는 것이 아닙니다. 모든 종속 항목은 선택된 것과 동일한 STL을 사용해야 합니다. STL을 사용하고 STL별 라이브러리를 제공하지 않는 비공개 소스 타사 종속 항목에 의존한다면 STL 선택권이 없습니다. 즉, 종속 항목과 동일한 STL을 사용해야 합니다.

서로 호환되지 않는 두 개의 라이브러리를 사용할 수도 있습니다. 이 상황에서 유일한 해결책은 종속 항목 중 하나를 제외하거나 운영자에게 다른 STL에 빌드된 라이브러리를 제공하도록 요청하는 것입니다.

C++ 예외

libc++에서 C++ 예외를 지원하지만 ndk-build에서는 기본적으로 이러한 예외가 사용 중지되어 있습니다. 이전부터 NDK에서는 C++ 예외를 사용할 수 없었기 때문입니다. CMake 및 독립 실행형 도구 모음에는 기본적으로 C++ 예외가 사용 설정되어 있습니다.

ndk-build의 전체 애플리케이션에서 예외를 사용 설정하려면 Application.mk 파일에 다음 행을 추가하세요.

APP_CPPFLAGS := -fexceptions

단일 ndk-build 모듈에 예외를 사용 설정하려면 Android.mk의 지정된 모듈에 다음 행을 추가하세요.

LOCAL_CPP_FEATURES := exceptions

또는 다음을 사용할 수 있습니다.

LOCAL_CPPFLAGS := -fexceptions

RTTI

예외와 마찬가지로 libc++에서 RTTI를 지원하지만 ndk-build에서는 기본적으로 RTTI 사용이 중지되어 있습니다. CMake 및 독립 실행형 도구 모음에는 RTTI가 기본적으로 사용 설정되어 있습니다.

ndk-build의 전체 애플리케이션에서 RTTI를 사용 설정하려면 Application.mk 파일에 다음 행을 추가하세요.

APP_CPPFLAGS := -frtti

단일 ndk-build 모듈에 RTTI를 사용 설정하려면 Android.mk의 지정된 모듈에 다음 행을 추가하세요.

LOCAL_CPP_FEATURES := rtti

또는 다음을 사용할 수 있습니다.

LOCAL_CPPFLAGS := -frtti