3

重複の可能性:
C++ からバウンスするオブジェクトの SWIG Java Retaining Class 情報

質問: Java コールバック関数に渡されると、C++ のスウィッグ オブジェクトの型が失われるのはなぜですか?

セットアップ: コールバックを行うための Swig Java の例を取り上げ、コールバックに渡すオブジェクトを追加しましたrun(Parent p)。コールバックは期待どおりに機能しますが、オブジェクトを渡すと、Java はその型を失い、そうあるべきときにChildその型を認識しているようです。これは、 Swig Java コールバックの例に基づいています。ParentChild

システム情報: Swig 1.3.33 を搭載した Ubuntu 8.04 - 万が一、最新の Swig が違いを生んだ場合、1.3.39 もテストしましたが、効果はありませんでした。

出力:

bash$ java -Djava.library.path=. ランメ
通常の C++ コールバックの追加と呼び出し
--------------------------------------------
Callback::run(5Child)
コールバック::~コールバック()

Java コールバックの追加と呼び出し
------------------------------------
JavaCallback.run(親)
Callback::run(5Child)
コールバック::~コールバック()

出力でわかるように、オブジェクトは実際には Child 型ですが、その Java クラス名は Parent です。これは間違っています...

Java コールバックrun(Parent p)を見ると、Java クラスを取得している場所がわかります。Java は実際にこのオブジェクトが型であると認識しています。Parentこれを Child にキャストしようとするとClassCastException、期待どおりにスローされます。

コード:

/* File : example.i */
%module(directors="1") example
%{
#include "example.h"
%}

%include "std_string.i"

/* turn on director wrapping Callback */
%feature("director") Callback;

%include "example.h"




/* File : example.h */
#include <string>
#include <cstdio>
#include <iostream>
#include <typeinfo>

class Parent {
public:
    virtual const char* getName() {
        return typeid(*this).name();
    }
};


class Child : virtual public Parent {
};



class Callback {
public:
    virtual ~Callback() { std::cout << "Callback::~Callback()" << std:: endl; }
    virtual void run(Parent& p) { std::cout << "Callback::run(" << p.getName() << ")" << std::endl; }
};


class Caller {
private:
    Callback *_callback;
public:
    Caller(): _callback(0) {}
    ~Caller() { delCallback(); }
    void delCallback() { delete _callback; _callback = 0; }
    void setCallback(Callback *cb) { delCallback(); _callback = cb; }
    void call() {
        Parent *p = new Child();
        if (_callback) 
            _callback->run(*p);
        delete p;
    }
};



/* File: runme.java */
public class runme
{
  static {
    try {
        System.loadLibrary("example");
    } catch (UnsatisfiedLinkError e) {
      System.err.println("Native code library failed to load. See the chapter on Dynamic Linking Problems in the SWIG Java documentation for help.\n" + e);
      System.exit(1);
    }
  }

  public static void main(String[] args)
  {
    System.out.println("Adding and calling a normal C++ callback");
    System.out.println("----------------------------------------");

    Caller              caller = new Caller();
    Callback            callback = new Callback();

    caller.setCallback(callback);
    caller.call();
    caller.delCallback();

    callback = new JavaCallback();

    System.out.println();
    System.out.println("Adding and calling a Java callback");
    System.out.println("------------------------------------");

    caller.setCallback(callback);
    caller.call();
    caller.delCallback();

    // Test that a double delete does not occur as the object has already been deleted from the C++ layer.
    // Note that the garbage collector can also call the delete() method via the finalizer (callback.finalize())
    // at any point after here.
    callback.delete();

    System.out.println();
    System.out.println("java exit");
  }
}

class JavaCallback extends Callback
{
  public JavaCallback()
  {
    super();
  }

  public void run(Parent p)
  {
    System.out.println("JavaCallback.run("+p.getClass().getSimpleName()+")");
    super.run(p);
  }
}




# File: Makefile
TOP        = ../..
SWIG       = $(TOP)/../preinst-swig
CXXSRCS    = example.cxx
TARGET     = example
INTERFACE  = example.i
SWIGOPT    =

all::   java

java::
    $(MAKE) -f $(TOP)/Makefile CXXSRCS='$(CXXSRCS)' SWIG='$(SWIG)' \
    SWIGOPT='$(SWIGOPT)' TARGET='$(TARGET)' INTERFACE='$(INTERFACE)' java_cpp
    javac *.java

clean::
    $(MAKE) -f $(TOP)/Makefile java_clean

check: all

これは Swig のバグかもしれませんが、これが私の C++ 型/キャストの愚かさであることを願っています...

どんな考えでも大歓迎です!

4

1 に答える 1

1

週末にこの問題を掘り下げた後、これは Swig が C++ クラスと Java の間で抱えている「一般的な」問題だと思います。この問題はダウンキャスティングと呼ばれ、ディレクターの一般的な問題です。残念ながら、取締役はこの単純なケースでさえ処理できないようです。以下のように、ディレクターのすべての組み合わせを試しました

%feature("director") Callback;
%feature("director") Parent;
%feature("director") Child;

それはまったく役に立たないように見えましたが、次のハックを行うとうまくいきました:

class Callback {
public:
    virtual ~Callback() { std::cout << "Callback::~Callback()" << std:: endl; }
    virtual void run(Parent& p) {
        std::cout << "Callback::run1(" << p.getName() << ")\n";
    }

    virtual void run(Child& c) {
        std::cout << "Callback::run2(" << c.getName() << ")\n";
    }
};

次に、必要なサブタイプのJavaクラスで、オーバーロードが解決されます。

class JavaCallback extends Callback
{
  public void run(Child p)
  {
    out.p("JavaCallback.run("+p.getClass().getSimpleName()+")");
    out.p("p.getName() = "+p.getName());
    super.run(p);
  }
}

その後、魔法のように出力が機能します

bash$ java -Djava.library.path=. ランメ
通常の C++ コールバックの追加と呼び出し
--------------------------------------------
子作り
子型クラス 親
Callback::run2(5Child)
コールバック::~コールバック()
Java コールバックの追加と呼び出し
------------------------------------
JavaCallback.run(子)
p.getName() = 5Child
Callback::run2(5Child)
コールバック::~コールバック()
java 出口

これを行うためのより良い方法があるはずですが、これを適切に行う方法の明確な例を示した Swig ドキュメントはありません。libsbml ライブラリには、問題を解決するダウンキャスト型マップを作成するのに役立つ可能性のある非常に印象的なコードがいくつかありましたが、出力が少ないため非常に複雑であることが証明されていました... とにかく、これはシンプルで簡単でした。

誰かが簡単な(人間の)解決策を見つけられるなら、私はそれについて聞きたいです.

今日、ブログ投稿に出くわしました。具体的には、SWIG、C++、C# で型をダウンキャストすることについて話しています。とにかく、それは良い方向かもしれません。

于 2009-07-05T21:49:08.550 に答える