java - 使用 Swig Controller 将数组从 C++ 传递到 Java,up 调用

标签 java c++ swig

我有一个带有虚方法的 C++ 类。我正在使用 directors 并且我在 Java 中对 C++ 类进行了子类化。此类用于接收来自 C++ 代码的回调。因此 Java 类随后被传递给 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($2); 
   (jenv)->SetDoubleArrayRegion(jd, 0, $2, (jdouble *)$1); 
   $input = jd; 
} 
%typemap(directorargout) (double *DOUBLE, size_t LENGTH) 
%{(jenv)->GetDoubleArrayRegion($input, 0, $2, (jdouble *)$1); %} 

%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");
}

Java前端.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 类,也可以被它调用)。

此外,我认为在 Java 中使用多参数类型映射将指针和大小参数压缩为单个参数要好得多,因为长度隐含地是 Java 中数组的一个属性。

所以这里是我必须做的事情的快速总结:

  1. 首先,您现有的类型映射不会应用于您的代码,因为它们在参数类型和参数名称上都匹配。 %apply 是执行此操作的巧妙方法,尤其是对于像这样的多参数类型映射。
  2. 其次,为 in 而不仅仅是 directorin 添加了相应的类型映射。
  3. 第三,添加了类型映射以通过代理将 double[] 从 C++ 一直传递到 Java。
  4. 最后,因为类型现在只是 double[] 和一个隐式大小,测试用例需要更新以确保它仍然是覆盖而不是重载。为了更好地衡量,我添加了 @Override

我做了一些风格上的改变,因为它们是更好的代码:

  1. 从 jdouble 的类型映射输入中删除了强制转换。充其量他们应该是一个空操作(double*->jdouble*),但最坏的情况是他们会隐藏一些不愉快的事情。
  2. 将类型图设置在它们自己的一个 block 中。这意味着您可以在同一个函数上使用同一个类型映射两次,而不会导致局部变量名称冲突。 (虽然我最后还是去掉了局部变量)
  3. 使用 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
  $1 = JCALL2(GetDoubleArrayElements, jenv, $input, NULL);
  $2 = 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, $1, 0); 
}
%typemap(directorin,descriptor="[D") (double *DOUBLE, size_t LENGTH) { 
  $input = JCALL1(NewDoubleArray, jenv, $2); 
  JCALL4(SetDoubleArrayRegion, jenv, $input, 0, $2, $1); 
} 
%typemap(directorargout) (double *DOUBLE, size_t LENGTH) {
  (jenv)->GetDoubleArrayRegion($input, 0, $2, $1); 
} 

%feature("director") CallbackHandler;

%apply (double *DOUBLE, size_t LENGTH) { (double *params, size_t size) };

%include "c_backend.h"

这足以使您的测试用例通过上述更改正常工作,使其覆盖而不是现在过载。

关于java - 使用 Swig Controller 将数组从 C++ 传递到 Java,up 调用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42393651/

相关文章:

java - SWIG 生成的代码不处理或抛出异常

java - 可以引用匿名类的实例吗?

java - 具有时间延迟的递归函数调用

java - 如何在已排序的 ArrayList 中查找以子字符串开头的元素?

c++ - C++03 中默认初始化和值初始化的区别?

仅头文件库中的 C++ 内联函数和模板函数

c++ - 融合三角循环进行并行化,计算子索引

python - 如何将 python 对象传递给需要指针类型的函数

java - Android TabLayout setOnTabSelectedListener 不工作

c++ - 如何用 swig 包装 operator+ 函数?