پشتیبانی کتابخانه C++‎

NDK از چندین کتابخانه زمان اجرای C++‎ پشتیبانی می‌کند. این سند اطلاعاتی درباره این کتابخانه‌ها، موازنه‌های درگیر، و نحوه استفاده از آن‌ها ارائه می‌دهد.

کتابخانه‌های زمان اجرای C++‎

جدول ۱. ویژگی‌ها و زمان‌های اجرای NDK C++.

نام ویژگی‌ها
libc++‎ پشتیبانی از C++‎ مدرن.
سیستم new و delete. (در r18 منسوخ شده است.)
هیچ‌کدام بدون سرصفحه، C++‎ محدود.

libc++‎ هم به‌عنوان کتابخانه ایستا و هم به‌عنوان کتابخانه مشترک دردسترس است.

libc++‎

libc++‎ مربوط به LLVM کتابخانه استاندارد C++‎ است که از Lollipop در سیستم‌عامل Android استفاده می‌شود و از NDK r18 تنها STL موجود در NDK است.

CMake به‌طور پیش‌فرض روی هر نسخه‌ای از C++‎ که clang به‌طور پیش‌فرض روی آن تنظیم شده است (درحال‌حاضر 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 نسخه ۲.۰ با استثناهای LLVM است. برای اطلاعات بیشتر، فایل پروانه را ببینید.

سیستم

زمان اجرای سیستم به /system/lib/libstdc++.so اشاره دارد. این کتابخانه نباید با libstdc++‎ با ویژگی‌های کامل GNU اشتباه گرفته شود. در Android،‏ libstdc++‎ فقط new و delete است. از libc++‎ برای کتابخانه استاندارد C++‎ با ویژگی‌های کامل استفاده کنید.

زمان اجرای C++‎ سیستم از ABI زمان اجرای C++‎ پایه پشتیبانی می‌کند. اساساً این کتابخانه new و delete را ارائه می‌دهد. برخلاف سایر گزینه‌های موجود در NDK، هیچ پشتیبانی‌ای برای رسیدگی به استثنا یا RTTI وجود ندارد.

هیچ پشتیبانی کتابخانه استانداردی به‌جز پوشاننده‌های 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 مستقیماً در سیستم ساخت خودتان استفاده می‌کنید، clang++‎ به‌طور پیش‌فرض از c++_shared استفاده خواهد کرد. برای استفاده از نوع ایستا، -static-libstdc++ را به پرچم‌های پیونددهنده اضافه کنید. توجه داشته باشید که اگرچه این گزینه به‌دلایل تاریخی از نام «libstdc++‎» استفاده می‌کند، این نام برای libc++‎ نیز صحیح است.

ملاحظات مهم

زمان‌های اجرای ایستا

اگر همه کد بومی برنامه‌تان در یک کتابخانه مشترک واحد قرار دارد، توصیه می‌کنیم از زمان اجرای ایستا استفاده کنید. این کار به پیونددهنده اجازه می‌دهد تا حد امکان کدهای استفاده‌نشده را درون‌خطی و هرس کند، که منجر به بهینه‌ترین و کوچک‌ترین برنامه ممکن می‌شود. همچنین از اشکالات 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 به‌درستی کار نمی‌کند.

Beyond the behavioral issues involved, linking the static runtime into multiple libraries will duplicate the code in each shared library, increasing the size of your application.

به‌طور کلی، فقط درصورتی می‌توانید از گونه ایستا از زمان اجرای C++‎ استفاده کنید که یک و فقط یک کتابخانه مشترک در برنامه‌تان داشته باشید.

زمان‌های اجرای هم‌رسانی‌شده

اگر برنامه‌تان شامل چندین کتابخانه مشترک است، باید از libc++_shared.so استفاده کنید.

در Android،‏ libc++‎ که NDK استفاده می‌کند با libc++‎ که بخشی از سیستم‌عامل است یکسان نیست. این کار به کاربران NDK امکان می‌دهد به جدیدترین ویژگی‌های libc++‎ و رفع اشکال‌ها دسترسی داشته باشند، حتی وقتی نسخه‌های قدیمی Android را هدف قرار می‌دهند. نکته منفی این است که اگر از libc++_shared.so استفاده کنید، باید آن را در برنامه‌تان بگنجانید. اگر برنامه‌تان را با Gradle می‌سازید، این کار به‌طور خودکار انجام می‌شود.

نسخه‌های قدیمی Android در PackageManager و پیونددهنده پویا اشکالاتی داشتند که باعث می‌شد نصب، به‌روزرسانی، و بار کردن کتابخانه‌های بومی غیرقابل‌اعتماد باشد. به‌ویژه، اگر برنامه شما نسخه‌ای از Android را هدف قرار می‌دهد که قدیمی‌تر از Android 4.3 (سطح میانای برنامه‌سازی کاربردی Android نسخه ۱۸) است و از libc++_shared.so استفاده می‌کنید، باید کتابخانه مشترک را قبل‌از هر کتابخانه دیگری که به آن وابسته است بار کنید.

پروژه ReLinker راه‌حل‌هایی برای همه مشکلات شناخته‌شده بار کردن کتابخانه بومی ارائه می‌دهد و معمولاً انتخاب بهتری نسبت به نوشتن راه‌حل‌های خودتان است.

یک STL به‌ازای هر برنامه

در گذشته، NDK علاوه‌بر libc++‎ از GNU libstdc++‎ و STLport نیز پشتیبانی می‌کرد. اگر برنامه شما به کتابخانه‌های ازپیش‌ساخته‌شده‌ای وابسته است که با NDK متفاوتی نسبت به NDK استفاده‌شده برای ساخت برنامه شما ساخته شده‌اند، باید مطمئن شوید که این کار به‌صورت سازگار انجام می‌شود.

برنامه نباید از بیش از یک زمان اجرای C++‎ استفاده کند. انواع مختلف STL با یکدیگر سازگار نیستند. به‌عنوان مثال، چیدمان std::string در libc++‎ با gnustl یکسان نیست. کد نوشته‌شده برای یک STL نمی‌تواند از اشیای نوشته‌شده برای 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 توسط libc++ پشتیبانی می‌شود، اما به‌طور پیش‌فرض در ndk-build غیرفعال است. CMake و زنجیره‌های ابزار مستقل به‌طور پیش‌فرض RTTI را فعال کرده‌اند.

برای فعال کردن RTTI در سراسر برنامه در ndk-build، خط زیر را به فایل Application.mk اضافه کنید:

APP_CPPFLAGS := -frtti

برای فعال کردن RTTI برای یک ماژول ndk-build، خط زیر را به ماژول داده‌شده در Android.mk آن اضافه کنید:

LOCAL_CPP_FEATURES := rtti

یا می‌توانید از این موارد استفاده کنید:

LOCAL_CPPFLAGS := -frtti