1

我正在将 C++ 应用程序移植到 Java。我使用 SWIG 生成了一些 Java 类和 JNI 包装器。

在 c++ 中,我有一个名为 Lion 的类,它扩展了 Animal 并实现了 Killable。我收到一条警告,说 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 回答