Typemap ресурсы и преобразование списка в вектор (и обратно) - PullRequest
1 голос
/ 16 октября 2019

Я использую SWIG, чтобы обернуть c ++ в python, и мне нужно использовать typemap для того, чтобы сделать мой скрипт на python максимально простым. В качестве первой попытки я просто отправляю 2 списка, преобразовываю их в векторные, добавляю два вектора и возвращаю результат обратно в новый список.

Моя проблема в том, что я нахожу руководство по SWIG не очень поучительным, трудным для понимания и не даю каких-либо убедительных, полных примеров того, как я могу написать свою собственную карту типов.

Мои вопросы:

  1. Как мне обеспечить правильное преобразование моих списков в векторные, а затем обратно?
  2. Есть ли лучшие учебники / ссылки? там, как писать карты типов и что означают все синтаксис / функции?

Вот мой код:

add_array.h

#include <vector>
#include <functional>

std::vector<int> add_array(std::vector<int> src1, std::vector<int> src2);

add_array. i

%module add_array
%{
#include "add_array.h"
%}

%include std_vector.i 
%template(vectorInt) std::vector<int>;

%include "add_array.h"

add_array.cpp

#include "add_array.h"
#include <cassert>
#include <cstring>

std::vector<int> add_array(std::vector<int> src1, std::vector<int> src2) {  
  assert(src1.size() == src2.size());
  std::vector<int> dst;
  dst.resize(src1.size());

  for (size_t i = 0; i < src1.size(); i++) {
    dst[i] = src1[i] + src2[i];
  }
  return dst;
}

Makefile

all:
rm -f *.so *.o *_wrap.* *.pyc *.gch add_array.py
swig -c++ -python add_array.i
g++ -fpic -c add_array_wrap.cxx add_array.h add_array.cpp -I/home/tools/anaconda3/pkgs/python-3.7.3-h0371630_0/include/python3.7m/
g++ -shared add_array_wrap.o add_array.o -o _add_array.so

array.py (это файл, который я запускаю)

import add_array

a = [1, 2, 3, 4, 5, 6]
b = [5, 6, 7, 8, 9, 10]
c = add_array.add_array(a, b)
print(c)

Вывод: (6, 8, 10, 12, 14, 16)

Это будет кортежем (я бы хотел, чтобы это был список). Похоже, мне просто повезло, что он может преобразовать входные списки в векторы (хотя в другом направлении это не так удачно), но мне бы очень хотелось узнать, как это происходит и как я могу изменить это для будущего кода, если это необходимо.

Спасибо!

1 Ответ

2 голосов
/ 16 октября 2019

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

Пример (без проверки ошибок):

%module add_array
%{
#include "add_array.h"
%}

%include <std_vector.i>
%template(vectorInt) std::vector<int>;

// Override the template output typemap with one that returns a list.
// An "out" typemap controls how a value is returned.
// When a function returns std::vector<int> this template will convert it to
// a Python object.  In this case, a PyList.
// 
// Note: PyObject* tmp declares a local variable that will be used by this code snippet.
// Make sure to look at the generated wrapper code and find the add_array_wrap function
// and how this code is integrated into it.
// 
%typemap(out) std::vector<int> (PyObject* tmp) %{

    // Allocate a PyList object of the requested size.
    // $1 references the first type in the type list (in this case, std::vector<int>)
    // and represents the c++ return value of a function that returns
    // this type; therefore, we can call methods on that value to get the size.
    //
    // Note: The elements of the new PyList are null pointers and MUST be
    //       populated before returning it to Python.
    //
    tmp = PyList_New($1.size());

    // Populate the PyList.  PyLong_FromLong converts a C++ "long" to a
    // Python PyLong object.  PyList_SET_ITEM takes a PyList object (tmp),
    // an index (i), and a Python object to put in the list.  This particular
    // function *steals* the reference to the Python object, so you don't have to
    // Py_DECREF the object to free it later.
    //
    for(int i = 0; i < $1.size(); ++i)
        PyList_SET_ITEM(tmp,i,PyLong_FromLong($1[i]));

    // $result is where you assign the Python object that should be returned
    // after converting the C++ $1 object.  SWIG_Python_AppendOutput is not
    // well-documented, but it appends the return object to an existing
    // returned object.  It's most useful for "argout" typemaps where multiple
    // output or in/out arguments in C++ can be returned as a tuple of
    // return values in Python.  For example, a function like:
    //
    //     void func(int* pValue1, int* pValue2);
    //
    // would normally return None ($result = Py_None), but an "argout" typemap
    // could *$1 to a PyLong and use SWIG_Python_AppendOutput to add it to
    // the result.  The template would be applied twice and you'd get a tuple.
    //
    $result = SWIG_Python_AppendOutput($result,tmp);
%}

%include "add_array.h"

Вывод:

>>> import add_array
>>> add_array.add_array([1,2,3],[4,5,6])
[5, 7, 9]

AsЧто касается учебных пособий, я только когда-либо читал документацию SWIG и документацию для конкретного языка для расширений языка Си. Это на самом деле довольно хорошо, поскольку документация идет, но вы не можете просто выбирать, что читать. Изучите первые дюжину разделов для основ, а затем перейдите к разделу, связанному с языком (например, Python). Под установкой SWIG также находится каталог примеров.

Ссылки:

Вы должны просмотреть источники SWIG, чтобы получить любую информацию оSWIG_Python_AppendOutput. Или просто Google для других примеров.

...