Skip to content

Instantly share code, notes, and snippets.

@makerj
Last active March 16, 2018 05:23
Show Gist options
  • Save makerj/9efe2fbf5ff0baba154493a7808629e3 to your computer and use it in GitHub Desktop.
Save makerj/9efe2fbf5ff0baba154493a7808629e3 to your computer and use it in GitHub Desktop.
SWIG C and JAVA

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 매개변수를 의미한다

_

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

GENERATED-TYPE.java

%typemap(javacode) SWIGTYPE, SWIGTYPE &, SWIGTYPE *, SWIGTYPE [ANY] %{
  public void foo() {
     System.out.println("$javaclassname");
  }
%}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment