SWIG (v1.3.29) сгенерировал класс C ++ to Java Vector, который не работает должным образом - PullRequest
11 голосов
/ 16 ноября 2011

У меня есть некоторый нативный код C ++, который я конвертирую в Java с помощью SWIG, чтобы мое приложение Java могло его использовать.В частности, есть некоторые функции, которые возвращают std :: vector.Вот фрагмент моего файла интерфейса:

%include "std_vector.i"
namespace std {
  %template(Vector) vector<double>;
  %template(Matrix) vector<vector<double> >;
}

%include "std_string.i"

std_string.i и std_vector.i были включены в мою сборку SWIG, которую я использую.Моим первым сюрпризом было то, что вывод Java содержал «собственную» версию класса Vector от SWIG (вместо использования java.util.Vector).Моя настоящая проблема в том, что векторы, которые возвращаются из этих функций, похоже, не работают.Например, я не могу получить их содержимое, используя get() (иногда сбой программы) или функцию size(), возвращающую отрицательные значения.Я знаю, что Vector содержат данные, потому что я закодировал 'String' версии тех же функций, которые просто перебирают Vector s (обратно в собственный код C ++) и возвращают содержимое в виде значения String, разделенного запятой.Несмотря на то, что это верный обходной путь, в конечном итоге я хотел бы, чтобы это работало должным образом, чтобы я мог получать и манипулировать Vectors.Любая помощь / советы будут высоко ценится.

Ответы [ 2 ]

15 голосов
/ 19 ноября 2011

Подходящим базовым типом для переноса std::vector в Java является java.util.AbstractList.Использование java.util.Vector в качестве базы было бы странным, потому что в итоге вы получили бы два набора хранилищ, один в std::vector и один в java.util.Vector.

. Причина, по которой SWIG не подходитэто для вас, хотя, потому что вы не можете иметь AbstractList<double> в Java , это должно быть AbstractList<Double> (Double наследуется от Object, тогда как double является примитивным типом).

Сказав все это, я собрал небольшой пример, который прекрасно оборачивает std::vector<double> и std::vector<std::vector<double> > в Java.Он не завершен, но поддерживает стиль итераций «для каждого» в Java и set() / get() для элементов.Этого должно быть достаточно, чтобы показать, как реализовывать другие вещи так, как / когда вы этого хотите.

Я буду обсуждать файл интерфейса в отдельных разделах, но в основном все будет последовательным и полным.

Начиная с num.i, который определяет наш модуль num:

%module num

%{
#include <vector>
#include <stdexcept>

std::vector<double> testVec() {
  return std::vector<double>(10,1.0);
}

std::vector<std::vector<double> > testMat() {
  return std::vector<std::vector<double> >(10, testVec());
}
%}

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

У нас есть #include s для сгенерированного num_wrap.cxx и две реализации функций для тестирования (они могут бытьв отдельном файле я просто поместил их здесь из-за лени / удобства).

Там также есть хитрость с %pragma(java) jniclasscode=, которую я люблю использовать в Java SWIG-интерфейсах, чтобы общий объект / DLL былзагружается прозрачно для пользователя интерфейса.

Далее в файле интерфейса находятся части std::vector, которые мы хотим обернуть.Я не использую std_vector.i, потому что нам нужно внести несколько изменений:

namespace std {

    template<class T> class vector {
      public:
        typedef size_t size_type;
        typedef T value_type;
        typedef const value_type& const_reference;
        %rename(size_impl) size;
        vector();
        vector(size_type n);
        size_type size() const;
        size_type capacity() const;
        void reserve(size_type n);
        %rename(isEmpty) empty;
        bool empty() const;
        void clear();
        void push_back(const value_type& x);
        %extend {
            const_reference get_impl(int i) throw (std::out_of_range) {
                // at will throw if needed, swig will handle
                return self->at(i);
            }
            void set_impl(int i, const value_type& val) throw (std::out_of_range) {
                // at can throw
                self->at(i) = val;
            }
        }
    };
}

Основное изменение здесь %rename(size_impl) size;, которое говорит SWIG выставлять size() из std::vector как size_impl вместо.Мы должны сделать это, потому что Java ожидает, что size вернет int, тогда как версия std::vector вернет size_type, что, скорее всего, не будет int.

Далее вфайл интерфейса, который мы сообщаем ему, какой базовый класс и интерфейсы мы хотим реализовать, а также написание дополнительного Java-кода для приведения вещей между функциями несовместимых типов:

