תמיכה בספריית C++

NDK תומך במספר ספריות זמן ריצה של C++. במסמך הזה אנחנו מספקים מידע על הספריות האלה, היתרונות שלהן ואיך להשתמש בהן.

ספריות זמן ריצה של C++

טבלה 1. זמני ריצה ותכונות של NDK++

שם תכונות
libc++ תמיכת C++ מודרנית.
system new וגם delete (הוצא משימוש ב-r18.)
ללא ללא כותרות, מוגבל C++.

אפשר להשתמש ב-libc++ כספרייה סטטית וגם כספרייה משותפת.

libc++

libc++ של LLVM הוא תקן C++ שבה נמצאת מערכת ההפעלה Android OS מאז 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. במקרים אופייניים, מערכת ה-build תטפל באמצעות ספריות אלה ואריזות אותן בהתאם לצורך עבור המשתמש. למקרים לא אופייניים או במהלך הטמעה של מערכת build משלכם, עיינו בתחזוקה של מתחזקים של מערכות Build. המדריך או המדריך לשימוש במערכות build אחרות.

פרויקט LLVM נמצא ברישיון Apache גרסה 2.0, עם חריגים ל-LLVM. לקבלת מידע נוסף מידע נוסף, ראו קובץ הרישיון.

מערכת

זמן הריצה של המערכת מתייחס ל-/system/lib/libstdc++.so. אין להשתמש בספרייה הזו צריך להתבלבל עם libstdc++ של GNU עם כל התכונות. ב-Android, libstdc++ פשוט new וגם delete כדי להשתמש בכל התכונות הרגילות של C++ אפשר להשתמש ב-libc++.

זמן הריצה של המערכת C++ תומך בממשק ה-ABI הבסיסי של C++ זמן ריצה. בעיקרון, הספרייה הזו מספקת את new ואת delete. בניגוד למודל אחר הזמינות ב-NDK, אין תמיכה בטיפול חריג או RTTI.

אין תמיכה רגילה בספרייה מלבד wrappers של C++ עבור C כותרות של ספריות כמו <cstdio>. אם רוצים להשתמש ב-STL, צריך להשתמש באחד האפשרויות האחרות שמופיעות בדף הזה.

ללא

אפשר גם לא להשתמש ב-STL. אין קישור או רישוי בדרישות האלה. אין כותרות סטנדרטיות של C++ זמינות.

בחירת סביבת זמן ריצה של C++

CMake

ברירת המחדל ל-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 ישירות במערכת ה-build שלכם, clang++ ישתמש c++_shared כברירת מחדל. כדי להשתמש בווריאנט הסטטי, צריך להוסיף את -static-libstdc++ אל הדגלים של המנגנון לקישור חשוב לשים לב שלמרות שהאפשרות משתמשת בשם 'libstdc++ ' עבור מסיבות היסטוריות, הדבר נכון גם עבור libc++.

שיקולים חשובים

זמני ריצה סטטיים

אם כל הקוד המקורי של האפליקציה נמצא בקובץ משותף אחד אנחנו ממליצים להשתמש בסביבת זמן הריצה הסטטית. זה מאפשר ל-linker להטביע ולהסיר כמה שיותר קוד שאינו בשימוש, והתוצאה שמתקבלת היא וביישום קטן ככל האפשר. הוא גם מונע שימוש ב-PackageManager ובדינמית באגים מקושרים בגרסאות ישנות של Android שגורמים לטיפול בכמה משחקים משותפים ספריות קשות יותר ומסוגלות לשגיאות.

עם זאת, ב-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, הקישור libc++ שמשמש את NDK הוא לא אותו חלק של מערכת ההפעלה. כך תהיה למשתמשי NDK גישה לתכונות ולבאג הכי חדשים של libc++ שצריך לתקן גם כשמטרגטים גרסאות ישנות של Android. ההבדל הוא שאם להשתמש ב-libc++_shared.so, חובה לכלול אותו באפליקציה. אם אתם בונים ב-Gradle, זה מטופל באופן אוטומטי.

בגרסאות ישנות של Android היו באגים ב-PackageManager וב-ה-Linker הדינמי שגרמה להתקנה, לעדכון ולטעינה של ספריות נייטיב אמינה. באופן ספציפי, אם האפליקציה מטרגטת גרסת Android מוקדם יותר מאשר ב-Android 4.3 (Android API ברמה 18), ומשתמשים ב-libc++_shared.so, חייבים לטעון את הספרייה המשותפת לפני כל ספרייה אחרת שתלויה בה.

פרויקט ReLinker מציע פתרונות אפשריים לכל הבעיות הידועות בטעינה של ספריות נייטיב, ובדרך כלל היא דרך טובה יותר מאשר כתיבת פתרונות משלכם.

STL אחד לכל אפליקציה

בעבר, NDK תמך ב-GNU libstdc++ ו-STLport בנוסף ל-libc++. אם האפליקציה מסתמכת על ספריות מוכנות מראש שפותחו על NDK שונה מזה ששימש לפיתוח האפליקציה, צריך לוודא שהוא עושה זאת באופן תואם.

אפליקציה לא יכולה להשתמש ביותר מסביבת זמן ריצה אחת של C++ אחת. רשימות ה-STL השונות לא תואמים זה לזה. לדוגמה, הפריסה של std::string ב-libc++ לא זהה ל-gnustl. קוד שנכתב ב-STL אחד הם לא יכולים להשתמש באובייקטים שנכתבו נגד אחר. זו רק דוגמה אחת. יש הרבה אי התאמות.

כלל זה חל גם מעבר לקוד. כל יחסי התלות צריכים להשתמש STL שבחרת. הסתמכות על צד שלישי במקור סגור של התלות שמשתמשת ב-STL ולא מספקת ספרייה לכל STL, יש בחירה ב-STL. עליכם להשתמש באותו STL כמו תלויה.

ייתכן שתסתמך על שתי ספריות שאינן תואמות זו לזו. לחשבון במצב הזה הפתרונות היחידים הם לשחרר את אחד מיחסי התלות או לשאול לספק ספרייה שנבנתה כנגד ה-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. RTTI מופעלת כברירת מחדל ב-CMake ובצרוי כלים עצמאיים.

כדי להפעיל RTTI בכל האפליקציה ב-ndk-build, צריך להוסיף את הפרטים הבאים שורה לקובץ Application.mk:

APP_CPPFLAGS := -frtti

כדי להפעיל RTTI במודול ndk-build יחיד, צריך להוסיף את השורה הבאה למודול נתון ב-Android.mk שלו:

LOCAL_CPP_FEATURES := rtti

לחלופין, אפשר להשתמש ב:

LOCAL_CPPFLAGS := -frtti