我应该如何编写.i文件来包装Java或C#中的回调

Cla*_*aro 7 c c# java swig

我的C程序使用定期调用的回调函数.我希望能够在Java或C#程序中处理回调函数.我应该如何编写.i文件来实现这一目标?

C回调看起来如此:

static void on_incoming_call(pjsua_acc_id acc_id, pjsua_call_id call_id, pjsip_rx_data *rdata)
Run Code Online (Sandbox Code Playgroud)

Fle*_*exo 24

如果你有机会通过回调传递一些数据,你可以这样做,但你需要编写一些JNI粘合剂.我汇总了一个完整的示例,说明如何将C样式回调映射到Java接口.

您需要做的第一件事就是确定一个适合Java端的接口.我假设在C中我们有回调,如:

typedef void (*callback_t)(int arg, void *userdata);
Run Code Online (Sandbox Code Playgroud)

我决定用Java表示:

public interface Callback {
  public void handle(int value);
}
Run Code Online (Sandbox Code Playgroud)

(的损失void *userdata在Java端是不是因为我们可以存储在该州的一个现实问题Object,实现Callback平凡).

然后,我编写了以下头文件(它不应该只是一个标题,但它保持简单)来执行包装:

typedef void (*callback_t)(int arg, void *data);

static void *data = NULL;
static callback_t active = NULL;

static void set(callback_t cb, void *userdata) {
  active = cb;
  data = userdata;
}

static void dispatch(int val) {
  active(val, data);
}
Run Code Online (Sandbox Code Playgroud)

我能够使用以下界面成功包装此C:

%module test

%{
#include <assert.h>
#include "test.h"

// 1:
struct callback_data {
  JNIEnv *env;
  jobject obj;
};

// 2:
void java_callback(int arg, void *ptr) {
  struct callback_data *data = ptr;
  const jclass callbackInterfaceClass = (*data->env)->FindClass(data->env, "Callback");
  assert(callbackInterfaceClass);
  const jmethodID meth = (*data->env)->GetMethodID(data->env, callbackInterfaceClass, "handle", "(I)V");
  assert(meth);
  (*data->env)->CallVoidMethod(data->env, data->obj, meth, (jint)arg);
}
%}

// 3:
%typemap(jstype) callback_t cb "Callback";
%typemap(jtype) callback_t cb "Callback";
%typemap(jni) callback_t cb "jobject";
%typemap(javain) callback_t cb "$javainput";
// 4:
%typemap(in,numinputs=1) (callback_t cb, void *userdata) {
  struct callback_data *data = malloc(sizeof *data);
  data->env = jenv;
  data->obj = JCALL1(NewGlobalRef, jenv, $input);
  JCALL1(DeleteLocalRef, jenv, $input);
  $1 = java_callback;
  $2 = data;
}

%include "test.h"
Run Code Online (Sandbox Code Playgroud)

界面有很多部分:

  1. A struct用于存储调用Java接口所需的信息.
  2. 一个实现callback_t.它接受struct我们刚刚定义的用户数据,然后使用一些标准JNI调用Java接口.
  3. 一些类型映射导致Callback对象作为实际直接传递给C实现jobject.
  4. 一个类型映射,隐藏void*在Java端并设置callback数据并填充相应的实际函数参数,以使用我们刚刚编写的函数将调用发送回Java.它需要对Java对象进行全局引用,以防止它随后被垃圾回收.

我写了一个小Java类来测试它:

public class run implements Callback {
  public void handle(int val) {
    System.out.println("Java callback - " + val);
  }

  public static void main(String argv[]) {
    run r = new run();

    System.loadLibrary("test");
    test.set(r);
    test.dispatch(666);    
  }
}
Run Code Online (Sandbox Code Playgroud)

这是你希望的工作.

有些要点需要注意:

  1. 如果set多次调用它将泄漏全局引用.您需要提供一种方法来取消设置回调,防止多次设置,或者使用弱引用.
  2. 如果你有多个线程,你需要JNIEnv比我在这里更聪明.
  3. 如果你想混合使用回调的Java和C实现,那么你需要对这个解决方案进行一些扩展.您可以公开C函数作为回调函数,%constant但这些类型映射将阻止您的包装函数接受此类输入.可能你会想要提供重载来解决这个问题.

这个问题上有一些更好的建议.

我相信C#的解决方案会有些类似,使用不同的类型映射名称和您在C中编写的回调函数的不同实现.