외부 함수 인터페이스

외부 함수 인터페이스(Foreign function interface, FFI)는 한 프로그래밍 언어로 작성된 프로그램이 다른 언어로 작성되거나 컴파일된 루틴을 호출하거나 서비스를 사용할 수 있게 하는 메커니즘이다. FFI는 종종 동적 링크 라이브러리의 바이너리 파일로 호출이 이루어지는 환경에서 사용된다.

 명명 
이 용어는 커먼 리스프 사양에서 유래되었는데, 이는 언어 간 호출을 가능하게 하는 프로그래밍 언어 기능을 명시적으로 지칭한다. 이 용어는 하스켈, 러스트, PHP, 파이썬 및 LuaJIT (루아)의 인터프리터 및 컴파일러 문서화에서도 공식적으로 자주 사용된다. 다른 언어들은 다른 용어를 사용한다. 에이다는 언어 바인딩을 가지고 있으며, 자바는 자바 네이티브 인터페이스 (JNI), 자바 네이티브 액세스 (JNA) 또는 자바 22부터 외부 함수 및 메모리 API를 가지고 있다. 외부 함수 인터페이스는 이러한 서비스를 제공하는 메커니즘을 위한 일반 용어가 되었다.

 동작 
외부 함수 인터페이스의 주요 기능은 한 프로그래밍 언어(호스트 언어 또는 FFI를 정의하는 언어)의 의미 및 호출 규약과 다른 언어(게스트 언어)의 의미 및 규약을 연결하는 것이다. 이 과정은 또한 양쪽의 런타임 환경과 응용 프로그램 이진 인터페이스를 고려해야 한다. 이는 여러 가지 방법으로 수행될 수 있다.
 게스트 언어 함수가 호스트 언어에서 호출 가능하도록 특정 방식으로 지정하거나 구현하도록 요구하며, 종종 일종의 호환성 라이브러리를 사용한다.
 게스트 언어 함수를 적절한 글루 코드로 자동으로 래핑하여 필요한 변환을 수행하는 도구 사용.
 래퍼 라이브러리 사용.
 언어 간에 사용할 수 있는 호스트 언어 기능 집합을 제한한다. 예를 들어, C에서 호출되는 C++ 함수는 (일반적으로) 참조 매개변수를 포함하거나 예외를 발생시킬 수 없다.

FFI는 다음 고려 사항으로 인해 복잡해질 수 있다.
 한 언어가 쓰레기 수집 (GC)을 지원하고 다른 언어가 지원하지 않는 경우, 비GC 언어 코드가 다른 언어의 GC 실패를 유발하지 않도록 주의해야 한다. 예를 들어, JNI에서 자바로부터 받은 객체 참조를 "유지"하는 C 코드는 이 정보를 자바 가상 머신 또는 자바 런타임 환경 (JRE)에 성공적으로 전달해야 한다. 그렇지 않으면 C가 객체를 사용하기 전에 자바가 객체를 삭제할 수 있다. (C 코드는 더 이상 그 객체가 필요 없을 때 해당 객체에 대한 링크를 명시적으로 해제해야 한다.)
 복잡하거나 사소하지 않은 객체 또는 데이터 형식은 한 환경에서 다른 환경으로 매핑하기 어려울 수 있다.
 위 매핑 문제로 인해 두 언어가 동일한 변경 가능한 객체의 동일한 인스턴스에 대한 참조를 유지하는 것이 불가능할 수 있다.
 한 언어 또는 두 언어 모두 가상 머신 (VM)에서 실행될 수 있으며, 둘 다인 경우 종종 다른 VM이다.
 언어 간 상속 및 자료형 체계 또는 객체 구성 모델 간의 차이점과 같은 다른 차이점은 특히 어려울 수 있다.
UML ffi.svg

FFI의 예시는 다음과 같다.
 에이다 언어 바인딩은 외부 함수를 호출할 뿐만 아니라 에이다 함수 및 메소드를 에이다가 아닌 코드에서 호출할 수 있도록 내보낼 수 있게 한다.
 C++는 C와 사소한 FFI를 가지고 있는데, 이 언어들은 상당한 공통 부분 집합을 공유하기 때문이다. C++의  선언의 주요 효과는 C++ 네임 맹글링을 비활성화하는 것이다. 다른 언어의 경우 별도의 유틸리티 또는 미들웨어가 사용되며, 그 예시는 다음과 같다.
 그놈 프로젝트: GObject Introspection
 SWIG
 크로미엄 프로젝트: 블링크 및 V8 엔진은 표준 자바스크립트 인터페이스를 위한 인터페이스 정의 언어 (IDL) 컴파일러를 사용한다.
 다른 IDL 컴파일러
 클린은 C 또는 stdcall 호출 규약을 따르는 모든 언어와 양방향 FFI를 제공한다.
 커먼 리스프
 Compiled Native Interface (CNI), GNU 컴파일러 환경에서 사용되는 JNI의 대안.
 컴포넌트 오브젝트 모델의 기본 중 하나는 공통 인터페이스 형식인데, 이는 문자열 및 배열에 대해 비주얼 베이직과 동일한 형식을 기본적으로 사용한다.
 D는 C++와 동일한 방식으로 extern "C"를 통해 extern (C++)를 사용한다.
 다트는 모바일, 명령줄 및 서버 애플리케이션을 위한 C 코드를 호출하는 dart:ffi 라이브러리를 포함한다.
 파이썬, 펄, Tcl, 루비와 같은 동적 프로그래밍 언어는 모두 C, C++ 또는 C/C++ 호출 규약을 따르는 다른 언어로 작성된 네이티브 코드에 쉽게 접근할 수 있다.
 팩터는 C, 포트란, 오브젝티브-C, 및 윈도우 COM에 대한 FFI를 가지고 있으며, 이들 모두는 임의의 공유 라이브러리를 동적으로 가져오고 호출할 수 있게 한다.
 포트란 2003은 ISO_C_BINDING 모듈을 가지고 있는데, 이는 상호 운용 가능한 자료형(고유 자료형 및 POD 구조체 모두), 상호 운용 가능한 포인터, 상호 운용 가능한 전역 데이터 저장소, 그리고 포트란에서 C를 호출하고 C에서 포트란을 호출하는 메커니즘을 제공한다. 포트란 2018 표준에서 개선되었다.
 Go는 "C" 의사 패키지를 통해 C 코드를 직접 호출할 수 있다.
 구글 웹 툴킷 (GWT)에서 자바는 자바스크립트로 컴파일되며, 자바 소스 코드가 임의의 자바스크립트 함수를 호출하고 자바스크립트가 다시 자바를 호출할 수 있도록 하는 JSNI라는 FFI를 가지고 있다.
 하스켈
 자바 네이티브 인터페이스 (JNI)는 자바와 C/C++ 간의 인터페이스를 제공하며, 이는 자바가 배포되는 대부분의 시스템에서 선호되는 시스템 언어이다. 자바 네이티브 액세스 (JNA)는 글루 코드를 작성할 필요 없이 네이티브 라이브러리와의 인터페이스를 제공한다. 자바 22에서는 JNI의 현대적 대체품인 외부 함수 및 메모리 API가 추가되었다.
 LuaJIT, JIT 컴파일의 Lua 구현은 "순수한 Lua 코드에서 외부 C 함수를 호출하고 C 데이터 구조를 사용할 수 있는" FFI를 가지고 있다.
 닷넷은  속성을 통해 FFI를 가지고 있다.
 님은 C, C++, 오브젝티브-C의 소스를 사용할 수 있게 하는 FFI를 가지고 있다. 또한 자바스크립트와도 인터페이스할 수 있다.
 자바스크립트는 일반적으로 시스템 라이브러리나 실행할 명령에 직접 접근을 제공하지 않는 웹 브라우저 런타임 내부에서 실행되지만, 몇 가지 예외가 있다.
 Node.js는 사전 컴파일된  모듈을 열 수 있는 함수를 제공하며, 이는 내장되지 않은 리소스에 대한 접근을 제공할 수 있다.
 디노는  함수를 통해 일종의 FFI 인터페이스를 제공한다.
 Bun은 내장 모듈인 bun:ffi를 제공하여 자바스크립트에서 직접 네이티브 라이브러리를 효율적으로 호출할 수 있다.
 줄리아는 C (및 포트란과 같은 다른 언어)를 호출하기 위한 ccall 키워드를 가지고 있으며, 파이썬 (예: 객체 지향 지원 및 GC 지원 제공), 자바(스칼라와 같은 다른 JDK 언어 지원) 및 R과 같은 일부 언어에 대해 유사한 보일러플레이트 없는 지원을 제공하는 패키지가 제공된다. C++와의 대화형 사용도 Cxx.jl 패키지로 가능하다.
 PhoneGap (이전에는 Apache Callback이라고 불렸지만, 현재는 아파치 코도바)은 HTML, CSS 및 자바스크립트를 사용하여 네이티브 모바일 애플리케이션을 구축하기 위한 플랫폼이다. 또한 가속도계, 카메라 (PhotoLibrary 및 SavedPhotoAlbum 포함), 나침반, 저장소 (SQL 데이터베이스 및 localStorage), 알림, 미디어 및 캡처 (오디오 및 비디오 재생 및 녹음), 파일, 연락처 (주소록), 이벤트, 장치 및 연결 정보와 같은 휴대폰의 네이티브 기능의 메소드 및 속성에 접근하기 위한 자바스크립트 콜백 함수를 통한 FFI를 가지고 있다.,.
 PHP는 C에 대한 FFI를 제공한다.
 Pony는 FFI를 통해 C와의 통합을 지원한다.
 파이썬은 ctypes 및 cffi 모듈을 제공한다. 예를 들어, ctypes 모듈은 공유 라이브러리 또는 동적 링크 라이브러리 (DLL)에서 C 함수를 즉석에서 로드하고 파이썬과 C 의미론 간에 간단한 자료형을 자동으로 변환한다.