%typemap(javabase) std::vector<double> "java.util.AbstractList<Double>"
%typemap(javainterface) std::vector<double> "java.util.RandomAccess"
%typemap(javacode) std::vector<double> %{
  public Double get(int idx) {
    return get_impl(idx);
  }
  public int size() {
    return (int)size_impl();
  }
  public Double set(int idx, Double d) {
    Double old = get_impl(idx);
    set_impl(idx, d.doubleValue());
    return old;
  }

%}

%typemap(javabase) std::vector<std::vector<double> > "java.util.AbstractList<Vector>"
%typemap(javainterface) std::vector<std::vector<double> > "java.util.RandomAccess"
%typemap(javacode) std::vector<std::vector<double> > %{
  public Vector get(int idx) {
    return get_impl(idx);
  }
  public int size() {
    return (int)size_impl();
  }
  public Vector set(int idx, Vector v) {
    Vector old = get_impl(idx);
    set_impl(idx, v);
    return old;
  }

%}

Это устанавливает базовый класс java.util.AbstractList<Double>для std::vector<double> и java.util.AbstractList<Vector> для std::vector<std::vector<double> > (Vector - это то, что мы будем называть std::vector<double> на стороне интерфейса Java).

Мы также предоставляем реализацию get иset на стороне Java, которая может обрабатывать преобразование double в Double и обратно.

Наконец, в интерфейсе мы добавляем:

namespace std {
  %template(Vector) std::vector<double>;
  %template(Matrix) std::vector<vector<double> >;
}

std::vector<double> testVec();
std::vector<std::vector<double> > testMat();

Это говорит SWIG о необходимостидо std::vector<double> (с указанным типом) как Vector и аналогично для std::vector<vector<double> > как Matrix.Мы также просим SWIG предоставить две наши тестовые функции.

Далее test.java, простой main в Java для небольшого упражнения нашего кода:

import java.util.AbstractList;

public class test {
  public static void main(String[] argv) {
    Vector v = num.testVec();
    AbstractList<Double> l = v;
    for (Double d: l) {
      System.out.println(d);
    }
    Matrix m = num.testMat();
    m.get(5).set(5, new Double(5.0));
    for (Vector col: m) {
      for (Double d: col) {
        System.out.print(d + " ");
      }
      System.out.println();
    }
  }
}

Для сборки изапустите это, мы делаем:

swig -java -c++ num.i
g++ -Wall -Wextra num_wrap.cxx -shared -I/usr/lib/jvm/java-6-sun/include -I/usr/lib/jvm/java-6-sun/include/linux/ -o libnum.so

javac test.java && LD_LIBRARY_PATH=. java test

Я проверил это на g ++ версии 4.4 и SWIG 1.3.40 на Linux / x86.

Полная версия num.i может быть найдена здесь , но всегда можно восстановить из этого ответа, вставив каждую часть вместе в один файл.

Вещи, которые я не реализовал из AbstractList:

  1. add() - может быть реализовано через push_back(), std_vector.i даже пытается реализовать что-то совместимое по умолчанию, но это не работает с проблемой Double vs double или не соответствует типу возврата, указанному в AbstractList(Не забывайте увеличивать modCount)
  2. remove() - не очень хорошо для std::vector с точки зрения сложности времени, но не невозможно реализовать также (аналогично с modCount)
  3. Рекомендуется конструктор, который использует другой Collection, но здесь он не реализован.Может быть реализовано в одном месте set() и get(), но потребуется $javaclassname для правильного именования сгенерированного конструктора.
  4. Возможно, вы захотите использовать что-то вроде this дляубедитесь, что преобразование size_type -> int в size() является нормальным.
4 голосов
/ 07 декабря 2011

Я человек, который предложил награду за этот вопрос, потому что у меня была та же проблема. Мне немного неловко сообщать, что я наконец нашел реальное решение - и это в руководстве SWIG! Исправление заключается в использовании флага -fno-strict-aliasing для g++ при компиляции сгенерированного кода - все просто. Ненавижу признавать, что Гуглингу потребовалось много времени, чтобы наконец это выяснить.

Проблема заключается в том, что последние версии g++ проводят некоторые агрессивные оптимизации, которые делают предположения о псевдонимах указателей, которые не выполняются для кода, генерируемого SWIG для std_vector (и в других случаях.) g++ 4.1 не Это не так, но 4.4.5 определенно делает. Предположения совершенно верны и допускаются действующим стандартом ISO, хотя я не уверен, насколько они известны. По сути, два указателя разных типов (за некоторыми исключениями) могут никогда указывать на один и тот же адрес. Код, который генерирует SWIG для преобразования между указателем на объект и jlong, не соответствует этому правилу.

...