当前位置: 首页 > 面试题库 >

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

荆钱明
2023-03-14
问题内容

当使用Java,C ,Swig和Swig的导演时,我可以将继承C 类的Java对象传递给C ++。这很好。

现在,当我将相同的Java对象从C 代码传递回Java时,Swig创建了一个 新的 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");
}

这是覆盖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<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);
      }
  }
}

这是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 <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:

# 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));
}

如何在不依靠维护HashMap内部Java的情况下完成这项工作?


问题答案:

您可以通过一些工作克服您喜欢的约束(即不维护弱引用的映射)来执行此操作。事实证明,这比我最初预期的要少。我将首先讨论该解决方案,然后再对我第一次尝试执行此操作的方式进行一些讨论,以致于难以完成。

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

  1. 通过%extend内部人员尝试动态强制转换的某些C ++代码Director*(即,SWIG总监层次结构的一个基础)。如果存在,它将保存对原始Java类的jobject引用。因此,我们可以简单地返回该jboject,如果强制转换失败,则返回NULL。
  2. 一些Java代码将返回我们的C ++代码的结果,或者this如果不合适的话,将返回它们。然后,我们可以从witihin的javadirectorin类型映射中注入调用,以允许从新代理“升级”到原始对象。
  3. 琐碎的类型映射形式的另一个技巧是%extend自动将JNIEnv对象传递到#1方法中,因为通常无法直接在该方法中访问它,即使它可能会这样暴露。

因此,您的接口文件将变为:

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

我向stderr发出了一封邮件,以证明它确实有效。

在实际代码中,您可能想要添加一个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。我的计划是,它要么是一个实例Person或者java.lang.Long,我们会动态地决定基于instanceof的比较构造什么样的。

但是,这样做的问题是jnitype和jtype
tyemaps在使用它们的上下文之间没有区别。因此,任何其他用法Person(例如,构造函数,函数输入,director
out,director代码的其他位)都需要更改为使用Long对象而不是long原始类型。即使通过在变量名上匹配类型映射,它仍然不能避免过度匹配。(尝试一下,并注意c_backendJNI.java中长成为Person的地方)。因此,在要求非常明确地命名类型映射方面,这将是很丑陋的,但仍然与我想要的内容不匹配,因此需要对其他类型映射进行更侵入性的更改。



 类似资料:
  • 问题内容: 我在C 中有一个方法,该方法将双精度数组作为参数。我从Java调用此方法,需要传递一个双精度数组。C 例程读取和修改数组的值,而我需要Java中的那些更新后的值。我该怎么做呢? 例如,使用C ++例程: 和Java代码: 我猜不能像上面的调用那样对myMethod进行调用…还是可以吗?而在Swig中进行这项工作所需的是什么。如果我无法进行上述调用,如何将我的值获取到C ++代码? 问题

  • 问题内容: 因此,我试图从根本上建立一个用户选择ID的网页,然后该网页将ID信息发送给python,在python中,python使用该ID来查询数据库,然后将结果返回给该网页进行显示。 我不太确定该怎么做。我知道如何使用ajax调用来调用python生成的数据,但是我不确定如何将初始id信息传达给django应用。是否可以说,查询../app/id(IE / app / 8)之类的网址,然后使用

  • 问题内容: 我在php中有对象,每个对象代表一个“项目”以及与之相关的所有信息。 当用户浏览页面时,这些对象应传递给javascript。理想情况下,镜像相同的结构,因此我可以使用Raphael在我的网站上将每个项目及其信息显示为单独的形状。 但是,如何将对象从php转换为javascript? 问题答案: 您可以将PHP对象转换为数组,然后使用JSON函数对其进行编码。之后,从JavaScrip

  • 问题内容: 我正在尝试使用SWIG为python包装一个C ++库。该库通过将 某些类型的回调函数 传递给类方法来频繁使用回调函数。 现在,在包装代码之后,我想从python创建回调逻辑。这可能吗?这是我正在尝试发现的实验..目前不起作用。 头文件和swig文件如下: paska.h: paska.i: 最后,我在python中测试.. 最后一行抛出“ TypeError:方法’handleri_

  • 我一直在按照创建 POSIX 线程的说明进行操作,并密切关注他们的示例 Pthread 创建和终止。 根据他们的示例,他们将一个整数值传递给,这将导致类似于以下内容的调用: 我要运行的代码的草图是: 这是在一个作业的上下文中,老师希望我们在Linux中使用gcc,所以我使用Windows子系统来Linux这些编译参数: 运行上述程序将输出“线程值:42”。 我的 C 指针体验已经过去了几年,但只是

  • 我正在使用基于WebRTC的C代码库为Android开发一个实时通信应用程序(视频和音频)。我使用SWIG生成一个JNI桥,以从Java访问本机代码。调用的行为是通过在应用程序层中定义并在结构中传递给库代码的许多回调函数来确定的。传递这些回调的函数如下所示: