swig c ++ w/ java는 다형성 콜백 함수에서 유형을 잃습니다 [복제
-
21-08-2019 - |
문제
의문: 왜 내 C ++ swigged 객체가 Java 콜백 함수로 전달 될 때 유형을 잃어 버리는가?
설정: 콜백을 수행하기 위해 Swig Java 예제를 가져 와서 콜백에 전달할 객체를 추가했습니다. run(Parent p)
. 콜백은 예상대로 작동하지만 내가 통과 할 때 Child
객체 Java는 유형을 잃어버린 것 같습니다. Parent
해야 할 때 Child
. 이것은 기준입니다 Swig Java 콜백 예제.
시스템 정보: Ubuntu 8.04 w/ swig 1.3.33- Off Chance에서 최신 SWIG는 1.3.39를 테스트 한 차이를 만들었습니다.
출력:
bash$ java -Djava.library.path=. runme Adding and calling a normal C++ callback ---------------------------------------- Callback::run(5Child) Callback::~Callback() Adding and calling a Java callback ------------------------------------ JavaCallback.run(Parent) Callback::run(5Child) Callback::~Callback()
출력에서 볼 수 있듯이 - 개체는 실제로 자식 유형이지만 Java 클래스 이름은 부모입니다.
Java 콜백을 보면 run(Parent p)
내가 Java 클래스를 가져 오는 곳을 볼 수 있으며 Java는이 개체가 유형이라고 생각합니다. Parent
- 이것을 어린이에게 캐스팅하려고하면 던질 것입니다 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 ++ 유형/캐스팅으로 바보가되기를 바라고 ...
어떤 생각도 대단히 감사 할 것입니다!
해결책
주말 동안이 문제를 파헤친 후 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=. runme Adding and calling a normal C++ callback ---------------------------------------- make child child type class Parent Callback::run2(5Child) Callback::~Callback() Adding and calling a Java callback ------------------------------------ JavaCallback.run(Child) p.getName() = 5Child Callback::run2(5Child) Callback::~Callback() java exit
이 작업을 수행하는 더 좋은 방법이있을 수 있지만 SWIG 문서 중 어느 것도 제대로 수행하는 방법에 대한 명확한 예를 제시하지 않았습니다. LibSBML 라이브러리에는 사람들이 문제를 해결하는 다운 캐스팅 유형 맵을 만드는 데 도움이 될 수있는 정말 인상적인 코드가 있었지만 출력이 거의 없기 때문에 매우 복잡한 것으로 판명되었습니다. 어쨌든 이것은 간단하고 쉬웠습니다.
누구든지 쉬운 (인간) 솔루션을 알아낼 수 있다면 그것에 대해 듣는 데 관심이 있습니다.
나는 오늘 블로그 게시물을 만났다 특히 SWIG, C ++, C#의 다운 캐스팅 유형에 대해 이야기하고 있습니다. - 어쨌든 좋은 방향 일 수 있습니다.