واسطه تابع خارجی

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

## نامگذاری
اصطلاح "واسطه توابع خارجی" (FFI) از مشخصات زبان برنامه‌نویسی Common Lisp می‌آید که به‌طور صریح به ویژگی زبان برنامه‌نویسی اشاره دارد که امکان فراخوانی‌های بین زبان‌ها را فراهم می‌کند. این اصطلاح معمولاً توسط مستندها مفسر و کامپایلر زبان‌هایی مانند Haskell, Rust, PHP, Python و LuaJIT به‌طور رسمی استفاده می‌شود. برخی زبان‌ها مانند Ada به جای آن از اصطلاح "bindings" و جاوا از JNI یا JNA استفاده می‌کنند. FFI به‌طور کلی به مکانیزم‌هایی اشاره دارد که چنین خدمتهایی را ارائه می‌دهند.

## عملکرد
* وظیفه اصلی یک واسط توابع خارجی (FFI) تطبیق معنای دستورها و قراردادهای فراخوانی یک زبان برنامه‌نویسی (زبان میزبان، یا زبانی که FFI را تعریف می‌کند) با معنای دستورها و قراردادهای زبان دیگری (زبان مهمان) است. این فرایند باید محیط‌های اجرایی و رابط‌های باینری برنامه‌های هر دو زبان را نیز مد نظر قرار دهد. این کار می‌تواند به چندین روش انجام شود:
** نیاز به این که توابع زبان مهمان که قرار است به زبان میزبان قابل فراخوانی باشند به روشی خاص مشخص یا پیاده‌سازی شوند، که اغلب از نوعی کتابخانه سازگاری استفاده می‌کنند.
** استفاده از ابزاری برای *بسته‌بندی* خودکار توابع به زبان مهمان با کد چسب مناسب، که هر گونه ترجمه لازم را انجام می‌دهد.
** استفاده از کتابخانه لفاف دار
** محدود کردن مجموعه ای از توانایی‌های زبان میزبان که می‌تواند بین زبانی استفاده شود. به عنوان مثال، توابع C++ که از C فراخوانی می‌شوند ممکن است (به‌طور کلی) شامل پارامترهای مرجع یا استثناهای پرتابی نباشند.
** Iها ممکن است با ملاحظات زیر پیچیده شوند:
** اگر یک زبان از جمع‌آوری زباله (GC) پشتیبانی می‌کند و زبان دیگر از جمع‌آوری زباله پشتیبانی نمی‌کند. باید مراقب بود که کد زبان غیر GC باعث از کار افتادن GC در زبان دیگر نشود. برای مثال، در JNI، کد C که ارجاعات شی را که از جاوا دریافت می‌کند نگه می‌دارد، باید این اطلاعات را با موفقیت به ماشین مجازی جاوا یا محیط زمان اجرا جاوا (JRE) منتقل کند، در غیر این صورت، جاوا ممکن است اشیاء را قبل از اینکه C با آنها تمام شود حذف کند. (کد C همچنین باید به صراحت پیوند خود را به هر یک از این شیء آزاد کند، زمانی که C دیگر نیازی به آن شیء نداشته باشد)
** ممکن است نگاشت اشیاء یا نوع داده‌های پیچیده یا غیر پیش پا افتاده از یک محیط به محیط دیگر دشوار باشد.
*** به دلیل مشکل نگاشت فوق، ممکن است برای هر دو زبان امکان حفظ ارجاعات به یک نمونه از یک شیء قابل تغییر وجود نداشته باشد.
*** یک یا هر دو زبان ممکن است روی ماشین مجازی (VM) در حال اجرا باشند. علاوه بر این، اگر هر دو وجود داشته باشند، اینها اغلب ماشین‌های مجازی متفاوتی هستند.
*** وراثت بین زبانی و تفاوت‌های دیگر، مانند بین سیستم‌های نوع یا بین مدل‌های ترکیب شی، ممکن است به‌ویژه دشوار باشد.
***** زبان **Ada** دارای پیوندهایی است که اجازه می‌دهد نه‌تنها توابع خارجی را فراخوانی کند، بلکه توابع و متدهای خودش را هم طوری صادر کند که از زبان‌های غیر Ada قابل فراخوانی باشند.
***** زبان **++C** یک رابط ساده با زبان **C** دارد، چون بخش بزرگی از دستور زبان آن‌ها مشترک است. دستور `extern "C"` در ++C باعث غیرفعال شدن نام‌گذاری پیچیده (name mangling) در ++C می‌شود. برای تعامل با زبان‌های دیگر، معمولاً از ابزارها یا میان‌افزارهای خاصی استفاده می‌شود، مانند:
****** GObject Introspection در پروژهٔ GNOME
****** SWIG
****** استفاده از **IDL compiler** در پروژهٔ Chromium برای تعریف رابط‌های استاندارد JavaScript (در موتورهای Blink و V8)
****** سایر مفسرهای IDL
***** زبان **Clean** دارای یک FFI دوطرفه با تمام زبان‌هایی است که از قرارداد فراخوانی C یا stdcall پیروی می‌کنند.
***** **Common Lisp** از چندین روش برای تعامل با کدهای بومی استفاده می‌کند.
***** **CNI** (Compiled Native Interface)، جایگزینی برای JNI در محیط کامپایلر GNU است.
***** در **COM (Component Object Model)**، فرمت رابطی مشترک استفاده می‌شود که انواع داده‌ای مشابه Visual Basic برای رشته‌ها و آرایه‌ها دارد.
***** زبان **D** نیز مانند ++C از `extern "C"` (در قالب `extern (C++)`) استفاده می‌کند.
***** **Dart** دارای کتابخانه‌ای به نام `dart:ffi` است که اجازه می‌دهد کد C بومی را در اپ‌های موبایل، خط فرمان یا سرور فراخوانی کند.
***** زبان‌های پویا مثل **Python**, **Perl**, **Tcl** و **Ruby** دسترسی آسانی به کدهای C/C++ (با رعایت قراردادهای فراخوانی) فراهم می‌کنند.
***** **Factor** رابط‌هایی برای C, Fortran, Objective-C و COM دارد که امکان بارگذاری و استفادهٔ پویا از کتابخانه‌های مشترک را فراهم می‌کند.
***** **Fortran 2003** ماژولی به نام `ISO_C_BINDING` دارد که امکان تعریف انواع دادهٔ سازگار، اشاره‌گرها و متغیرهای مشترک، و فراخوانی دوطرفهٔ توابع با زبان C را فراهم می‌کند. این قابلیت در Fortran 2018 بهبود یافته است.
***** **Go** از طریق بستهٔ `C` قابلیت فراخوانی مستقیم کد C را دارد.
***** در **Google Web Toolkit (GWT)** که کد Java به JavaScript ترجمه می‌شود، رابطی به نام JSNI وجود دارد که اجازه می‌دهد Java با توابع JavaScript تعامل داشته باشد و بالعکس.
***** زبان **Haskell** نیز روش‌های متنوعی برای استفاده از کدهای بومی دارد.
***** **JNI (Java Native Interface)** برای اتصال Java با C/C++ به‌کار می‌رود. ابزارهای دیگری مثل **JNA** و **JNR** هم در همین زمینه کاربرد دارند.
***** **LuaJIT** رابطی برای استفادهٔ مستقیم از توابع و ساختارهای C از درون کد Lua دارد.
***** زبان **Nim** دارای FFI برای تعامل با C, C++ و Objective-C است و حتی قابلیت ارتباط با JavaScript را هم دارد.
***** در محیط JavaScript (که اغلب در مرورگر اجرا می‌شود و دسترسی مستقیم به کتابخانه‌های سیستمی ندارد)، استثناهایی مثل:
****** **Node.js** که امکان بارگذاری ماژول‌های `.node` کامپایل‌شده را دارد
****** **Deno** که از `dlopen()` برای بارگذاری کتابخانه‌های بومی استفاده می‌کند
****** **Bun** که ماژولی به نام `bun:ffi` برای تعامل سریع با کتابخانه‌های بومی فراهم می‌کند
***** زبان **Julia** از کلمهٔ کلیدی `ccall` برای فراخوانی توابع C و سایر زبان‌ها (مثل Fortran) استفاده می‌کند. بسته‌هایی برای تعامل با Python, Java، و R هم وجود دارند. حتی با استفاده از `Cxx.jl` می‌توان به صورت تعاملی با C++ کار کرد.
***** **Cordova** (که قبلاً PhoneGap و Apache Callback نام داشت) چارچوبی است برای ساخت اپ‌های موبایل بومی با HTML/CSS/JavaScript و دارای رابط‌هایی برای دسترسی به ویژگی‌های موبایل از طریق JavaScript، مثل دوربین، شتاب‌سنج، صدا، و غیره.
***** زبان **PHP** نیز رابطی برای استفاده از کدهای C دارد.
***** زبان **Pony** از FFI برای ارتباط با کدهای C پشتیبانی می‌کند.
***** **Python** دارای ماژول‌هایی به نام `ctypes` و `cffi` است که امکان بارگذاری و فراخوانی توابع C از فایل‌های مشترک را فراهم می‌کنند. به‌عنوان مثال `ctypes` می‌تواند توابع C را از DLLها بارگذاری کرده و نوع‌های داده را به‌طور خودکار بین Python و C ترجمه کند.
***** **P/Invoke** (Platform Invocation Services) قابلیتی در پلتفرم .NET مایکروسافت است که امکان ارتباط بین محیط اجرایی مشترک (.NET CLR) و کدهای بومی (native) را فراهم می‌کند.
***** زبان **Racket** دارای FFI داخلی است که مبتنی بر ماکروها عمل می‌کند و اجازه می‌دهد به‌صورت پویا کتابخانه‌های مشترک (shared libraries) دلخواه را وارد کند.
***** زبان **Raku** (که جانشین Perl است) توانایی فراخوانی مستقیم زبان‌های متنوعی از جمله Ruby, Python, Perl, Brainfuck, Lua, C، C++, Go, Scheme (مانند Guile و Gambit)، و Rust را دارد.
***** زبان **Ruby** از FFI پشتیبانی می‌کند، یا از طریق افزونه‌ای به نام `ffi gem`، یا از طریق کتابخانهٔ استانداردی به نام `fiddle`.
***** 
```ruby
require 'fiddle'

libm = Fiddle.dlopen('/lib/libm.so.6')
# Equivalent to: double floor(double x);
floor = Fiddle::Function.new(
  libm.sym('floor'),     # ptr is a referenced function(, or symbol), of a Fiddle::Handle.
  [Fiddle::TYPE_DOUBLE], # args is an Array of arguments, passed to the ptr function.
  Fiddle::TYPE_DOUBLE    # ret_type is the return type of the function
)
# Equivalent to: floor(3.14159);
floor.call(3.14159) #=> 3.0
```
راست (Rust) یک رابط تابع خارجی (FFI) برای توابع با انواع مختلف رابط‌های باینری کاربردی استاندارد (ABIs) تعریف می‌کند. همچنین یک کتابخانه برای برقراری ارتباط با الیکسر (Elixir) به نام Rustler وجود دارد. V (Vlang) می‌تواند کد منبع C و کتابخانه‌ها را شامل و پشتیبانی کند. Visual Basic یک نحو اعلامی دارد که به آن اجازه می‌دهد تا توابع C غیر یونی‌کد را فراخوانی کند. زبان Wolfram تکنولوژی به نام پروتکل انتقال نمادین Wolfram (WSTP) دارد که امکان فراخوانی دوطرفه کد بین زبان‌های مختلف را فراهم می‌کند. Zig رابط FFI به C را با استفاده از تابع داخلی cImport فراهم می‌کند. علاوه بر این، بسیاری از FFIها می‌توانند به‌طور خودکار تولید شوند؛ به عنوان مثال، SWIG. با این حال، در مورد زبان‌های افزونه، ممکن است معکوس شدن معنایی رابطه مهمان و میزبان رخ دهد، جایی که یک زبان افزونه کوچک به عنوان مهمان خدماتی را در یک زبان میزبان بزرگتر فراخوانی می‌کند. برخی از FFIها فقط به توابع ایستاده محدود هستند، در حالی که برخی دیگر اجازه می‌دهند توابعی که در یک شی یا کلاس جاسازی شده‌اند، فراخوانی شوند. در بیشتر موارد، یک FFI توسط یک زبان سطح بالاتر تعریف می‌شود تا از خدماتی که در یک زبان سطح پایین‌تر مانند C یا C++ تعریف و پیاده‌سازی شده‌اند، استفاده کند. این معمولاً برای دسترسی به خدمات سیستم‌عامل یا اهداف عملکرد انجام می‌شود. بسیاری از FFIها همچنین به زبان فراخوانی شده این امکان را می‌دهند که خدماتی را در زبان میزبان فراخوانی کند. اصطلاح رابط تابع خارجی معمولاً برای توصیف زمان‌بندی‌های چندزبانه مانند زمان‌بندی مشترک زبان (CLR) مایکروسافت استفاده نمی‌شود، جایی که یک بستر مشترک فراهم می‌شود که به هر زبان CLR سازگار اجازه می‌دهد از خدماتی که در هر زبان دیگری تعریف شده استفاده کند.
**** علاوه بر این، بسیاری از FFIها می‌توانند به‌طور خودکار تولید شوند، مانند SWIG. با این حال، در مورد زبان‌های توسعه، ممکن است یک وارونگی معنایی در رابطه مهمان و میزبان رخ دهد، زمانی که بخش کوچکتری از زبان توسعه، به عنوان مهمان، سرویس‌هایی را از زبان میزبان بزرگتر فراخوانی می‌کند، مانند نوشتن یک پلاگین کوچک برای GIMP.
**** برخی از FFIها محدود به توابع مستقل هستند، در حالی که برخی دیگر اجازه می‌دهند که توابع تعبیه شده در یک شیء یا کلاس (که معمولاً به آن فراخوانی متد گفته می‌شود) نیز فراخوانی شوند؛ برخی حتی اجازه می‌دهند که داده‌های پیچیده یا اشیاء از مرز زبان عبور کنند.
**** در بیشتر موارد، FFI توسط یک زبان سطح بالاتر تعریف می‌شود تا از خدمات تعریف‌شده و پیاده‌سازی‌شده در یک زبان سطح پایین‌تر استفاده کند، معمولاً زبان‌های برنامه‌نویسی سیستم مانند C یا C++. این کار معمولاً برای دسترسی به خدمات سیستم‌عامل (OS) در زبان‌هایی که API سیستم‌عامل در آن تعریف شده یا برای اهداف بهینه‌سازی عملکرد انجام می‌شود.
**** اصطلاح «رابطه توابع خارجی» به‌طور کلی برای توصیف زمان‌های اجرای چندزبانه مانند Microsoft Common Language Runtime (CLR) استفاده نمی‌شود، جایی که زیرساخت مشترک به زبان‌های سازگار با CLR این امکان را می‌دهد که از خدمات زبان‌های دیگر استفاده کنند. بسیاری از معماری‌های محاسبه‌ها توزیع‌شده مانند RMI جاوا، RPC, CORBA, SOAP و D-Bus این امکان را می‌دهند که خدمات مختلفی در زبان‌های مختلف نوشته شوند، اما این معماری‌ها معمولاً به عنوان FFI در نظر گرفته نمی‌شوند.

## موارد خاص
برخی از موارد خاص وجود دارد که در آن‌ها زبان‌ها به کد بایت یکسانی کامپایل می‌شوند، مانند Clojure و Java یا Elixir و Erlang. از آنجا که هیچ رابطی وجود ندارد، به‌طور دقیق‌تر نمی‌توان آن‌ها را FFI در نظر گرفت، در حالی که همان عملکردها را برای کاربر ارائه می‌دهند.

## منابع
رده:زبان‌های برنامه‌نویسی
رده:واسط‌های برنامه‌نویسی کاربردی
رده:زیرروال‌ها