class Sample1 { // --- Native methods @native def intMethod(n: Int): Int @native def booleanMethod(b: Boolean): Boolean @native def stringMethod(s: String): String @native def intArrayMethod(a: Array[Int]): Int } // --- Code in App body will get wrapped in a main method on compilation object Sample1 extends App { // --- Main method to test our native library System.loadLibrary("Sample1") val sample = new Sample1 val square = sample.intMethod(5) val bool = sample.booleanMethod(true) val text = sample.stringMethod("java") val sum = sample.intArrayMethod(Array(1, 1, 2, 3, 5, 8, 13)) println(s"intMethod: $square") println(s"booleanMethod: $bool") println(s"stringMethod: $text") println(s"intArrayMethod: $sum") }
여기서, @native는 java에서와 같이 Native 함수를 선언하는 것입니다. 여기서는 "intMethod, booleanMethod, stringMethod, intArrayMethod"가 Native Method로 설정이 되어있습니다. 일단, Scala Class를 컴파일 해 봅시다.
$ scalac Sample1.scala
컴파일을 한 후, "Sample1$.class Sample1$delayedInit$body.class Sample1.class"등을 얻을 수 있을 것 입니다.
그렇다면, 이제 필요한 함수에 대한 Native Method를 작성해 봅시다. 일단, javah를 이용하여, 해더 파일을 만들어야 할 것입니다. 그전에, javah에서 참조가 필요한 Scala Library에 대한 Classpath설정을 한 후에 시작해야 합니다.
$ SCALA_LIB_HOME=/usr/local/Cellar/scala/2.10.2/libexec/lib/
$ SCALA_CP=$SCALA_LIB_HOME/scala-library.jar
$ javah -cp $SCALA_CP:. Sample1
위의 SCALA_LIB나, SCAPA_CP는 ~/.bashrc에다가 등록하면 편합니다.
이렇게 되면 다음과 같은 해더 파일이 나옵니다.
Sample1.h
/* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class Sample1 */ #ifndef _Included_Sample1 #define _Included_Sample1 #ifdef __cplusplus extern "C" { #endif /* * Class: Sample1 * Method: intMethod * Signature: (I)I */ JNIEXPORT jint JNICALL Java_Sample1_intMethod (JNIEnv *, jobject, jint); /* * Class: Sample1 * Method: booleanMethod * Signature: (Z)Z */ JNIEXPORT jboolean JNICALL Java_Sample1_booleanMethod (JNIEnv *, jobject, jboolean); /* * Class: Sample1 * Method: stringMethod * Signature: (Ljava/lang/String;)Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_Sample1_stringMethod (JNIEnv *, jobject, jstring); /* * Class: Sample1 * Method: intArrayMethod * Signature: ([I)I */ JNIEXPORT jint JNICALL Java_Sample1_intArrayMethod (JNIEnv *, jobject, jintArray); #ifdef __cplusplus } #endif #endif
#include "Sample1.h" #include <ctype.h> #include <string.h> // Mutate array to uppercase void uppercase(char* str) { size_t n = strlen(str); for (size_t i = 0; i < n; i++) { str[i] = toupper(str[i]); } } JNIEXPORT jint JNICALL Java_Sample1_intMethod (JNIEnv* env, jobject obj, jint num) { return num * num; } JNIEXPORT jboolean JNICALL Java_Sample1_booleanMethod (JNIEnv* env, jobject obj, jboolean boolean) { return !boolean; } JNIEXPORT jstring JNICALL Java_Sample1_stringMethod (JNIEnv* env, jobject obj, jstring string) { const char* str = env->GetStringUTFChars(string, 0); char cap[128]; strcpy(cap, str); env->ReleaseStringUTFChars(string, str); uppercase(cap); return env->NewStringUTF(cap); } JNIEXPORT jint JNICALL Java_Sample1_intArrayMethod (JNIEnv* env, jobject obj, jintArray array) { int sum = 0; jsize len = env->GetArrayLength(array); jint* body = env->GetIntArrayElements(array, 0); for (int i = 0; i < len; i++) { sum += body[i]; } env->ReleaseIntArrayElements(array, body, 0); return sum; }
이렇게 되면 scala에서 쓰이는 libSample1.so파일을 만들 준비가 완료 되었습니다.
Java뿐만이 아니라, 기본적으로 so 파일을 링크할때 lib{라이브러리 이름}.so를 사용합니다.
그렇다면, 이제 libSample1.so파일로 컴파일 해봅시다.
$ g++ -shared -fPIC -Wall -O3 -I/usr/include/ -I$JAVA_HOME/include/ -I$JAVA_HOME/include/linux/ Sample1.cpp -o libSample1.so
컴파일이 완료되면, libSample1.so가 나올 것 입니다. 그, 후 so파일과 함깨, 실행해 봅시다.
$ scala -Djava.library.path=$(pwd) -cp . Sample1
intMethod: 25
booleanMethod: false
stringMethod: JAVA
intArrayMethod: 33
다음과 같은 실행 결과를 볼 수 있습니다.
'00. Functional Programming > Scala' 카테고리의 다른 글
SBT와 SSH or SFTP를 이용한 개인용 Maven Repository 구축 (0) | 2016.09.19 |
---|---|
Scala에서 시스템 커멘드를 사용하기 (0) | 2016.08.16 |
Scala IDE 설치 & Hello World Example (0) | 2014.11.12 |