Processing を使ってユーティリティ系のソフトを作っていると、どうしてもかゆいところに手がとどかない(システム環境設定系とか)
Swiftで実装すればAPIを叩くのに苦労はしないが、
Java側で Runtime#exec
とか ProcessBuilder
を使うのはなんか違う気がするので、
どうにかしてして、Javaで実装されたメソッドのようにSwiftで実装した関数を叩きたい!
JNA(Java Native Access)
Processingの実体はPAppletクラスを継承したJavaクラスであるため、
Javaの文法、資産を利用することができる
Javaには、JNI(Java Native Interface)と呼ばれる、C/C++などで実装されたネイティブコードと連携する仕組みがある。
このJNIを利用すれば、Swiftで実装したネイティブコードを呼び出されるのではと思うが、
このJavaとC/C++を繋ぐJNIを書くのがマヂ無理
(パッケージ名を含めた関数名をつけた関数をつくったり...)
※ Swiftの実装を呼び出すには結局SwiftとJavaの間にCのコードを挟まないといけない
Write & call Swift code using Java’s JNI
https://stackoverflow.com/questions/27628385/write-call-swift-code-using-java-s-jni
あー、Javaからネイティブコードを呼びたい
けど、JNIは書きたくない
そんなときは JNA(Java Native Access) を使おう
なんかよしなにやってくれるらしい!
JNAに関してはこのあたりを参照されたし
https://qiita.com/everylittle/items/b888cbec643f14de5ea6
Swiftのソースからダイナミックリンクライブラリを生成
兎にも角にも、Javaから呼び出したいメソッドを作ります
import Darwin.C
@_cdecl("hello")
public func hello() {
print("Hello")
fflush(stdout)
}
上のプログラムは標準出力に Hello
と表示する関数の実装
ポイントは 1.呼び出し規約を設定する,2.publicにする です
fflush(stdout)
がないとprintされないらしいので入れておきましょう
Printf from native code appears only at the end of the java app
https://stackoverrun.com/ja/q/3379588
このままではソースのままなのでコンパイルしましょう
上のプログラムからダイナミックリンクライブラリ(MacOSだとlib*.dylib)を生成します
swiftc -emit-library hello.swift
ターミナルで上のコマンドを実行すると lib + ファイル名(拡張子なし) + .dylib
の
ダイナミックリンクライブラリが生成されます
nmコマンドで確認すると hello
関数がグローバルセクションにあってなんか呼べそうな気がしてきますね
cha84rakanal$ nm libhello.dylib
0000000000000e40 T _$S5helloAAyyF
U _$S6Darwin6stdoutSpySo7__sFILEVGvg
U _$SSS21_builtinStringLiteral17utf8CodeUnitCount7isASCIISSBp_BwBi1_tcfC
U _$SSSN
U _$Ss27_allocateUninitializedArrayySayxG_BptBwlFyp_Tg5
U _$Ss5print_9separator10terminatoryypd_S2StF
U _$Ss5print_9separator10terminatoryypd_S2StFfA0_
U _$Ss5print_9separator10terminatoryypd_S2StFfA1_
0000000000000fac s ___swift_reflection_version
U __swift_FORCE_LOAD_$_swiftDarwin
0000000000001060 s __swift_FORCE_LOAD_$_swiftDarwin_$_hello
U _fflush
0000000000000e30 T _hello
U _swift_bridgeObjectRelease
U _swift_bridgeObjectRetain
U dyld_stub_binder
cha84rakanal$
実際にJava側からSwiftで実装した関数を呼び出す
さて実際にProcessing(Java)側から先程作った Hello関数
を呼び出してみます
スケッチのdataディレクトリに先に生成したダイナミックリンクライブラリを入れて、次のスケッチで動かします!
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Platform;
public interface SwiftLib extends Library {
void hello();
}
SwiftLib mylib;
void setup(){
mylib = (SwiftLib) Native.loadLibrary(dataPath("libhello.dylib"), SwiftLib.class);
mylib.hello();
}
void draw(){
}
ポイントは、
- JNA関連のライブラリをインポート
- com.sun.jna.Libraryを継承したインターフェースを定義(中に作成した関数の定義)
-
Native#loadLibrary
でダイナミックリンクライブラリを読み込み、ライブラリ名だけだと、Javaが参照しているダイナミックライブラリのパスをみにいく、絶対パスで入れられると確実
これでHelloと表示されたらOK! あとは何でも好きな処理をSwiftで実装するだけ!
それでは、よいProcessing Life