使用Swig将Java对象传递给C ++ …然后返回Java

使用Java,C ++,Swig和Swig的导向器时,我可以将inheritanceC ++类的Java对象传递给C ++。 这很好用。

现在,当我从C ++代码将同一个Java对象传递回Java时,Swig创建了一个新的 Java对象来包装C ++指针。 这个问题是新对象与旧对象的类型不同。 我在Java中inheritance了C ++类,我需要Java对象。

我为什么要这样做? 我有一个Java资源池,C ++代码检查这些资源然后将它们返回到池中。

以下是SSCE:

这是检出资源并返回它的C ++代码:

// c_backend.cpp #include "c_backend.h" #include  void Server::doSomething( JobPool *jp ) { printf("In doSomthing\n"); Person *person = jp->hireSomeone(); person->doSomeWorkForMe(3); jp->returnToJobPool(person); printf("exiting doSomthing\n"); } 

这是覆盖C ++类的Java代码:

 //JavaFrontend.java import java.util.List; import java.util.ArrayList; public class JavaFrontend { static { System.loadLibrary("CBackend"); } public static void main( String[] args ) { JobPool jobPool = new JobPoolImpl(); new Server().doSomething(jobPool); } public static class JobPoolImpl extends JobPool { private List people = new ArrayList(); public Person hireSomeone() { if ( people.size() > 0 ) { Person person = people.get(0); people.remove(person); return person; } else { System.out.println("returning new PersonImpl"); return new PersonImpl(); } } public void returnToJobPool(Person person) { people.add((PersonImpl)person); } } public static class PersonImpl extends Person { public void doSomeWorkForMe(int i) { System.out.println("Java working for me: "+i); } } } 

这是Swig接口文件:

 //c_backend.i %module(directors="1") c_backend %{ #include "c_backend.h" %} %feature("director") Person; %feature("director") JobPool; %include "c_backend.h" 

最后,带有基类的C ++头文件,然后是一个编译它的Makefile:

 // c_backend.h #ifndef C_BACKEND_H #define C_BACKEND_H #include  class Person { public: virtual ~Person() {} virtual void doSomeWorkForMe(int i) { printf("in C++ doSomeWorkForMe %i\n",i); } }; class JobPool { public: virtual ~JobPool() {} virtual Person *hireSomeone() { printf("in C++ hireSomeone\n"); return NULL; } virtual void returnToJobPool(Person *person) { printf("in C++ returnToJobPool\n"); } }; class Server { public: void doSomething( JobPool * ); }; #endif 

Makefile:

 # 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 JobPool.java Person.java 

这是swig代码中的一个片段,它创建了替换原始Java对象的新Java对象:

 public static void SwigDirector_JobPool_returnToJobPool(JobPool jself, long person) { jself.returnToJobPool((person == 0) ? null : new Person(person, false)); } 

如何在不依赖于在Java中维护HashMap情况下完成这项工作?

您可以通过一些工作来实现这一点,同时使用您喜欢的约束(即不维护弱引用的映射)。 事实certificate,事实上我的工作量也比我原先预期的要少。 我将首先讨论解决方案,然后在我第一次尝试这样做的方式上添加一些讨论,这些讨论变得太难以完成。

工作解决方案的高级视图是我们添加了三件事:

  1. 一些C ++代码,通过%extend内部人员,尝试动态转换为Director* (即SWIG导演heirarchy的一个基础)。 这包含对原始Java类的jobject引用(如果存在)。 所以我们可以简单地返回jboject,如果转换失败则返回NULL。
  2. 一些Java代码将返回我们的C ++代码的结果,或者如果不合适则返回this代码。 然后,我们可以从我们的javadirectorin类型映射中注入调用,以允许从新代理到原始对象的“升级”。
  3. 另一个以简单的类型映射forms的技巧,它将JNIEnv对象自动传递到#1的%extend方法,因为它通常不能直接在那里访问,即使它可以像这样暴露。

所以你的接口文件变成:

 %module(directors="1") c_backend %{ #include "c_backend.h" #include  %} %feature("director") Person; %feature("director") JobPool; // Call our extra Java code to figure out if this was really a Java object to begin with %typemap(javadirectorin) Person * "$jniinput == 0 ? null : new $*javaclassname($jniinput, false).swigFindRealImpl()" // Pass jenv into our %extend code %typemap(in,numinputs=0) JNIEnv *jenv "$1 = jenv;" %extend Person { // return the underlying Java object if this is a Director, or null otherwise jobject swigOriginalObject(JNIEnv *jenv) { Swig::Director *dir = dynamic_cast($self); std::cerr << "Dynamic_cast: " << dir << "\n"; if (dir) { return dir->swig_get_self(jenv); } return NULL; } } %typemap(javacode) Person %{ // check if the C++ code finds an object and just return ourselves if it doesn't public Person swigFindRealImpl() { Object o = swigOriginalObject(); return o != null ? ($javaclassname)o : this; } %} %include "c_backend.h" 

我向stderr发了一条消息,certificate它确实有效。

在实际代码中,您可能希望添加一个javaout类型映射,它反映了javadirectorin类型映射的function。 你也许可以在宏中整齐地装扮它,因为编写所有代码是为了避免假设一个固定的类型名称。

如果我不得不猜测为什么SWIG在默认情况下不会做那样的事情,那几乎可以肯定,因为那会强制使用RTTI,但过去将-fno-rtti传递给你的编译器“为了性能”已经-fno-rtti了,所以许多代码库试图避免假设可以依赖动态强制转换。


如果您关心的只是解决方案,请立即停止阅读。 但是这里以参考的方式包含了我最初放弃的原始方法。 它开头是这样的:

 //c_backend.i %module(directors="1") c_backend %{ #include "c_backend.h" %} %feature("director") Person; %feature("director") JobPool; %typemap(jtype) Person * "Object" %typemap(jnitype) Person * "jobject" %typemap(javadirectorin) Person * "$jniinput instanceof $*javaclassname ? ($*javaclassname)$jniinput : new $*javaclassname((Long)$jniinput), false)" %typemap(directorin,descriptor="L/java/lang/Object;") Person * { SwigDirector_$1_basetype *dir = dynamic_cast($1); if (!dir) { jclass cls = JCALL1(FindClass, jenv, "java/lang/Long"); jmid ctor = JCALL3(GetMethodID, jenv, cls, "", "J(V)"); $input = JCALL3(NewObject, jenv, cls, ctor, reinterpret_cast($1)); } else { $input = dir->swig_get_self(jenv); } } %include "c_backend.h" 

这改变了Person类型,从包装器代码中一直返回Object / jobject 。 我的计划是它将是Personjava.lang.Long的实例,我们将根据比较的实例动态地决定构建什么。

这个问题虽然是jnitype和jtype tyemaps没有区分它们被使用的上下文。所以Person任何其他用法(例如构造函数,函数输入,导出器,导演代码的其他位)都需要改变使用Long对象而不是long基本类型。 即使通过匹配变量名称上的类型映射,它仍然无法避免过度匹配。 (尝试一下,注意c_backendJNI.java里面的人长的地方)。 因此,在要求非常明确地命名类型映射方面并且仍然具有超出我想要的范围并因此需要对其他类型映射进行更多侵入式更改时,这将是丑陋的。