import ctypes
libc = ctypes.CDLL("/lib/libc.so.6")  # Under Linux/Unix
t = libc.time(None)                   # Equivalent C code: t = time(NULL)
print(t)
 P/Invoke는 마이크로소프트 공통 언어 런타임과 네이티브 코드 간의 인터페이스를 제공한다.
 래킷은 매크로에 크게 의존하는 네이티브 FFI를 가지고 있으며, 임의의 공유 라이브러리를 동적으로 가져올 수 있게 한다.
 라쿠는 루비, 파이썬, 펄, 브레인퍽, Lua, C, C++, Go, 스킴 (가일, 갬빗), 그리고 러스트를 호출할 수 있다.
 루비는 ffi 젬 또는 표준 라이브러리 fiddle을 통해 FFI를 제공한다.
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
 러스트는 다양한 표준 응용 프로그램 이진 인터페이스 (ABI)를 가진 함수에 대한 외부 함수 인터페이스를 정의한다. Elixir와 인터페이스하기 위한 Rustler 라이브러리도 있다.
 V (Vlang)는 C 소스 코드 및 라이브러리 사용을 포함하고 지원한다.
 비주얼 베이직은 비유니코드 C 함수를 호출할 수 있는 선언적 구문을 가지고 있다.
 울프럼 언어는 C++, 자바, 닷넷 및 기타 언어에 대한 바인딩을 통해 다른 언어 간의 코드 양방향 호출을 가능하게 하는 Wolfram Symbolic Transfer Protocol (WSTP)라는 기술을 제공한다.
 Zig는 내장  함수를 사용하여 C에 대한 FFI를 제공한다.

또한 많은 FFI는 자동으로 생성될 수 있다. 예를 들어, SWIG가 있다. 그러나 확장 언어의 경우, 더 작은 확장 언어 본체가 GIMP에 작은 플러그인을 작성하는 것과 같이, 더 큰 호스트 언어 본체에서 서비스를 호출하는 게스트일 때 게스트와 호스트 관계의 의미적 반전이 발생할 수 있다.

일부 FFI는 독립적인 함수로 제한되는 반면, 다른 FFI는 객체 또는 클래스에 내장된 함수(종종 메소드 호출이라고 함) 호출도 허용한다. 일부는 복잡한 자료형이나 객체를 언어 경계를 넘어 마이그레이션하는 것까지 허용한다.

대부분의 경우 FFI는 고급 프로그래밍 언어에 의해 정의되어, 일반적으로 C 또는 C++와 같은 시스템 프로그래밍 언어인 저급 프로그래밍 언어에서 정의되고 구현된 서비스를 사용할 수 있게 한다. 이는 일반적으로 운영체제 (OS) API가 정의된 언어로 OS 서비스에 접근하거나 성능 목표를 위해 수행된다.

많은 FFI는 호출된 언어가 호스트 언어에서 서비스를 호출할 수 있는 수단도 제공한다.

외부 함수 인터페이스라는 용어는 마이크로소프트 공통 언어 런타임과 같이 공통 기반이 제공되어 모든 CLR 준수 언어가 다른 언어에서 정의된 서비스를 사용할 수 있게 하는 다국어 런타임을 설명하는 데 일반적으로 사용되지 않는다. (그러나 이 경우 CLR은 런타임 외부를 호출하기 위한 FFI인 P/Invoke를 포함한다.) 또한 자바 원격 함수 호출 (RMI), RPC, CORBA, SOAP 및 D-Bus와 같은 많은 분산 컴퓨팅 아키텍처는 다른 서비스가 다른 언어로 작성되도록 허용한다. 이러한 아키텍처는 일반적으로 FFI로 간주되지 않는다.

 특수 사례 
클로저와 자바, Elixir와 얼랭처럼 언어들이 동일한 바이트코드 VM으로 컴파일되는 특수 사례가 있다. 인터페이스가 없기 때문에 엄밀히 말하면 FFI가 아니지만 사용자에게는 동일한 기능을 제공한다.

 같이 보기 
 호출 규약
 네임 맹글링
 응용 프로그램 이진 인터페이스
 SWIG
 원격 프로시저 호출

 각주 

 외부 링크 
  c2.com: Foreign function interface

분류:API
분류:함수 (프로그래밍)