پرش به مطلب اصلی

FFI

این سند مشخصات FFI (واسطه توابع خارجی) برای فراخوانی توابع پیاده‌سازی شده در خارج از زبان Wave را توضیح می‌دهد. از طریق FFI، برنامه‌های Wave می‌توانند به طور مستقیم با کتابخانه‌های بومی نوشته شده به زبان‌های دیگر تعامل داشته باشند.


خلاصه

FFI در Wave براساس اعلان‌ها عمل می‌کند. توابع خارجی در کد Wave پیاده‌سازی نمی‌شوند و فقط مشخص می‌شود که تابع مربوطه از چه ABI (واسطه باینری برنامه) پیروی می‌کند. پیاده‌سازی واقعی در مرحله لینک از کتابخانه‌های خارجی به دست می‌آید.

FFI در زمان کامپایل تنها وجود تابع را اعلام می‌کند و در زمان ایجاد فایل اجرایی، لینک‌کننده به نماد واقعی متصل می‌شود.


اعلان extern

توابع خارجی با استفاده از کلمه کلیدی extern اعلان می‌شوند. در حال حاضر در ویو مشخص کردن ABI ضروری است و فقط extern(c) پشتیبانی می‌شود.

extern(c) fun function_name(args...) -> return_type;

تعیین کردن ABI

در اعلان extern باید ABI را مشخص کنید. در حال حاضر ABI پشتیبانی شده تنها c است.

extern(c) fun printf(fmt: ptr<u8>);

اعلان هایی مانند extern(rust) ممکن است تجزیه شوند اما در مرحله تحلیل معنایی خطا رخ می‌دهد.


اعلان extern در سطح تابع

برای اعلام یک تابع خارجی به صورت زیر عمل می‌کنیم.

extern(c) fun InitWindow(width: i32, height: i32, title: ptr<u8>);

این اعلان به معنای وجود نماد InitWindow که از ABI C پیروی می‌کند در کتابخانه خارجی است.


اعلان extern در سطح بلوک

در صورتی که چندین تابع خارجی از همان ABI استفاده کنند، می‌توان اعلامیه‌ها را به صورت بلوکی گروه‌بندی کرد.

extern(c) {
fun InitWindow(width: i32, height: i32, title: ptr<u8>);
fun CloseWindow();
fun BeginDrawing();
fun EndDrawing();
}

اعلان‌های سطح بلاک از نظر معنایی کاملاً با اعلان‌های سطح تابع یکسان هستند و صرفاً از نظر خوانایی و ساختاردهی برای قواعد دستوری استفاده می‌شوند.


تعیین نام نماد

در برخی ABIها، نام تابع Wave ممکن است با نام نماد لینک شده واقعی مطابقت نداشته باشد. در این حالت، می‌توان نام نماد واقعی که تابع خارجی به آن متصل می‌شود را به صورت رشته‌ای مشخص کرد.

تعیین نماد سطح تابع

extern(c, "puts")
fun rust_func(i32);

این اعلان مشخص می‌کند که در هنگام فراخوانی rust_func از نماد لینک واقعی puts استفاده شود.


تعیین نماد سطح بلاک

در اعلان سطح بلاک، می‌توان نام نماد را به صورت جداگانه پس از هر تابع مشخص کرد.

extern(c) {
fun my_puts(ptr<i8>) "puts";
fun my_strlen(ptr<i8>) "strlen";
}

نوع پوینتر

پوینتر به صورت ptr<T> نمایش داده می‌شود.

ptr<u8>
ptr<MyStruct>

ptr<T> مستقیما با اشاره‌گرهای زبان‌های خارجی تطبیق دارد، و مدیریت مالکیت حافظه یا طول عمر توسط Wave صورت نمی‌گیرد.


استفاده از ساختار

ساختارها می‌توانند به عنوان آرگومان یا مقدار بازگشتی از توابع خارجی استفاده شوند.

struct Color {
r: u8,
g: u8,
b: u8,
a: u8,
}

هنگام استفاده از ساختارها در FFI، ترتیب فیلدها به ترتیب اعلام شده حفظ می‌شود و چیدمان حافظه ای که ABI خواسته است رعایت می‌گردد.


فراخوانی تابع خارجی

توابع اعلام شده با extern به شیوه‌ای همانند توابع عادی فراخوانی می‌شوند.

fun main() -> i32 {
InitWindow(800, 600, "Wave");
BeginDrawing();
EndDrawing();
CloseWindow();
return 0;
}

در زمان فراخوانی، تفاوت نحوی وجود ندارد، و قراردادهای فراخوانی و اتصال نماد به طور کامل توسط ABI و لینک‌کننده مدیریت می‌شوند.


لینک کردن

پیاده‌سازی واقعی یک تابع خارجی در مرحله لینک از یک کتابخانه خارجی ارائه می‌شود. کامپایلر Wave یک فایل شئ شامل فراخوانی‌های توابع خارجی تولید می‌کند و لینک‌کننده نمادها را با استفاده از کتابخانه‌های مشخص شده حل می‌کند.

روش تعیین کتابخانه از طریق ابزارهای ساخت و گزینه‌های CLI مشخص می‌شود.


محدودیت‌ها

Wave ویژگی‌های زیر را ارائه نمی‌کند.

  • پوینترهای تابع
  • توابع بازگشتی
  • مدیریت خودکار حافظه
  • یکپارچگی مدیریت خطا بین زبان‌ها

این ویژگی‌ها ممکن است در نسخه‌های بعد به طور جداگانه اضافه شوند.