1

C++ アプリケーションを Java に移植しています。SWIG を使用して、いくつかの Java クラスと JNI ラッパーを生成しました。

C++ には、Animal を拡張して Killable を実装する Lion というクラスがあります。Java には多重継承が存在しないという警告が表示されました。今のところ問題ありません。Lion は私の Java クラスで Animal を拡張するだけです。

SWIG で生成されたクラス:

public class Killable {
  private long swigCPtr;
  protected boolean swigCMemOwn;

  public Killable(long cPtr, boolean cMemoryOwn) {
    swigCMemOwn = cMemoryOwn;
    swigCPtr = cPtr;
  }

  protected static long getCPtr(Killable obj) {
    return (obj == null) ? 0 : obj.swigCPtr;
  }

  protected void finalize() {
    delete();
  }

  public synchronized void delete() {
    if (swigCPtr != 0) {
      if (swigCMemOwn) {
        swigCMemOwn = false;
        cppinterfaceJNI.delete_Killable(swigCPtr);
      }
      swigCPtr = 0;
    }
  }

  public long getKillableId() {
    return cppinterfaceJNI.Killable_getId(swigCPtr, this);
  }

  public void kill() {
    cppinterfaceJNI.Killable_kill(swigCPtr, this);
  }
}


public class Lion extends Animal {
  private long swigCPtr;

  public Lion(long cPtr, boolean cMemoryOwn) {
    super(cppinterfaceJNI.Lion_SWIGUpcast(cPtr), cMemoryOwn);
    swigCPtr = cPtr;
  }

  public static long getCPtr(Lion obj) {
    return (obj == null) ? 0 : obj.swigCPtr;
  }

  ...
}

ここで、Killable の配列が必要だとしましょう。SWIG で生成された Java クラスは元の c++ クラスへのポインターを格納するため、次のようなことができるはずです。

LinkedList<Killable> list = new LinkedList<Killable>();
Killable k = new Killable(Lion.getCPtr(myLionObject), false);
list.add(k);
System.out.println(k.getKillableId()) // Return a long, no crash here but I got a huge number (wrong number)
k.kill(); // Crash

それを行ってもエラーはありませんが、オブジェクト k のメソッドにアクセスしようとすると、ライブラリがクラッシュします。適切な実装が見つからないためにクラッシュが発生したと思いますが、新しいオブジェクトへの有効なポインターを与えるため、理由がわかりません。クラッシュが発生する理由や、ここで使用できる回避策はありますか?

ご協力いただきありがとうございます

4

1 に答える 1

2

これまで見てきたように、SWIG + Javaの組み合わせでは、純粋な仮想クラスが自動的にインターフェイスになるわけではありません

getCPtr()問題は、あなたが示したようにオブジェクトを直接操作することは、次のようなものを書くことに似ているということです。

Lion *l = new lion;
intptr_t ptr = (intptr_t)l;
Killable *k = (Killable*)ptr;

C ++では、未定義の動作をマスクするため、このようなCスタイルのキャストを使用するのは不適切な形式です。意図したとおりに機能する可能性は非常に低く、たとえ機能したとしても、コードベースで未定義の動作が診断されていないため、クラッシュするよりも悪い結果になります。幸いなことに、SWIGには、そのようなキャスト(通常は単に自動変換)をまだ可能にしたいという簡単な回避策があります。

次のヘッダーファイルがあるとします。

#include <iostream>

class Killable {
public:
  virtual ~Killable() {}
  virtual void die() = 0;
};

class Animal {
public:
  virtual ~Animal() {}
  virtual void moo() { std::cout << "The dog says: meow\n"; }
};

class Lion : public Animal, public Killable {
  virtual void die() { std::cout << "Deaded\n"; }
};

これを次のSWIGインターフェイスで正常にラップできます。

%module test

%{
#include "test.h"
%}

%include "test.h"

%extend Animal {
  Killable *toKillable() {
    return dynamic_cast<Killable*>($self);
  }
}

ここで、は、%extend他の方法では自動的に公開されないキャストを処理する別のメンバー関数を追加します。変換が正常である場合、有効なものKillableが返されます。そうでない場合は、null代わりに返されます。

これはJavaで使用できます。

import java.util.LinkedList;

public class run {
  public static void main(String[] argv) {
    System.loadLibrary("test");
    LinkedList<Killable> list = new LinkedList<Killable>();
    Lion l = new Lion();
    l.moo();
    Killable k = l.toKillable();
    list.add(k);
    k.die();
  }
}
于 2013-01-26T12:20:26.933 に答える