使用Swig控制器将数组从C ++传递到Java,向上调用

时间:2017-02-22 14:02:06

标签: java c++ swig

我有一个带有虚方法的C ++类。我使用的是导演,我在Java中对C ++类进行了细分。此类用于接收来自C ++代码的回调。然后将Java类传递给C ++和C ++调用其上的方法(upcalls to Java)。有数组参数(或指针数组,我已尝试过两者)并将它们转换为SWIGTYPE_p_double。

我想有一个双面[]的Java边类型签名,当然还有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");
}

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 -version:

  

SWIG版本3.0.8

     

使用clang ++编译[x86_64-apple-darwin15.2.0]

     

配置选项:+ pcre

     

有关报告错误的信息,请参阅http://www.swig.org   信息

1 个答案:

答案 0 :(得分:1)

我付出了一些努力。您找到的作为起点的类型图是合理的,但我认为不完整。我不认为你可以在没有匹配in / jtype / jstype / jni / javadirectorin / javain的情况下逼真地编写directorin / directorargout类型图,因为你很快就会在生成的代码的各个片段之间产生不匹配的期望。 (导演代码也可以调用常规Java类,也可以调用它)。

此外,我认为使用多参数类型映射将指针和大小参数压缩到Java中的单个参数会更好,因为长度隐式是Java中数组的属性。

所以这里有一个简要的总结,说明我必须做些什么才能让它发挥作用:

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

    1. 从类型映射输入中移除了强制转换为jdouble。充其量它们应该是无操作(double* - > jdouble*),但最糟糕的是它们会隐藏一些不合适的东西。
    2. 将字体映射设置为自己的块。这意味着您可以在同一函数上使用相同的类型映射两次,而不会破坏局部变量名。 (尽管我最后还删除了局部变量)
    3. 使用SWIG的JCALLx宏 - 这显然是C ++代码,但这是我在编写SWIG代码时尝试保留的习惯。
    4. 所以最后您的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"
      

      这足以使您的测试用例正确地使用上述更改来使其覆盖而不是现在过载。