我的C程序使用定期调用的回调函数。我希望能够在Java或C#程序中处理回调函数。我应该如何编写.i文件来实现这一目标?
C回调看起来如此:
static void on_incoming_call(pjsua_acc_id acc_id, pjsua_call_id call_id, pjsip_rx_data *rdata)
答案 0 :(得分:24)
如果你有机会通过回调传递一些数据,你可以这样做,但是你需要编写一些JNI粘合剂。我汇总了一个完整的示例,说明如何将C样式回调映射到Java接口。
您需要做的第一件事就是确定一个适合Java端的接口。我假设在C中我们有回调,如:
typedef void (*callback_t)(int arg, void *userdata);
我决定用Java表示:
public interface Callback {
public void handle(int value);
}
(Java端void *userdata
的丢失不是一个真正的问题,因为我们可以将状态存储在Object
中,实现Callback
。{/ p>
然后我编写了以下头文件(它不应该只是一个标题,但它让事情变得简单)来运行包装:
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"
界面有很多部分:
struct
。callback_t
的实现。它接受我们刚刚定义的struct
作为用户数据,然后使用一些标准JNI调用Java接口。Callback
个对象作为真实jobject
直接传递给C实现的一些类型映射。void*
并设置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);
}
}
这是你希望的。
需要注意的一些要点:
set
,它将泄漏全局参考。您需要提供一种方法来取消设置回调,防止多次设置,或者使用弱引用。 JNIEnv
更聪明。%constant
公开C函数作为回调,但这些类型映射将阻止您的包装函数接受此类输入。可能你会想要提供重载来解决这个问题。在this question中有一些更好的建议。
我认为C#的解决方案有点类似,使用不同的类型映射名称和您在C中编写的回调函数的不同实现。
答案 1 :(得分:1)
这很好地允许C ++执行对C#的回调。
在下面测试:
report-uri.io
:Visual Studio 2015 C#
:Intel Parallel Studio 2017 SE C++
:应该可以很好地使用Visual Studio 2015 C ++(任何人都可以验证这个吗?)。C++
:对于生成标准.dll的任何其他Windows C ++编译器应该可以正常工作(任何人都可以验证吗?)。在您的SWIG C++
文件中,请包含此文件.i
:
callback.i
答案 2 :(得分:-3)
C#解决方案: 你必须使用代表。看一下代码示例
public delegate void DoSome(object sender);
public class MyClass{
public event DoSome callbackfunc;
public void DoWork(){
// do work here
if(callbackfunc != null)
callbackfunc(something);
}
}
这也像事件处理机制,但理论上它们在c#
中具有相同的实现Java解决方案: 你必须使用接口,看看这个示例代码
interface Notification{
public void somthingHappend(Object obj);
}
class MyClass{
private Notification iNotifer;
public void setNotificationReciver(Notification in){
this.iNotifier = in;
}
public void doWork(){
//some work to do
if(something heppens){ iNotifier.somthingHappend(something);}
}
}
实际上你可以使用Notification Notification来实现一组回调接收器