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

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

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

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

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

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

libc++

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

CMake به هر نسخه از C++ که به طور پیش‌فرض صدا می‌کند (در حال حاضر C++14) پیش‌فرض می‌شود، بنابراین برای استفاده از ویژگی‌های C++17 یا جدیدتر، باید CMAKE_CXX_STANDARD روی مقدار مناسب در فایل CMakeLists.txt تنظیم کنید. برای جزئیات بیشتر به مستندات CMake برای CMAKE_CXX_STANDARD مراجعه کنید.

ndk-build نیز به‌طور پیش‌فرض تصمیم به صدا زدن را واگذار می‌کند، بنابراین کاربران ndk-build باید از APP_CPPFLAGS برای اضافه کردن -std=c++17 یا هر چیزی که می‌خواهند استفاده کنند.

کتابخانه مشترک libc++ libc++_shared.so و کتابخانه استاتیک libc++_static.a است. در موارد معمولی، سیستم ساخت، استفاده و بسته بندی این کتابخانه ها را در صورت نیاز برای کاربر انجام می دهد. برای موارد غیر معمول یا هنگام اجرای سیستم ساخت خود، به راهنمای نگهدارنده‌های سیستم ساخت یا راهنمای استفاده از سیستم‌های ساخت دیگر مراجعه کنید.

پروژه LLVM تحت مجوز آپاچی نسخه 2.0 با استثناهای LLVM است. برای اطلاعات بیشتر، فایل مجوز را ببینید.

سیستم

زمان اجرای سیستم به /system/lib/libstdc++.so اشاره دارد. این کتابخانه نباید با libstdc++ با امکانات کامل گنو اشتباه گرفته شود. در اندروید، libstdc++ new است و delete . از libc++ برای یک کتابخانه استاندارد C++ با امکانات کامل استفاده کنید.

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

هیچ پشتیبانی از کتابخانه استانداردی به غیر از پوشش های C++ برای سرصفحه های کتابخانه C مانند <cstdio> وجود ندارد. اگر STL می خواهید، باید از یکی از گزینه های دیگر ارائه شده در این صفحه استفاده کنید.

هیچ کدام

همچنین گزینه عدم وجود STL وجود دارد. هیچ الزامی برای پیوند یا مجوز در آن مورد وجود ندارد. هیچ هدر استاندارد C++ موجود نیست.

انتخاب یک C++ Runtime

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

با این اوصاف، در C++، تعریف بیش از یک کپی از یک تابع یا شی در یک برنامه واحد امن نیست. این یکی از جنبه های قانون One Definition موجود در استاندارد 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++ و رفع اشکالات را می‌دهد، حتی زمانی که نسخه‌های قدیمی اندروید را هدف قرار می‌دهند. مبادله این است که اگر libc++_shared.so استفاده می کنید، باید آن را در برنامه خود قرار دهید. اگر برنامه خود را با Gradle می سازید، این به طور خودکار مدیریت می شود.

نسخه‌های قدیمی اندروید دارای اشکالاتی در PackageManager و پیوند پویا بودند که باعث می‌شد نصب، به‌روزرسانی و بارگیری کتابخانه‌های بومی غیرقابل اعتماد باشد. به ویژه، اگر برنامه شما نسخه‌ای از Android را زودتر از Android 4.3 (سطح 18 API Android) هدف قرار می‌دهد و از 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 غیرفعال است. CMake و زنجیره‌های ابزار مستقل به طور پیش‌فرض RTTI را فعال کرده‌اند.

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

APP_CPPFLAGS := -frtti

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

LOCAL_CPP_FEATURES := rtti

به طور متناوب، می توانید استفاده کنید:

LOCAL_CPPFLAGS := -frtti