2017-02-20 83 views
1

当使用Java,C++,痛饮,并痛饮的董事,我可以通过继承一个C++类C++的Java对象。这很好。传递Java对象到C++使用痛饮......然后回Java

现在,当我传递相同的Java从C++代码对象回Java,痛饮创建一个新 Java对象来包裹C++指针。问题在于新对象与旧对象的类型不同。我继承了Java中的C++类,并且需要返回该Java对象。

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

以下是对SSCE:

下面是检查出来的资源,并返回它的C++代码:

// c_backend.cpp 
#include "c_backend.h" 

#include <stdio.h> 

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

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

//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<PersonImpl> 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); 
     } 
    } 
} 

这里的痛饮接口文件:

//c_backend.i 
%module(directors="1") c_backend 

%{ 
#include "c_backend.h" 
%} 

%feature("director") Person; 
%feature("director") JobPool; 

%include "c_backend.h" 

最后,与基类,然后生成文件的C++头文件,编译这一切:

// c_backend.h 
#ifndef C_BACKEND_H 
#define C_BACKEND_H 

#include <stdio.h> 

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

下面是从用于创建所述痛饮一个代码段新的Java对象替换我原来的Java对象:

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

我怎样才能使这项工作不依赖于保持HashMap里面的Java?

+0

我想我在这里回答基本上相同的问题:http://stackoverflow.com/questions/9817516/swig-java-retaining-class-information-of-the-objects-bouncing-from-c - 这样做帮帮我?如果不是的话,我可以写出一些说明任何差异的东西。 – Flexo

+0

事实上,这是同样的问题,解决方案将适用于我的问题。虽然我不喜欢答案(即使它有效)。我希望Swig直接支持的东西......事实上......我希望Swig在创建director实例时将存储Java对象,并在C++对象传递给Java时检测并检索它。这是可能的,而且很简单。没有散​​列。 但是,唉,目前似乎有必要。 – Jason

+1

[SWIG Java保留从C++弹出的对象的类信息]的重复(http://stackoverflow.com/questions/9817516/swig-java-retaining-class-information-of-the-objects-bouncing-from- c) – Jason

回答

2

你可以做到这一点,withing一点点的工作,你赞成限制(即不保持地图弱引用的)。事实证明,我的工作比我原先预期的要少。我会先通过解决方案进行讨论,然后再讨论一下我第一次尝试这样做的方式,这个方法太难以完成了。

工作方案的高层次的看法是,我们已经增加了三件事情:

  1. 一些C++代码,通过%extend内试图一个动态转换到痛饮导演层次结构的Director*(即一个基本的人)。如果存在的话,它会保存对原始Java类的jobject引用。所以我们可以简单地返回jboject,如果转换失败,则返回NULL。
  2. 一些Java代码,将返回我们的C++代码,或者this如果没有合适的任何结果。然后我们可以注入来自我们的javadirectorin类型映射的调用,以允许从新代理到原始对象的“升级”发生。
  3. 在于传递JNIEnv的对象成自动#1的%extend方法的琐碎类型映射的形式的另一特技因为它不是正常访问那里直接,尽管它可以这样被暴露。

所以,你的接口文件就变成了:

%module(directors="1") c_backend 

%{ 
#include "c_backend.h" 
#include <iostream> 
%} 

%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<Swig::Director*>($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" 

我在消息抛出,只是标准错误,证明它确实干过。

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

如果我不得不猜测为什么SWIG默认不这样做,那几乎可以肯定是因为它会强制使用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<SwigDirector_$1_basetype*>($1); 
    if (!dir) { 
     jclass cls = JCALL1(FindClass, jenv, "java/lang/Long"); 
     jmid ctor = JCALL3(GetMethodID, jenv, cls, "<init>", "J(V)"); 
     $input = JCALL3(NewObject, jenv, cls, ctor, reinterpret_cast<jlong>($1)); 
    } 
    else { 
     $input = dir->swig_get_self(jenv); 
    } 
} 
%include "c_backend.h" 

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

与这虽然问题是jnitype和jtype的tyemaps使他们得到在使用的上下文之间没有区别。所以Person(如构造函数,函数的输入,导演了,导演代码的其他位)任何其他用途都需要更改为使用Long对象而不是long基本类型。即使通过匹配变量名称上的类型映射,它仍然不能避免过度匹配。 (尝试一下,注意long变成c_backendJNI.java中Person的地方)。所以在要求非常明确的类型映射命名方面它会很丑陋,并且仍然超出了我想要的,因此需要对其他类型映射进行更多侵入式更改。

+0

不错!并感谢所有的工作! – Jason

+0

不错,但我不同意你关于禁用RTTI的看法。 RTTI与大量模板元编程代码不能很好地交互,因为在所有实例中都会产生RTTI开销,而没有RTTI,它们将从二进制中消失。这可能很容易达到许多兆无用的RTTI数据。所以禁用它是有正当理由的。 – enobayram

+0

我认为反对rtti的案例常常根据民间文学艺术而不是实际的基准进行夸大。在很多情况下,Ram很便宜,“多MB”仍然是花生,除了嵌入式以外,即使在某些移动设备上。如果您只动态投射一种或两种类型,实际影响可能只是工作集中的一两个额外页面。因此,假设通过慢速链接下载不在关键路径上,我敢打赌,一个基准测试显示了许多其他的开销,这些开销完全超过了它。我不喜欢的是没有证据的假设,而且我看到的代码库似乎正在减少。 – Flexo