使用 Swig 控制器将数组从 C++ 传递到 Java,up 调用
Passing arrays from C++ to Java using Swig directors, the up call
我有一个带有虚方法的 C++ class。我正在使用 directors 并在 Java 中子 classed C++ class。 class 用于接收来自 C++ 代码的回调。所以 Java class 然后被传递给 C++,C++ 调用它的方法(向上调用 Java)。有数组参数(或指针数组,我都试过了),它们被转换为 SWIGTYPE_p_double.
我想要一个 Java double[] 的边类型签名,当然在那个 double[] 参数中有数组的内容(复制内容就可以)。
我该怎么做?
我已经尝试使用从一些电子邮件列表中提取的以下代码:
c_backend.i:
%module(directors="1") c_backend
%{
#include "c_backend.h"
%}
%typemap(directorin, descriptor="[D") (double *DOUBLE, size_t LENGTH) {
jdoubleArray jd = (jenv)->NewDoubleArray();
(jenv)->SetDoubleArrayRegion(jd, 0, , (jdouble *));
$input = jd;
}
%typemap(directorargout) (double *DOUBLE, size_t LENGTH)
%{(jenv)->GetDoubleArrayRegion($input, 0, , (jdouble *)); %}
%feature("director") CallbackHandler;
%include "c_backend.h"
c_backend.h:
#ifndef CALLBACK_HANDLER_H
#define CALLBACK_HANDLER_H
#include <stdio.h>
class CallbackHandler {
public:
virtual ~CallbackHandler() {}
virtual void statusUpdate( double *params, size_t size ) {
printf("in C++ statusUpdate\n");
}
};
class Server {
public:
void doSomething( CallbackHandler * );
};
#endif
c_backend.cpp:
#include "c_backend.h"
#include <stdio.h>
#include <stdlib.h>
void Server::doSomething( CallbackHandler *ch ) {
double *params = (double *)malloc(3*sizeof(double));
params[0] = 1.1;
params[1] = 2.2;
params[2] = 3.3;
printf("In doSomthing\n");
ch->statusUpdate(params,3);
printf("exiting doSomthing\n");
}
JavaFrontend.java:
public class JavaFrontend {
static {
System.loadLibrary("CBackend");
}
public static void main( String[] args ) {
JFCallbackHandler jf = new JFCallbackHandler();
new Server().doSomething(jf);
}
public static class JFCallbackHandler extends CallbackHandler {
public void statusUpdate( double params[], long size ) {
System.out.println("Java got params: "+params);
}
}
}
以及要编译的 Makefile:
JAVA_INCLUDE=-I/Library/Java/JavaVirtualMachines/jdk1.8.0_91.jdk/Contents/Home/include -I/Library/Java/JavaVirtualMachines/jdk1.8.0_91.jdk/Contents/Home/include/darwin
all:
c++ -c c_backend.cpp
swig -java -c++ $(JAVA_INCLUDE) c_backend.i
c++ $(JAVA_INCLUDE) -c c_backend_wrap.cxx
c++ -dynamiclib -o libCBackend.jnilib *.o -framework JavaVM
javac *.java
clean:
rm -rf *.class *.o *_wrap.cxx *_wrap.h Server.java SWIGTYPE*.java c_backend*.java CallbackHandler.java
Swig -版本:
SWIG Version 3.0.8
Compiled with clang++ [x86_64-apple-darwin15.2.0]
Configured options: +pcre
Please see http://www.swig.org for reporting bugs and further
information
我付出了一些努力才完成这项工作。您找到的作为起点的类型图是合理的,但我认为不完整。我不认为你可以在没有匹配的 in/jtype/jstype/jni/javadirectorin/javain 类型映射的情况下实际编写 directorin/directorargout 类型映射,因为你很快就会在生成的代码的各个片段之间产生不匹配的期望。 (director 代码也可以调用正则Java class 也可以被它调用)
此外,我认为使用多参数类型映射将指针和大小参数压缩为 Java 中的单个参数要好得多,因为长度是 Java 中数组的隐式属性].
下面是我必须做的事情的简要总结:
- 首先,您现有的类型映射不会应用于您的代码,因为它们在参数类型和参数名称上都匹配。
%apply
是做到这一点的巧妙方法,特别是对于像这样的多参数类型映射。
- 其次,为 in 而不仅仅是 directorin 添加了相应的类型映射。
- 第三,添加了类型映射以通过代理将
double[]
从 C++ 一直传递到 Java。
- 最后,因为类型现在只是
double[]
和一个隐式大小,测试用例需要更新以确保它仍然是覆盖而不是重载。为了好的措施,我添加了 @Override
.
我做了一些风格上的改变,因为它们是更好的代码:
- 从 jdouble 的类型映射输入中删除了强制转换。充其量他们应该是一个空操作(
double*
->jdouble*
),但最坏的情况是他们会隐藏一些不愉快的事情。
- 将类型映射设置为在它们自己的块内。这意味着您可以在同一个函数上使用同一个类型映射两次,而不会导致局部变量名称冲突。 (虽然我最后还是去掉了局部变量)
- 使用了 SWIG 的 JCALLx 宏 - 这显然是 C++ 代码,但这是我在编写 SWIG 代码时尽量保持的习惯。
所以最后你的 SWIG 界面看起来像这样:
%module(directors="1") c_backend
%{
#include "c_backend.h"
%}
%typemap(jstype) (double *DOUBLE, size_t LENGTH) "double[]"
%typemap(jtype) (double *DOUBLE, size_t LENGTH) "double[]"
%typemap(jni) (double *DOUBLE, size_t LENGTH) "jdoubleArray"
%typemap(javadirectorin) (double *DOUBLE, size_t LENGTH) "$jniinput"
%typemap(javain) (double *DOUBLE, size_t LENGTH) "$javainput"
%typemap(in,numinputs=1) (double *DOUBLE, size_t LENGTH) {
// Note the NULL here if you don't want to be making changes visible
= JCALL2(GetDoubleArrayElements, jenv, $input, NULL);
= JCALL1(GetArrayLength, jenv, $input);
}
%typemap(freearg) (double *DOUBLE, size_t LENGTH) {
// Swap 0 for JNI_ABORT if you don't want to make changes visible
JCALL3(ReleaseDoubleArrayElements, jenv, $input, , 0);
}
%typemap(directorin,descriptor="[D") (double *DOUBLE, size_t LENGTH) {
$input = JCALL1(NewDoubleArray, jenv, );
JCALL4(SetDoubleArrayRegion, jenv, $input, 0, , );
}
%typemap(directorargout) (double *DOUBLE, size_t LENGTH) {
(jenv)->GetDoubleArrayRegion($input, 0, , );
}
%feature("director") CallbackHandler;
%apply (double *DOUBLE, size_t LENGTH) { (double *params, size_t size) };
%include "c_backend.h"
这足以使您的测试用例与上述更改一起正常工作,使其现在覆盖而不是重载。
我有一个带有虚方法的 C++ class。我正在使用 directors 并在 Java 中子 classed C++ class。 class 用于接收来自 C++ 代码的回调。所以 Java class 然后被传递给 C++,C++ 调用它的方法(向上调用 Java)。有数组参数(或指针数组,我都试过了),它们被转换为 SWIGTYPE_p_double.
我想要一个 Java double[] 的边类型签名,当然在那个 double[] 参数中有数组的内容(复制内容就可以)。
我该怎么做?
我已经尝试使用从一些电子邮件列表中提取的以下代码:
c_backend.i:
%module(directors="1") c_backend
%{
#include "c_backend.h"
%}
%typemap(directorin, descriptor="[D") (double *DOUBLE, size_t LENGTH) {
jdoubleArray jd = (jenv)->NewDoubleArray();
(jenv)->SetDoubleArrayRegion(jd, 0, , (jdouble *));
$input = jd;
}
%typemap(directorargout) (double *DOUBLE, size_t LENGTH)
%{(jenv)->GetDoubleArrayRegion($input, 0, , (jdouble *)); %}
%feature("director") CallbackHandler;
%include "c_backend.h"
c_backend.h:
#ifndef CALLBACK_HANDLER_H
#define CALLBACK_HANDLER_H
#include <stdio.h>
class CallbackHandler {
public:
virtual ~CallbackHandler() {}
virtual void statusUpdate( double *params, size_t size ) {
printf("in C++ statusUpdate\n");
}
};
class Server {
public:
void doSomething( CallbackHandler * );
};
#endif
c_backend.cpp:
#include "c_backend.h"
#include <stdio.h>
#include <stdlib.h>
void Server::doSomething( CallbackHandler *ch ) {
double *params = (double *)malloc(3*sizeof(double));
params[0] = 1.1;
params[1] = 2.2;
params[2] = 3.3;
printf("In doSomthing\n");
ch->statusUpdate(params,3);
printf("exiting doSomthing\n");
}
JavaFrontend.java:
public class JavaFrontend {
static {
System.loadLibrary("CBackend");
}
public static void main( String[] args ) {
JFCallbackHandler jf = new JFCallbackHandler();
new Server().doSomething(jf);
}
public static class JFCallbackHandler extends CallbackHandler {
public void statusUpdate( double params[], long size ) {
System.out.println("Java got params: "+params);
}
}
}
以及要编译的 Makefile:
JAVA_INCLUDE=-I/Library/Java/JavaVirtualMachines/jdk1.8.0_91.jdk/Contents/Home/include -I/Library/Java/JavaVirtualMachines/jdk1.8.0_91.jdk/Contents/Home/include/darwin
all:
c++ -c c_backend.cpp
swig -java -c++ $(JAVA_INCLUDE) c_backend.i
c++ $(JAVA_INCLUDE) -c c_backend_wrap.cxx
c++ -dynamiclib -o libCBackend.jnilib *.o -framework JavaVM
javac *.java
clean:
rm -rf *.class *.o *_wrap.cxx *_wrap.h Server.java SWIGTYPE*.java c_backend*.java CallbackHandler.java
Swig -版本:
SWIG Version 3.0.8
Compiled with clang++ [x86_64-apple-darwin15.2.0]
Configured options: +pcre
Please see http://www.swig.org for reporting bugs and further information
我付出了一些努力才完成这项工作。您找到的作为起点的类型图是合理的,但我认为不完整。我不认为你可以在没有匹配的 in/jtype/jstype/jni/javadirectorin/javain 类型映射的情况下实际编写 directorin/directorargout 类型映射,因为你很快就会在生成的代码的各个片段之间产生不匹配的期望。 (director 代码也可以调用正则Java class 也可以被它调用)
此外,我认为使用多参数类型映射将指针和大小参数压缩为 Java 中的单个参数要好得多,因为长度是 Java 中数组的隐式属性].
下面是我必须做的事情的简要总结:
- 首先,您现有的类型映射不会应用于您的代码,因为它们在参数类型和参数名称上都匹配。
%apply
是做到这一点的巧妙方法,特别是对于像这样的多参数类型映射。 - 其次,为 in 而不仅仅是 directorin 添加了相应的类型映射。
- 第三,添加了类型映射以通过代理将
double[]
从 C++ 一直传递到 Java。 - 最后,因为类型现在只是
double[]
和一个隐式大小,测试用例需要更新以确保它仍然是覆盖而不是重载。为了好的措施,我添加了@Override
.
我做了一些风格上的改变,因为它们是更好的代码:
- 从 jdouble 的类型映射输入中删除了强制转换。充其量他们应该是一个空操作(
double*
->jdouble*
),但最坏的情况是他们会隐藏一些不愉快的事情。 - 将类型映射设置为在它们自己的块内。这意味着您可以在同一个函数上使用同一个类型映射两次,而不会导致局部变量名称冲突。 (虽然我最后还是去掉了局部变量)
- 使用了 SWIG 的 JCALLx 宏 - 这显然是 C++ 代码,但这是我在编写 SWIG 代码时尽量保持的习惯。
所以最后你的 SWIG 界面看起来像这样:
%module(directors="1") c_backend
%{
#include "c_backend.h"
%}
%typemap(jstype) (double *DOUBLE, size_t LENGTH) "double[]"
%typemap(jtype) (double *DOUBLE, size_t LENGTH) "double[]"
%typemap(jni) (double *DOUBLE, size_t LENGTH) "jdoubleArray"
%typemap(javadirectorin) (double *DOUBLE, size_t LENGTH) "$jniinput"
%typemap(javain) (double *DOUBLE, size_t LENGTH) "$javainput"
%typemap(in,numinputs=1) (double *DOUBLE, size_t LENGTH) {
// Note the NULL here if you don't want to be making changes visible
= JCALL2(GetDoubleArrayElements, jenv, $input, NULL);
= JCALL1(GetArrayLength, jenv, $input);
}
%typemap(freearg) (double *DOUBLE, size_t LENGTH) {
// Swap 0 for JNI_ABORT if you don't want to make changes visible
JCALL3(ReleaseDoubleArrayElements, jenv, $input, , 0);
}
%typemap(directorin,descriptor="[D") (double *DOUBLE, size_t LENGTH) {
$input = JCALL1(NewDoubleArray, jenv, );
JCALL4(SetDoubleArrayRegion, jenv, $input, 0, , );
}
%typemap(directorargout) (double *DOUBLE, size_t LENGTH) {
(jenv)->GetDoubleArrayRegion($input, 0, , );
}
%feature("director") CallbackHandler;
%apply (double *DOUBLE, size_t LENGTH) { (double *params, size_t size) };
%include "c_backend.h"
这足以使您的测试用例与上述更改一起正常工作,使其现在覆盖而不是重载。