Есть ли способ использовать pythonappend с новой встроенной функцией SWIG? - PullRequest
77 голосов
/ 14 февраля 2012

У меня есть небольшой проект, который прекрасно работает с SWIG. В частности, некоторые из моих функций возвращают std::vector s, которые переводятся в кортежи в Python. Теперь я делаю много чисел, поэтому я просто хочу, чтобы SWIG конвертировал их в пустые массивы после того, как они возвращаются из кода c ++. Для этого я использую что-то вроде следующего в SWIG.

%feature("pythonappend") My::Cool::Namespace::Data() const %{ if isinstance(val, tuple) : val = numpy.array(val) %}

(На самом деле есть несколько функций с именем Data, некоторые из которых возвращают значения с плавающей точкой, поэтому я проверяю, что val на самом деле является кортежем.) Это прекрасно работает.

Но я также хотел бы использовать флаг -builtin, который теперь доступен. Вызовы этих функций данных редки и в основном интерактивны, поэтому их медлительность не является проблемой, но есть и другие медленные циклы, которые значительно ускоряются с помощью встроенной опции.

Проблема в том, что когда я использую этот флаг, функция pythonappend автоматически игнорируется. Теперь Data просто возвращает кортеж снова. Есть ли какой-нибудь способ, которым я все еще мог бы вернуть массивы? Я пытался использовать наборы, но это превратилось в гигантский беспорядок.

Edit:

Бореалид очень хорошо ответил на вопрос. Просто для полноты, я включаю пару связанных, но немного разных типов карт, которые мне нужны, потому что я возвращаюсь по константной ссылке и использую векторы векторов (не начинайте!). Они достаточно разные, и я бы не хотел, чтобы кто-то еще спотыкался, пытаясь выяснить незначительные различия.

%typemap(out) std::vector<int>& {
  npy_intp result_size = $1->size();
  npy_intp dims[1] = { result_size };
  PyArrayObject* npy_arr = (PyArrayObject*)PyArray_SimpleNew(1, dims, NPY_INT);
  int* dat = (int*) PyArray_DATA(npy_arr);
  for (size_t i = 0; i < result_size; ++i) { dat[i] = (*$1)[i]; }
  $result = PyArray_Return(npy_arr);
}
%typemap(out) std::vector<std::vector<int> >& {
  npy_intp result_size = $1->size();
  npy_intp result_size2 = (result_size>0 ? (*$1)[0].size() : 0);
  npy_intp dims[2] = { result_size, result_size2 };
  PyArrayObject* npy_arr = (PyArrayObject*)PyArray_SimpleNew(2, dims, NPY_INT);
  int* dat = (int*) PyArray_DATA(npy_arr);
  for (size_t i = 0; i < result_size; ++i) { for (size_t j = 0; j < result_size2; ++j) { dat[i*result_size2+j] = (*$1)[i][j]; } }
  $result = PyArray_Return(npy_arr);
}

Редактировать 2:

Хотя не совсем то, что я искал, подобные проблемы также могут быть решены с использованием подхода @ MONK ( объяснено здесь ).

1 Ответ

8 голосов
/ 10 января 2013

Я согласен с вами, что использование typemap немного запутанно, но это верный способ выполнить эту задачу.Вы также правы, что в документации SWIG прямо не говорится, что %pythonappend несовместим с -builtin, но это явно подразумевается: %pythonappend добавляет к прокси-классу Python и прокси-классу Pythonне существует вообще вместе с флагом -builtin.

Раньше вы выполняли SWIG-преобразование объектов C ++ std::vector в кортежи Python, а затем передавали эти кортежи обратно в numpy - где они были снова преобразованы.

Что вы действительно хотите сделать, так это конвертировать их один раз, на уровне C.

Вот некоторый код, который превратит все std::vector<int> объекты в целое число NumPy.массивы:

%{
#include "numpy/arrayobject.h"
%}

%init %{
    import_array();
%}

%typemap(out) std::vector<int> {
    npy_intp result_size = $1.size();

    npy_intp dims[1] = { result_size };

    PyArrayObject* npy_arr = (PyArrayObject*)PyArray_SimpleNew(1, dims, NPY_INT);
    int* dat = (int*) PyArray_DATA(npy_arr);

    for (size_t i = 0; i < result_size; ++i) {
        dat[i] = $1[i];
    }

    $result = PyArray_Return(npy_arr);
}

При этом используются функции на уровне C для создания и возврата массива.По порядку:

  • Гарантирует, что файл NumPy arrayobject.h включен в выходной файл C ++
  • Вызывает import_array при вызове модуля Python (иначе всеМетоды NumPy будут иметь ошибки)
  • Отображает любые возвраты std::vector<int> в массивы NumPy с typemap

Этот код должен быть размещен перед вами %import заголовки, которые содержат функции, возвращающие std::vector<int>.Помимо этого ограничения, он полностью автономен, поэтому он не должен добавлять слишком много субъективного «беспорядка» к вашей кодовой базе.

Если вам нужны другие типы векторов, вы можете просто изменить NPY_INT и всебиты int* и int, в противном случае дублируется вышеуказанная функция.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...