swigjava保留了从C++;

swigjava保留了从C++;,java,polymorphism,swig,Java,Polymorphism,Swig,好的,有一个关键字我故意避开标签和标题。这是“安卓”,但那是因为即使这个项目是安卓的,我不认为我的问题与它有任何关系,我也不想吓唬没有安卓经验的人 所以,swig的常见问题是。我在C++类中有一个虚拟方法,通过在类中添加主任功能,使它在java中可以重载,并且它工作。问题在于,该方法接收到一个多态参数,该参数也在java端进行了扩展,在java中的虚拟方法调用期间,对象附带了所有多态信息 提出确切的情况;我正在编写一个C++游戏引擎,我想在java中愉快地使用它。游戏引擎有一个GameObjec

好的,有一个关键字我故意避开标签和标题。这是“安卓”,但那是因为即使这个项目是安卓的,我不认为我的问题与它有任何关系,我也不想吓唬没有安卓经验的人

所以,swig的常见问题是。我在C++类中有一个虚拟方法,通过在类中添加<代码>主任<代码>功能,使它在java中可以重载,并且它工作。问题在于,该方法接收到一个多态参数,该参数也在java端进行了扩展,在java中的虚拟方法调用期间,对象附带了所有多态信息

提出确切的情况;我正在编写一个C++游戏引擎,我想在java中愉快地使用它。游戏引擎有一个
GameObject
类,它注册
CollisionListener
s,当碰撞引擎检测到碰撞事件时,它调用所有注册的
CollisionListener
collistedwith(GameObject&collide)
方法,将它们与碰撞的对象传递给它们

class CollisionListener {
public:
    virtual bool collidedWith(GameObject &){};
    ~CollisionListener(){} // I know this needs to be virtual but let's forget about that now
};
我使用下面的接口文件
Bridge.I

%module(directors="1") Bridge

%feature("director") CollisionListener;
%include "CollisionListener";
%feature("director") GameObject;
%include "GameObject.h"
现在,当我从java中的
CollisionListener
继承并重载
CollistedWith
时,它会被java端的
GameObject
对象调用。例如,如果我从java端继承
GameObject
类并定义一个
Bullet
类,当此Bullet与另一个对象与侦听器碰撞时,在
CollideWith
方法调用中,我收到的只是一个裸
GameObject
,因此
(object instanceof Bullet)
不起作用。毫不奇怪,我深入研究了swig生成的
BridgeJNI.java
,发现:

  public static boolean SwigDirector_CollisionListener_collidedWith(CollisionListener self, long arg0) {
    return self.collidedWith(new GameObject(arg0, false));
  }
因此,在调用java重载之前,它会在指针周围包装一个新对象

所以,主要的问题是当发生碰撞时,如何接收
项目符号
对象

我已经想出了一个方法来轻松实现这一点,但我需要修改自动生成的文件,这是一个坏主意。所以我希望一些swig大师能帮助我将修改注入到swig生成的文件中

我的小黑客是在每个C++侧<代码> GAMObjs< /Cord>对象中保留<代码>作业* * /java代码,并在实际java侧<代码> GAMObjult< /C> >(而不是只包装指针的)中分配实际Java对象的地址。这样,我就可以在C++侧代码< GAMObjult</代码>中定义多态性<代码> GETHORIO/<代码>方法,并在java中愉快地使用结果。有没有办法将必要的代码注入到swig生成的文件中

谢谢


注意:如果你在Android上尝试了控制器,但它们没有工作,那是因为当前的稳定版本不支持它。从swig网站下载最新版本。但我在2012年3月22日写了这封信,这封信很快就没有必要了。析构函数不是虚拟的原因是,最新版本使程序在析构函数中崩溃,而使其非虚拟似乎可以暂时控制它。

我已经为这个问题提出了一个解决方案。不过,这并不是您在问题中建议的解决方案,它在Java端有更多的代码,在JNI/C++端没有额外的代码。(我发现按照你建议的方式做,在所有可能的情况下都很难做到正确)

我将您的类简化为单个头文件:

class GameObject {
};

class CollisionListener {
public:
    virtual bool collidedWith(GameObject &) { return false; }
    virtual ~CollisionListener() {} 
};

inline void makeCall(GameObject& o, CollisionListener& c) {
    c.collidedWith(o);
}
它还添加了
makeCall
,使问题更加明显

我使用的技巧是在创建时在
HashMap
中自动注册
GameObject
的所有Java派生实例。然后,当调度控制器调用时,只需在HashMap中查找它

