انتقل إلى المحتوى الرئيسي

واجهة الدوال الخارجية (FFI)

يشرح هذا المستند إتفاقية واجهة الدوال الخارجية (FFI) لاستدعاء الدوال المطبقة خارجياً في لغة Wave. يسمح FFI لبرنامج Wave بالارتباط المباشر بالمكتبات الأصلية المكتوبة بلغات أخرى.


نظرة عامة

تعمل واجهة الدوال الخارجية لـ Wave بناءً على الإعلانات. لا يتم تنفيذ الدوال الخارجية في كود Wave، بل يتم تحديد فقط أي ABI (واجهة ثنائية للتطبيقات) تتبعه. يتم حل التنفيذ الفعلي في مرحلة الربط من خلال المكتبة الخارجية.

تعمل واجهة الدوال الخارجية عن طريق إعلان وجود الدالة فقط وقت الترجمة، بينما يقوم الرابط التلقائي بربط الرموز الفعلية وقت إنشاء الملف القابل للتنفيذ.


إعلان extern

يتم إعلان الدوال الخارجية باستخدام الكلمة المفتاحية extern. على الحالي، تحتاج إلى تحديد ABI في Wave وتدعم extern(c) فقط.

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

تحديد ABI

يجب تحديد 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,
}

عند استخدام الهيكل في واجهة الدوال الخارجية، يجب أن يحافظ تسلسل الحقول على الترتيب المُعلن ويطابق تخطيط الذاكرة المطلوب في الـ ABI.


استدعاء دالة خارجية

تُستدعى الدوال المُعلنة بـ extern بنفس طريقة الدوال العادية.

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

لا يوجد فرق نحوي في الدعوة أثناء الاستدعاء، ويتم تشغيل الاتفاقيات الرمزية والدعوة بالكامل بواسطة الـ ABI والرابط.


الربط

التنفيذ الفعلي للدوال الخارجية يوفره الرابط في مرحلة الربط من المكتبة الخارجية. ينشئ مترجم Wave ملف كائن يتضمن استدعاءات الدوال الخارجية، حيث يقوم الرابط بحل الرموز من خلال المكتبات المحددة.

يتم تحديد طرق تعيين المكتبات من خلال أدوات البناء وخيارات CLI.


قيود

لا يقدم Wave الوظائف التالية.

  • مُؤَشِّرَات الدَوَالِّ (Function Pointer)
  • دوال التعقيبات (Callback Function)
  • إدارة الذاكرة التلقائية
  • التكامل مع استثناءات المعالجة عبر اللغات

يمكن التعامل مع هذه الميزة بشكل منفصل في الإصدارات القادمة.