当前位置: 首页 > 面试题库 >

我应该如何编写.i文件以用Java或C#包装回调

曾绯辞
2023-03-14
问题内容

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

C回调看起来像这样:

static void on_incoming_call(pjsua_acc_id acc_id, pjsua_call_id call_id, pjsip_rx_data *rdata)

问题答案:

如果您有机会通过回调传递一些数据,则可以执行此操作,但是您需要编写一些JNI胶。我整理了一个完整的示例,说明如何将C样式的回调映射到Java接口。

您需要做的第一件事是确定在Java方面合适的接口。我假设在C中我们有如下回调:

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

我决定用Java将其表示为:

public interface Callback {
  public void handle(int value);
}

void *userdata因为我们可以将状态存储在琐碎Object实现的Java中,所以Java方面的损失不是真正的问题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);
}

我能够使用以下接口成功包装此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"

该接口包含很多部分:

  1. 一个struct存储要对Java接口的调用所需的信息。
  2. 的实现callback_t。它接受struct我们刚刚定义的用户数据,然后使用一些标准的JNI将调用调度到J​​ava接口。
  3. 某些类型映射会导致Callback对象作为real直接传递给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);    
  }
}

如您所愿。

需要注意的几点:

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

我相信C#的解决方案会有些相似,不同的Typemap名称和用C编写的回调函数的不同实现。



 类似资料:
  • 问题内容: 当我尝试在/ decrement中写一个后缀/前缀,然后在/ decrement中写一个后缀/前缀时,出现以下错误: 操作++ /-的无效参数 。 但是,根据JLS: 和 所以写: 应该有可能…有什么想法吗? 问题答案: 请注意,原始语法缺少任何语义。这只是语法,并不是每个语法上有效的程序通常都是有效的。例如,语法通常没有涵盖使用前必须声明变量的要求(可以,但是很麻烦)。 Postfi

  • 问题内容: 我有一些当前代码,问题是它创建了一个1252代码页文件,我想强制它创建一个UTF-8文件 任何人都可以通过此代码帮助我,因为我说它当前可以工作…但是我需要强制保存utf ..我可以传递参数或其他东西吗? 这就是我所拥有的,任何帮助我都感激不尽 问题答案: 而不是使用,创建一个。然后,你可以将其包装在中,以允许你在构造函数中传递编码。然后,你可以将数据写入语句中:

  • 问题内容: 鉴于我有一个类似以下的表格: 日程 员工表 如果我必须计算每个员工类型和每个班次的每个部门的总成本,该如何在sql中执行此操作? 假设如果包括午夜在内,我的工资将乘以25%? 我有以下示例输出,但不知道如何执行此操作?: 到目前为止,这是我的sql语句:但是,如何使它起作用?编辑1: 编辑2: 我收到以下错误,但似乎无法找出原因? 您的SQL语法有误;检查与您的MariaDB服务器版本

  • 问题内容: 函数的返回类型应该是什么?(与大多数其他语言一样,例如在此处阅读) 我考虑过一些配对类型,但是Java中不存在。人们经常说这是因为专门的Pair-class比一般的Pair- class好(请参阅此问题)。但是,这在一般功能中是不可能的。 问题答案: 由于您似乎决定不理会具有Java多年经验的人,因此这里的代码与python中的zip函数相同。 版画

  • 问题内容: 所以,这是表格- 在这里,表格只会映射其他两个表格。 我想要显示数据的方式是- 在这里,“厨房”,“洗手间”,“屋顶”,“花园”列的值是发生的事件总数。“百分比”列将显示总计数的百分比。 如何在Oracle SQL中做到这一点? 问题答案: 您需要使用标准的 PIVOT 查询。 根据您的 Oracle数据库版本 ,您可以通过以下两种方式进行操作: 将 PIVOT 用于 11g 及更高

  • 问题内容: 好的,我在问这个问题之前就考虑过,因为起初听起来确实像是一个古老的问题,可以归结为“是否有可能在存档中写入内容”。我是Mac OS X的新手,但我已经阅读过应用程序只是一个带有扩展名和特定文件结构的文件夹。因此,我想知道是否可以在此文件夹中写入内容,以及如何写入。 我问的原因是因为在我的Java程序中,在Windows上,它会读取程序目录中的某些文件(“程序文件”的原理),但在OSX应