Skip to content

واجهات الوظائف الخارجية (FFI)

(الـ) iroha_ffi الصندوق يقدم الكليات والخصائص لإنتاج C ABI التزامات من Rust APIs. يتم استخدامه عند Iroha أنواع تحتاج إلى عبور FFI الحدود، على سبيل المثال من خلال SDK التزامات أو تكاملات المضيف.

لماذا FFI

الوظيفة هي كيان مجرد إلى حد ما، وعلى الرغم من أن معظم اللغات تتفق على ما يجب أن تفعله الوظيفة، فإن الطريقة التي يتم بها تمثيل الوظائف مختلفة جداً. وبالإضافة إلى ذلك، في بعض اللغات، مثل Rust ، فإن عواقب استدعاء وظيفة والأشياء التي يسمح لها بالقيام بها مختلفة أيضًا. عندما تحتاج إلى استدعاء Rust APIs من لغة أخرى أو بيئة مضيفة مختلفة، يستخدم Iroha واجهة وظيفة أجنبية (FFI) لتسوية الحالة.

المعيار الرئيسي المستخدم اليوم هو واجهة التطبيقات الثنائية C. إنها بسيطة ومتوفرة على نطاق واسع ومستقرة. في المبدأ ، يمكنك القيام بكل شيء يدوياً ، ولكن Iroha يوفر صندوق iroha_ffi لتوليد وظائف متوافقة مع FFI من Rust موجودة API.

يمكنك، بالطبع، القيام بذلك بطريقتك. الصندوق iroha_ffi يخلق فقط الشفرة التي ستحتاج إلى توليدها على أي حال. كتابة اللوحة المطلوبة تتطلب قدرًا كبيرًا من الكثافة والتربية. كل مكالمة وظيفة فوق حدود FFI هي unsafe مع إمكانية أن تسبب سلوك غير محدد. الطريقة التي تمكنا من حلها، تتداول حول استخدام صيغ قوية repr(C) .

INFO

الاستثناء الوحيد هو المؤشرات. لا يمكن فرض التحقق الصفري والصلاحية على المستوى العالمي، لذلك لا يتم استخدام المؤشرات الخام (كما هو الحال دائما) إلا في حالات استثنائية. نظرًا لأننا نقدم ملفات حول كل مثال تقريبًا من كائن في نموذج البيانات Iroha ، لا يجب عليك استخدام مؤشرات خامة على الإطلاق.

مثال

وهنا مثال على توليد الالتزام:

rust
#[derive(FfiType)]
struct DaysSinceEquinox(u32);

#[ffi_export]
impl DaysSinceEquinox {
    pub fn update_value(&mut self, a: &u8) {
        self.0 = *a as u32;
    }
}

النموذج المذكور أعلاه سيخلق الارتباط التالي مع DaysSinceEquinox تمثله كمرجع غير شفاف:

rust
pub extern fn DaysSinceEquinox__update_value(handle: *mut DaysSinceEquinox, a: *const u8) -> FfiReturn {
    // function implementation
}

FFI الجيل المرتبط

يستخدم صندوق iroha_ffi لتوليد الوظائف التي يمكن استدعاءها عبر FFI. بالنظر إلى هيكلات وطرق Rust ، فإنها تولد رمز unsafe الذي ستحتاج إليه لعبور الحدود المرتبطة.

يتم تحويل نوع Rust إلى نوع قوي repr(C) يمكنه عبور حدود FFI مع FfiType::into_ffi ، وهذا يسير بالعكس أيضًا: يتم تحويل النوع FFI ReprC إلى نوع Rust عن طريق FfiType::try_from_ffi.

WARNING

لاحظ أن التحويل المعاكس خاطئ ويمكن أن يسبب سلوك غير محدد. في حين أننا يمكن أن نبذل قصارى جهدنا لتجنب الأخطاء الأكثر وضوحا، يجب عليك التأكد من صحة البرنامج على وجهك.

السمات الرئيسية التي تمكن من توليد الالتزام هي ReprC، FfiType، و FfiConvert.

سمةوصف
ReprCهذه الصفة تمثل نوعًا قويًا يتوافق مع C ABI. يمكن مشاركة النوع بأمان عبر حدود FFI.
FfiTypeتحدد هذه السمة نوع ReprC المقابلة لنوع Rust معين. يتم استخدام النوع ReprC المحدد بدلاً من نوع Rust في وظيفة API التي تم إنشاؤها FFI .
FfiConvertهذه السمة تحدد طريقتين into_ffi و try_from_ffi تستخدم لتنفيذ التحويل من نوع Rust إلى أو من نوع ReprC.

لاحظ أنه لا توجد نقل ملكية على FFI باستثناء أنواع المؤشرات غير الشفافة. جميع الأنواع الأخرى التي تحمل الملكية ، مثل Vec<T> ، يتم نسخها.

اسم (مانجلينغ)

لاحظ استخدام علامات تحتية مزدوجة في الأسماء التي تم إنشاؤها لأجسام FFI:

  • بالنسبة لنهج inherent_fn المحدد على هيكل StructName، فإن اسم FFI سيكون StructName__inherent_fn.

  • بالنسبة إلى طريقة MethodName من صفة TraitName في هيكل StructName، فإن اسم FFI سيكون StructNameTraitNameMethodName.

  • لتحديد حقل field_name في هيكل StructName ، سيكون اسم الوظيفة FFI هو StructName__set_field_name.

  • للحصول على حقل field_name في هيكل StructName، سيكون اسم الوظيفة FFI هو StructName__field_name.

  • للحصول على حقل field_name المتغير في هيكل StructName، سيكون اسم الوظيفة FFI هو StrucuName__field_name_mut.

  • بالنسبة لـ module_name::fn_name المستقلة، فإن اسم FFI سيكون module_name::__fn_name.

  • بالنسبة للخصائص التي ليست عامة وتسمح بمشاركة تنفيذها في FFI (انظر Clone أدناه) ، سيكون اسم FFI module_name::__clone.

    rust
    impl Clone for Type1 {
        fn clone(&self) -> Self;
    }
    impl Clone for Type2 {
        fn clone(&self) -> Self;
    }