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 הופך לסוג robust 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;
    }