@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 */
%}
'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 |