دعم مكتبة C++

ويدعم NDK مكتبات وقت تشغيل C++ متعددة. يقدم هذا المستند على معلومات عن هذه المكتبات والإيجابيات والسلبيات المرتبطة بها وكيفية استخدامها.

مكتبات وقت تشغيل C++

الجدول 1. ميزات وبيئات تشغيل NDK C++

الاسم الميزات
libc++ دعم C++ حديث.
نظام التشغيل new وdelete (سيتم إيقافها في الإصدار r18.)
بلا ليست هناك رؤوس، ولكن C++ محدود.

تتوفر libc++ كمكتبة ثابتة ومشتركة.

libc++

إنّ libc++ التابع لـ LLVM هو معيار C++ . إحدى المكتبات التي استخدمها نظام التشغيل Android منذ الإصدار Lollipop، واعتبارًا من NDK r18 هي STL الوحيد المتوفر في NDK.

Cاجعل الإعدادات الافتراضية لأي إصدار افتراضي من C++ clang هو (C++14 حاليًا)، لذا، عليك ضبط قيمة CMAKE_CXX_STANDARD العادية على القيمة المناسبة. في ملف CMakeLists.txt لاستخدام ميزات C++17 أو الإصدارات الأحدث. الاطّلاع على CMake مستندات خاصة بـ "CMAKE_CXX_STANDARD" لمزيد من التفاصيل.

يترك ndk-build أيضًا قرار الربط بشكل افتراضي، حتى يتمكن مستخدمو ndk-build بدلاً من ذلك، يجب استخدام "APP_CPPFLAGS" لإضافة "-std=c++17" أو أي محتوى تريده.

المكتبة المشتركة لـ libc++ هي libc++_shared.so، ومكتبة libc++ الثابتة المكتبة libc++_static.a. في الحالات العادية، سيتعامل نظام الإصدار مع استخدام هذه المكتبات وتغليفها حسب حاجة المستخدم. للحالات غير النموذجية أو عند تنفيذ نظام التصميم الخاص بك، راجع قسم صيانة نظام الإنشاءات دليل أو دليل لاستخدام أنظمة التصميم الأخرى

يخضع مشروع LLVM لترخيص Apache الإصدار 2.0 مع استثناءات 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++

إنشاء فيديوهات Shorts

ويكون الإعداد التلقائي لـ 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++ إلى علامات الرابط. تجدر الإشارة إلى أنّه على الرغم من أنّ الخيار يستخدم الاسم "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 لا يعمل بشكل صحيح.

بالإضافة إلى المشكلات السلوكية المتضمنة، يمكن ربط بيئة التشغيل الثابتة فإن المكتبات تكرر التعليمات البرمجية في كل مكتبة مشتركة، مما يزيد من حجم تطبيقك.

بشكل عام، لا يمكنك استخدام صيغة ثابتة من بيئة تشغيل C++ إلا إذا كان لديك صيغة ومكتبة مشتركة واحدة فقط في تطبيقك.

بيئات التشغيل المشترَكة

إذا كان تطبيقك يتضمن مكتبات مشتركة متعددة، فيجب عليك استخدام libc++_shared.so

على Android، لا يكون تنسيق libc++ الذي يستخدمه NDK هو نفسه المحتوى التابع من نظام التشغيل. يمنح ذلك مستخدمي NDK إمكانية الوصول إلى أحدث ميزات libc++ والأخطاء حتى عند استهداف الإصدارات القديمة من Android. المقايضة هي أنه إذا تستخدم libc++_shared.so، يجب تضمينها في تطبيقك. إذا كنت تقوم بإنشاء باستخدام Gradle، يتم التعامل مع هذا الأمر تلقائيًا.

كانت هناك أخطاء في الإصدارات القديمة من Android في PackageManager وأداة الربط الديناميكي التي تسبّبت في حدوث عمليات تثبيت وتحديث وتحميلات للمكتبات الأصلية غير موثوقة. وعلى وجه الخصوص، إذا كان تطبيقك يستهدف إصدارًا سابقًا من Android من Android 4.3 (المستوى 18 من واجهة برمجة تطبيقات 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. يتم تفعيل استثناءات C++ تلقائيًا في سلاسل الأدوات CMake وسلاسل الأدوات المستقلة.

لتمكين الاستثناءات عبر التطبيق بالكامل في ndk-build، أضف السطر التالي إلى ملف Application.mk:

APP_CPPFLAGS := -fexceptions

لتمكين الاستثناءات لوحدة ndk-build واحدة، أضف السطر التالي إلى الوحدة المحدّدة في Android.mk:

LOCAL_CPP_FEATURES := exceptions

بدلاً من ذلك، يمكنك استخدام:

LOCAL_CPPFLAGS := -fexceptions

مراسلة نصية في الوقت الفعلي

كما هو الحال مع الاستثناءات، تتوافق ميزة RTTI مع libc++، ولكن يتم إيقافها تلقائيًا في ndk-build. تكون ميزة "مراسلة نصية في الوقت الفعلي" مفعَّلة تلقائيًا في سلاسل الأدوات CMake وسلاسل الأدوات المستقلة.

لتفعيل ميزة "مراسلة نصية في الوقت الفعلي" في تطبيقك بالكامل في ndk-build، أضِف ما يلي: إلى ملف Application.mk:

APP_CPPFLAGS := -frtti

لتفعيل ميزة "مراسلة نصية في الوقت الفعلي" لوحدة ndk-Build واحدة، أضف السطر التالي إلى في Android.mk:

LOCAL_CPP_FEATURES := rtti

بدلاً من ذلك، يمكنك استخدام:

LOCAL_CPPFLAGS := -frtti