ページ

2014年12月29日月曜日

JNAは楽チン♪

Javaのプログラムで部分的にC/C++で実装したいということはよくあるかと思います。でもJNIって何か面倒なイメージが・・・。
(慣れてしまえば、って感じなのかもしれませんが。)

そこで便利なのがJNA。JNIで実装しなければいけない面倒なコードをだいぶ省略できるようです。
ただ、超簡単なサンプルはよく見かけるのですが、ほんのちょっとインタフェースを複雑にしたりすると意外とサンプルって見かけないものですよね。(検索の仕方が下手なのかもしれませんが・・・
なものでちょこっとC/C++側とJava側のサンプルを作ってみました。

コメント等はそのうち気が向いたら補足するとして、とりあえずコードだけ載せておきます。
構造体やコールバック関数が使えればだいたいのことはできるんじゃないかな?というレベルのものです。日本語扱うときだと環境によっては注意が必要でしょうね。

ちなみに私が使ったJNAのバージョンは4.1.0です。コードを見ていただくと分かりますがWindows上で試してました。

なお、あくまでサンプルコードなので当方では一切の責任を持ちません。皆様の責任の範囲でご参照ください。

--------------------------------------------------
<C/C++側>

#include
#include

struct SampleContext {
  int member;
};
typedef SampleContext* SampleHandle;

struct SampleStruct {
  char member[1024];
};

extern "C" {

__declspec(dllexport) SampleHandle function_open(SampleStruct *arg)
{
  SampleContext *context = (SampleHandle)new SampleContext;
  context->member = atoi(arg->member);
  return (SampleHandle)context;
}

__declspec(dllexport) void function_run(SampleHandle arg, void(*callback)(SampleHandle))
{
  SampleContext *context = (SampleContext *)arg;
  std::cout << "   start: " << context->member << std::endl;
  (*callback)(arg);
  std::cout << "     end: " << context->member << std::endl;
}

__declspec(dllexport) int function_get(SampleHandle arg)
{
  SampleContext *context = (SampleContext *)arg;
  return context->member;
}

__declspec(dllexport) void function_close(SampleHandle arg)
{
  SampleContext *context = (SampleContext *)arg;
  delete context;
}

}
--------------------------------------------------
<Java側>

package sample_jna;

import java.util.Arrays;
import java.util.List;

import com.sun.jna.Callback;
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.PointerType;
import com.sun.jna.Structure;

public class SampleJna {
  public static class SampleStruct extends Structure {
    public byte [] member = "12345".getBytes();
      @Override
      protected List getFieldOrder() {
          return Arrays.asList ("member");
      }
      public SampleStruct() {
        super();
      }
  }

  public static class SampleHandle extends PointerType {
    public SampleHandle() {
      super();
    }
    public SampleHandle(Pointer address) {
      super(address);
    }
  }

  public interface SampleCallback extends Callback {
    public void invoke(SampleHandle arg);
  }

  public interface SampleLibrary extends Library {
    SampleLibrary INSTANCE = (SampleLibrary)Native.loadLibrary("sample_library", SampleLibrary.class);
    SampleHandle function_open(SampleStruct arg);
    void function_run(SampleHandle arg, SampleCallback callback);
    int function_get(SampleHandle arg);
    void function_close(SampleHandle arg);
  }

  public static class UserCallback implements SampleCallback {
    public void invoke(SampleHandle arg) {
      String message = "callback: " + SampleLibrary.INSTANCE.function_get(arg);
      System.out.println(message);
    }
  }

  public static void main(String[] args) {
    SampleStruct struct = new SampleStruct();
    UserCallback callback = new UserCallback();

    SampleHandle handle = SampleLibrary.INSTANCE.function_open(struct);
    SampleLibrary.INSTANCE.function_run(handle, callback);
    SampleLibrary.INSTANCE.function_close(handle);
  }
}
--------------------------------------------------

実行結果はこんな感じ。

   start: 12345
callback: 12345
     end: 12345

ちゃんとコールバック関数内でもコンテキスト内部の情報が取れてますね。