(작성중)
SWIG는 기존에 작성된 C 또는 C++ 라이브러리를 다양한 언어에 포팅하는데 사용되는 도구이다. 이 글에서는 C 라이브러리를 JAVA와 연결하는데 필요한 정보를 정리한다. 이 글에서 C 라이브러리의 이름은 mylib 라고 가정한다.
mylib.c, mylib.h 작성 -> mylib.i SWIG 중간 언어 작성 -> mylib.i를 swig로 변환 -> libmylib.so 빌드 -> libmylib_java.so 빌드 -> Java에서 사용 및 테스트
_
흐름이 굉장히 길고, 한 프로세스는 이전 프로세스에 영향을 받는다. 따라서 mylib.h에 변경이 하나라도 생기면 나머지 모든 작업을 다시 테스트 해봐야 한다. mylib.h에서 제공하는 API가 충분히 고정되었을 때 SWIG 컨버팅을 시작해야 한다.
언어간 연동을 위해 필요한 기본이 되는 단위인 모듈은 SWIG 중간 언어를 사용해서 작성한다. mylib.i 파일에 %module mylib
이라고 작성하고, swig를 사용해 변환하면 다음과 같이 3개의 파일이 자동으로 생성된다.
-
mylib_wrap.c - JNI 코드 (내부 구현)
-
mylibJNI.java - Java Native를 정의한 보조 클래스 (내부 구현)
-
mylib.java - Java API를 정의한 클래스. 사용자는 이 Java 클래스를 사용한다.
-
%inline %{ C_CODES %}
mylib.i 파일과, mylib_wrap.c 파일에 동시에 해당 코드를 작성한 것과 같은 효과를 낸다.%{ C_CODES %}
는 mylib_wrap.c 파일에만 작성한 효과를 내므로,%inline
이 더 강력하다. -
%ignore SYMBOL_NAME
해당 심볼을 완전히 무시한다.ignore
는 해당 심볼이 나타나기 전에 선언되어야 한다. 이미 해당 심볼을 파싱하고 난 다음에는 의미가 없다. -
extend STRUCT_OR_CLASS_NAME { JAVA_CODES };
해당 이름을 갖는 구조체나 클래스에 추가 메소드를 삽입한다.struct Foo
를 대상으로 코드를 추가한 뒤, 생성된 Foo.java 파일을 열어보면 추가된 코드를 확인할 수 있다.
기본형(Primitive Types)를 제외한 사용자 정의 타입들은 SWIG에서 정의한 것이 없다. 따라서 해당 타입들을 적절하게 사용하려면 직접 SWIG의 typemap
기능을 사용해서 해당 타입을 어떻게 다룰 지를 정해줘야 한다.
_
typemap
을 사용하면 해당 타입에 대한 완전한 제어를 할 수 있게 되지만 다음과 같은 위험이 있다.
-
개발자가 직접 C/C++ 연동 코드 일부를 작성해야 한다.
-
개발자가 직접 연동 코드를 작성하기 때문에, JNI를 깊게 알아야 한다.
-
조금이라도 잘못 작성하면 SWIG 전체 연동 코드를 망칠 수 있다.
간단하게 정리한 typemap
디렉티브의 종류 (더 많지만 생략)
-
%typemap(jstype) PROTOTYPE "TYPENAME";
mylib.java에 나타날 타입 -
%typemap(jtype) PROTOTYPE "TYPENAME";
mylibJNI.java에 나타날 타입 -
%typemap(jni) PROTOTYPE "TYPENAME";
mylib_wrap.c에 나타날 타입 -
%typemap(in) (PROTOTYPE) { JNICODE };
mylib_wrap.c에 매개변수로 넘어온 jtype을 C 타입으로 변환하는 코드를 직접 작성하는데 사용 -
%typemap(argout) PROTOTYPE %{ JNICODE %};
mylib_wrap.c에 매개변수로 넘어온 jtype과 관련된 동작이 모두 끝난 부분에 추가될 C 코드 -
%fragment("FRAGMENT_NAME", "header") { C_CODES }
typemap에 반복적으로 사용될 코드를 묶고, 이름을 둬서 나중에 typemap을 적용할 때 참조할 수 있게 한다.typemap(in), typemap(varin)
처럼 같은 목적의 C 코드가 반복될 것으로 예상되는 부분에 활용. -
%apply TARGET_TYPE { CURRENT_TYPE }
CURRENT_TYPE으로 선언된 코드를 마치 TARGET_TYPE으로 선언한 것과 같이 취급한다.
%typemap(in)
은 해당 타입을 C 타입으로 변환하는 방법을 지시하는데 사용된다.
_
먼저 가장 간단한 예제는 다음과 같다.
%typemap(in) int {
$1 = $input;
printf("Received an integer : %d\n", $1);
}
int 타입이 매개변수로 들어왔을 때, mylib_wrap.c에서 처리하는 방법을 지시했다. 코드가 뭘 하는지는 명확하므로 설명하지 않고, 새로 등장한 키워드를 정리한다.
-
$1
매개변수는 jint로 넘어왔을 테니, 이를 담을 c타입을 선언한다 -
$input
매개변수를 의미한다
_
SWIG는 다양한 헤더를 통해, 개발자가 필요로 할 것으로 에상하는 기능을 제공한다. C and JAVA 상황에서 유용한 헤더는 다음과 같다.
%include <stdint.i>
C의 stdint와 같은 효과를 낸다.%include <typemaps.i>
기초 자료형에 대해 INPUT, OUTPUT, INOUT 타입맵을 제공한다. 포인터 매개변수 형태로 값을 반환하는 라이브러리와 연동할 때 매우 유용하다%include <various.i>
char**, BYTE array타입을 지원한다
mylib.java
%pragma(java) modulecode=%{
/* comments on mylib.java */
%}
mylibJNI.java
%pragma(java) jniclasscode=%{
/* comments on mylibJNI.java */
%}
mylib_wrap.c
%{
/* cmments on mylib_wrap.c ! */
%}
GENERATED-TYPE.java
%typemap(javacode) SWIGTYPE, SWIGTYPE &, SWIGTYPE *, SWIGTYPE [ANY] %{
public void foo() {
System.out.println("$javaclassname");
}
%}