본문 바로가기

JAVA

SWIG C and JAVA

@markdown


# SWIG C and JAVA


(작성중)


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 컨버팅을 시작해야 한다.


## 모듈 작성 (mylib.i)

언어간 연동을 위해 필요한 기본이 되는 단위인 모듈은 SWIG 중간 언어를 사용해서 작성한다. mylib.i 파일에 `%module mylib`이라고 작성하고, swig를 사용해 변환하면 다음과 같이 3개의 파일이 자동으로 생성된다.

* mylib_wrap.c - JNI 코드 (내부 구현)

* mylibJNI.java - Java Native를 정의한 보조 클래스 (내부 구현)

* mylib.java - Java API를 정의한 클래스. 사용자는 이 Java 클래스를 사용한다.


## common directive


* `%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 파일을 열어보면 추가된 코드를 확인할 수 있다.


## typemap directive


기본형(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) TYPE


`%typemap(in)`은 해당 타입을 C 타입으로 변환하는 방법을 지시하는데 사용된다.

_

먼저 가장 간단한 예제는 다음과 같다.

```

%typemap(in) int {

    $1 = $input;

    printf("Received an integer : %d\n",  $1);

}

```

int 타입이 매개변수로 들어왔을 때, mylib_wrap.c에서 처리하는 방법을 지시했다. 코드가 뭘 하는지는 명확하므로 설명하지 않고, 새로 등장한 키워드를 정리한다.

* `$1` 매개변수는 jint로 넘어왔을 테니, 이를 담을 c타입을 선언한다

* `$input` 매개변수를 의미한다

_

`typemap(in) TYPE`에서 TYPE을 와일드카드처럼 어떤 타입이던지 매핑하고 싶은 경우 와일드카드 타입인 `SWIGTYPE`을 사용한다. 다음의 간단한 예제는 자동 생성되는 모든 타입의 Java 코드에 자신의 클래스명을 출력하는 foo 메소드를 추가하는 코드이다.

```

%typemap(javacode) SWIGTYPE, SWIGTYPE &, SWIGTYPE *, SWIGTYPE [ANY] %{

  public void foo() {

     System.out.println("$javaclassname");

  }

%}

```



## SWIG 헤더


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 ! */ 
%}
```


'JAVA' 카테고리의 다른 글

JNI 헤더 만들기  (0) 2015.11.25
Java bouncycastle 사용하기  (0) 2015.09.17
GSON  (0) 2015.08.13
Java XML 생성 및 저장  (0) 2014.07.20
Java XML 정리  (0) 2014.07.19