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