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>, կլոնված են:

Անունը Mangling

Նշենք, որ FFI օբյեկտների ստեղծված անվանումներում օգտագործվում են կրկնակի ենթակետեր.

  • StructName կառուցվածքի վրա սահմանված inherent_fn մեթոդի համար FFI անվանումը կլինի StructName__inherent_fn:

  • StructName կառուցվածքում գտնվող TraitName հատկանիշից MethodName մեթոդի համար FFI անվանումը կլինի StructNameTraitNameMethodName:

  • StructName կառուցվածքի մեջ field_name դաշտը սահմանելու համար FFI գործառույթի անվանումը կլինի StructName__set_field_name:

  • Որպեսզի ստանալ field_name դաշտում StructName Կազմակերպություն FFI գործառույթի անվանումը կլինի StructName__field_name.

  • StructName կառուցվածքում փոփոխվող field_name դաշտը ստանալու համար 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;
    }