然后显示模块文件:

%module(directors="1") Test

%{
#include "test.hh"
%}

%pragma(java) jniclasscode=%{
  static {
    try {
        System.loadLibrary("test");
    } catch (UnsatisfiedLinkError e) {
      System.err.println("Native code library failed to load. \n" + e);
      System.exit(1);
    }
  }
%}

/* Pretty standard so far, loading the shared object 
   automatically, enabling directors and giving the module a name. */    

// An import for the hashmap type
%typemap(javaimports) GameObject %{
import java.util.HashMap;
import java.lang.ref.WeakReference;
%}

// Provide a static hashmap, 
// replace the constructor to add to it for derived Java types
%typemap(javabody) GameObject %{
  private static HashMap<Long, WeakReference<$javaclassname>> instances 
                        = new HashMap<Long, WeakReference<$javaclassname>>();

  private long swigCPtr;
  protected boolean swigCMemOwn;

  public $javaclassname(long cPtr, boolean cMemoryOwn) {
    swigCMemOwn = cMemoryOwn;
    swigCPtr = cPtr;
    // If derived add it.
    if (getClass() != $javaclassname.class) {
      instances.put(swigCPtr, new WeakReference<$javaclassname>(this));
    }
  }

  // Just the default one
  public static long getCPtr($javaclassname obj) {
    return (obj == null) ? 0 : obj.swigCPtr;
  }

  // Helper function that looks up given a pointer and 
  // either creates or returns it
  static $javaclassname createOrLookup(long arg) {
    if (instances.containsKey(arg)) {
      return instances.get(arg).get();
    }
    return new $javaclassname(arg,false);
  }
%}

// Remove from the map when we release the C++ memory
%typemap(javadestruct, methodname="delete", 
         methodmodifiers="public synchronized") GameObject {
  if (swigCPtr != 0) {
    // Unregister instance
    instances.remove(swigCPtr);
    if (swigCMemOwn) {
      swigCMemOwn = false;
      $imclassname.delete_GameObject(swigCPtr);
    }
    swigCPtr = 0;
  }
}

// Tell SWIG to use the createOrLookup function in director calls.
%typemap(javadirectorin) GameObject& %{
    $javaclassname.createOrLookup($jniinput)
%}
%feature("director") GameObject;

// Finally enable director for CollisionListener and include the header
%feature("director") CollisionListener;    
%include "test.hh"
其中,
JCollisionListener
是:

public class JCollisionListener extends CollisionListener {
  public boolean collidedWith(GameObject i) {
    System.out.println("In collide");
    if (i instanceof JGameObject) {
       System.out.println("Is J");
    }
    else {
       System.out.println("Not j");
    }
    JGameObject o = (JGameObject)i;
    return false;
  }
}
public class JGameObject extends GameObject {
}
JGameObject
是:

public class JCollisionListener extends CollisionListener {
  public boolean collidedWith(GameObject i) {
    System.out.println("In collide");
    if (i instanceof JGameObject) {
       System.out.println("Is J");
    }
    else {
       System.out.println("Not j");
    }
    JGameObject o = (JGameObject)i;
    return false;
  }
}
public class JGameObject extends GameObject {
}

(如果你想做另一种方法,你可以考虑写一个
directorin
typemap作为参考)。

所以你的问题的简短版本是你希望能够(例如)在Java中派生
GameObject
,并且当派生类型被传递到
collidedWith
的Java实现时,仍然能够在Java中强制转换?如果是这样的话,我敢肯定你的小技巧可以被包装在一个类型图中。没错!我原以为swig会有一种注入代码的方法,但我对swig还不太熟悉。我会检查打字图。我希望明天能写一个答案。哇!这么多的努力和一个伟大的答复。这似乎解决了我的问题,也教会了我很多关于swig的知识。看起来我们有时需要覆盖一半的swig才能得到我们想要的。非常感谢。SWIG的好处在于,它可以让你在想做的时候做这样的事情,但它将所有事情封装起来,这样你只需处理“零碎”部分,所有繁重的工作都会为你完成。软件工程师永无止境的决策:“我应该在哪里投入时间?”。SWIG似乎是一个很好的交易,因为它处理跨语言问题的方法似乎是最普遍的。我的意思是,作为一个外部代码生成器,它不受纯基于库的解决方案(如boost python)的大多数限制。5年后,我重新审视了这一点,并意识到有一个可以说更整洁的解决方案不需要额外存储: