การสนับสนุนไลบรารี C++

NDK รองรับไลบรารีรันไทม์ C++ หลายไลบรารี เอกสารนี้มี ข้อมูลเกี่ยวกับไลบรารีเหล่านี้ ข้อดีและข้อเสียที่เกี่ยวข้อง และวิธีใช้งาน

ไลบรารีรันไทม์ C++

ตาราง 1 รันไทม์และฟีเจอร์ NDK C++

ชื่อ ฟีเจอร์
libc++ รองรับ C++ ที่ทันสมัย
ระบบ new และdelete (เลิกใช้งานใน r18)
ไม่มี ไม่มีส่วนหัว, C++ ที่จำกัด

libc++ สามารถใช้ได้เป็นทั้งไลบรารีแบบคงที่และไลบรารีที่ใช้ร่วมกัน

libc++

LLVM libc++ คือมาตรฐาน C++ ไลบรารีที่ระบบปฏิบัติการ Android ใช้มาตั้งแต่ Lollipop และตั้งแต่ NDK r18 เป็น STL เดียวที่มีใน NDK

CMake ตั้งค่าเริ่มต้นเป็น C++ เวอร์ชันใดก็ตามที่มีค่าเริ่มต้นเป็น (ปัจจุบันคือ C++14) คุณจึงต้องตั้งค่า CMAKE_CXX_STANDARD มาตรฐานเป็นค่าที่เหมาะสม ในไฟล์ CMakeLists.txt เพื่อใช้ฟีเจอร์ C++17 ขึ้นไป ดู CMake เอกสารประกอบสำหรับ CMAKE_CXX_STANDARD เพื่อดูรายละเอียดเพิ่มเติม

ndk-build ปล่อยให้มีการตัดสินใจที่จะ clang ตามค่าเริ่มต้น ดังนั้น ndk-build ผู้ใช้ ควรใช้ APP_CPPFLAGS เพื่อเพิ่ม -std=c++17 หรืออะไรก็ได้ที่ต้องการแทน

ไลบรารีที่ใช้ร่วมกันสำหรับ libc++ คือ libc++_shared.so และแท็ก ไลบรารีคือ libc++_static.a ในกรณีทั่วไป ระบบบิลด์จะจัดการ โดยใช้และรวมไลบรารีเหล่านี้ตามต้องการสำหรับผู้ใช้ สำหรับกรณีที่ไม่ปกติ หรือเมื่อใช้ระบบบิลด์ของคุณเอง โปรดดูผู้ดูแลระบบของบิลด์ คู่มือหรือคำแนะนำสำหรับการใช้ระบบบิลด์อื่นๆ

โปรเจ็กต์ LLVM อยู่ภายใต้ Apache License v2.0 ที่มีข้อยกเว้น LLVM สำหรับข้อมูลเพิ่มเติม ให้ดูที่ไฟล์ใบอนุญาต

ระบบ

รันไทม์ของระบบอ้างอิงถึง /system/lib/libstdc++.so ไลบรารีนี้ไม่ควร ก็สับสนกับ libstdc++ ที่มีฟีเจอร์เต็มของ GNU สำหรับ Android libstdc++ new และ delete ใช้ libc++ สำหรับไลบรารีมาตรฐาน C++ ที่มีฟีเจอร์ครบถ้วน

รันไทม์ของ C++ จะรองรับ ABI พื้นฐานของ C++ ในรันไทม์ โดยพื้นฐานแล้ว ไลบรารีนี้จะมี new และ delete ตรงข้ามกัน ที่มีให้บริการใน NDK โดยไม่มีการสนับสนุนการจัดการข้อยกเว้นหรือ RTTI

ไม่มีการสนับสนุนไลบรารีมาตรฐาน นอกเหนือจาก Wrapper C++ สำหรับ C ส่วนหัวไลบรารี เช่น <cstdio> หากต้องการใช้ STL คุณควรใช้ ตัวเลือกอื่นๆ ในหน้านี้

ไม่มี

นอกจากนี้ คุณยังมีตัวเลือกให้ไม่มี STL ด้วย ไม่มีการลิงก์หรือการอนุญาตให้ใช้สิทธิ ที่จำเป็นในกรณีดังกล่าว ไม่มีส่วนหัวมาตรฐาน C++

การเลือกรันไทม์ C++

ผู้ผลิต

ค่าเริ่มต้นของ CMake คือ c++_static

คุณสามารถระบุ c++_shared, c++_static, none หรือ system ได้โดยใช้ ตัวแปร ANDROID_STL ในไฟล์ build.gradle ระดับโมดูล หากต้องการทราบข้อมูลเพิ่มเติม โปรดดูเอกสารประกอบสำหรับ ANDROID_STL ใน CMake

Ndk-Build

ค่าเริ่มต้นสำหรับ ndk-build คือ none

คุณสามารถระบุ c++_shared, c++_static, none หรือ system ได้โดยใช้ APP_STL ตัวแปรในไฟล์ Application.mk เช่น

APP_STL := c++_shared

ndk-build อนุญาตให้คุณเลือกรันไทม์หนึ่งชุดสำหรับแอปของคุณเท่านั้น และสามารถทำได้เฉพาะใน Application.mk

ใช้การพูดโดยตรง

หากคุณใช้ clang ในระบบบิลด์ของคุณเองโดยตรง clang++ จะใช้ c++_shared โดยค่าเริ่มต้น หากต้องการใช้ตัวแปรแบบคงที่ ให้เพิ่ม -static-libstdc++ ลงใน แฟล็ก Linker ของคุณ โปรดทราบว่าแม้ว่าตัวเลือกนี้จะใช้ชื่อ "libstdc++" สำหรับ ตามประวัติความเป็นมา ค่านี้ถูกต้องสำหรับ libc++ ด้วย

สิ่งสำคัญที่ควรพิจารณา

รันไทม์แบบคงที่

หากโค้ดดั้งเดิมของแอปพลิเคชันของคุณอยู่ในโค้ดเดียวกัน เราขอแนะนำให้ใช้รันไทม์แบบคงที่ วิธีนี้ช่วยให้ Linker ดำเนินการต่อไปนี้ได้ แทรกโค้ดที่ไม่มีการใช้งานไว้ และลดจำนวนโค้ดที่ไม่มีการใช้งานให้ได้มากที่สุด เพื่อให้เกิดประสิทธิภาพสูงสุด และเล็กที่สุดเท่าที่จะเป็นไปได้ นอกจากนี้ยังหลีกเลี่ยง PackageManager และไดนามิก ข้อบกพร่องตัวลิงก์ใน Android เวอร์ชันเก่าที่ทำให้การจัดการกับปัญหาหลายๆ อย่างร่วมกัน ห้องสมุดที่สร้างได้ยากและมีโอกาสเกิดข้อผิดพลาดได้ง่าย

อย่างไรก็ตาม ใน C++ การกำหนดสำเนาที่เหมือนกันมากกว่า 1 สำเนานั้นไม่ปลอดภัย ฟังก์ชันหรืออ็อบเจกต์ในโปรแกรมเดียว นี่เป็นแง่มุมหนึ่งของ มีกฎคำจำกัดความ 1 ข้ออยู่ในมาตรฐาน 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++ เวอร์ชันคงที่ได้เท่านั้นหากมี และไลบรารีที่ใช้ร่วมกันเพียง 1 รายการในแอปพลิเคชัน

รันไทม์ที่ใช้ร่วมกัน

หากแอปพลิเคชันของคุณมีไลบรารีที่ใช้ร่วมกันหลายรายการ คุณควรใช้ libc++_shared.so

ใน Android libc++ ที่ NDK ใช้จะไม่เหมือนกับที่ ของระบบปฏิบัติการ ซึ่งทำให้ผู้ใช้ NDK เข้าถึงฟีเจอร์และข้อบกพร่องล่าสุดของ libc++ ได้ แก้ไขข้อบกพร่องได้แม้ในการกำหนดเป้าหมาย Android เวอร์ชันเก่า ข้อดีคือถ้าคุณ ใช้ libc++_shared.so คุณต้องรวมเอาไว้ในแอปด้วย ถ้าคุณกำลังสร้าง แอปพลิเคชันที่มี Gradle ปัญหานี้จะได้รับการจัดการโดยอัตโนมัติ

Android เวอร์ชันเก่ามีข้อบกพร่องใน PackageManager และ Linker แบบไดนามิก ที่ทำให้การติดตั้ง อัปเดต และการโหลดไลบรารีเนทีฟเป็น ไม่น่าเชื่อถือ โดยเฉพาะอย่างยิ่ง หากแอปกําหนดเป้าหมายเป็น Android เวอร์ชันก่อนหน้านี้ กว่า Android 4.3 (Android API ระดับ 18) และใช้ libc++_shared.so คุณ ต้องโหลดไลบรารีที่ใช้ร่วมกันก่อนไลบรารีอื่นๆ ที่เกี่ยวข้อง

โครงการ ReLinker นำเสนอ ในการแก้ปัญหาการโหลดไลบรารีเนทีฟที่ทราบทั้งหมด ทางเลือกที่ดีกว่าการเขียนวิธีแก้ปัญหาเบื้องต้นของคุณเอง

1 STL ต่อแอป

ก่อนหน้านี้ NDK รองรับ GNU libstdc++ และ STLport นอกเหนือจาก libc++ หากแอปพลิเคชันของคุณใช้ไลบรารีที่สร้างไว้ล่วงหน้าซึ่งสร้างขึ้นตาม NDK แตกต่างจากเวอร์ชันที่ใช้สร้างแอปพลิเคชัน คุณต้องตรวจสอบว่า ในลักษณะที่ทำงานร่วมกันได้

แอปพลิเคชันไม่ควรใช้รันไทม์ C++ มากกว่า 1 รายการ STL แบบต่างๆ ไม่ ทำงานร่วมกันได้ ตัวอย่างเช่น เลย์เอาต์ของ std::string ใน libc++ ไม่เหมือนกับใน gnustl โค้ดที่เขียนเทียบกับ STL 1 รายการจะ ไม่สามารถใช้วัตถุที่เขียนกับวัตถุอื่น นี่เป็นเพียงตัวอย่างเดียว ความเข้ากันไม่ได้มีหลายอย่าง

กฎนี้ครอบคลุมมากกว่าโค้ดของคุณ ทรัพยากร Dependency ทั้งหมดต้องใช้ทรัพยากรเดียวกัน STL ที่คุณเลือก หากคุณใช้บุคคลที่สามแบบโอเพนซอร์ส ทรัพยากร Dependency ที่ใช้ STL และไม่มีไลบรารีต่อ STL มีตัวเลือกใน STL คุณต้องใช้ STL เดียวกันกับทรัพยากร Dependency

คุณอาจจะใช้ไลบรารีที่ใช้ร่วมกันไม่ได้ 2 ไลบรารี ใน สถานการณ์นี้ วิธีแก้ไขมีเพียงการทิ้งทรัพยากร Dependency ไว้ 1 รายการ หรือถาม เป็นไลบรารีที่สร้างขึ้นตาม STL อื่น

ข้อยกเว้นของ C++

libc++ รองรับข้อยกเว้น C++ แต่จะปิดใช้โดยค่าเริ่มต้นใน ndk-build เนื่องจากก่อนหน้านี้ ข้อยกเว้น C++ จะไม่มีให้ใน NDK CMake และ Toolchain แบบสแตนด์อโลนเปิดใช้ข้อยกเว้น C++ โดยค่าเริ่มต้น

หากต้องการเปิดใช้ข้อยกเว้นทั่วทั้งแอปพลิเคชันของคุณใน ndk-build ให้เพิ่ม บรรทัดต่อไปนี้ลงในไฟล์ Application.mk

APP_CPPFLAGS := -fexceptions

หากต้องการเปิดใช้ข้อยกเว้นสำหรับโมดูล ndk-build รายการเดียว ให้เพิ่มบรรทัดต่อไปนี้ใน โมดูลที่ระบุใน Android.mk

LOCAL_CPP_FEATURES := exceptions

อีกวิธีหนึ่งคือ

LOCAL_CPPFLAGS := -fexceptions

RTT

ทั้งนี้เช่นเดียวกับข้อยกเว้น RTTI ได้รับการสนับสนุนโดย libc++ แต่จะปิดใช้โดยค่าเริ่มต้นใน ndk-build CMake และ Toolchain แบบสแตนด์อโลนเปิดใช้ RTTI โดยค่าเริ่มต้น

หากต้องการเปิดใช้ RTTI ทั่วทั้งแอปพลิเคชันใน ndk-build ให้เพิ่มรายการต่อไปนี้ ในบรรทัดไฟล์ Application.mk ของคุณ

APP_CPPFLAGS := -frtti

หากต้องการเปิดใช้ RTTI สำหรับโมดูล ndk-build รายการเดียว ให้เพิ่มบรรทัดต่อไปนี้ลงในส่วน ที่ให้ไว้ใน Android.mk

LOCAL_CPP_FEATURES := rtti

อีกวิธีหนึ่งคือ

LOCAL_CPPFLAGS := -frtti