SWIG обернутый вектор векторов (от C ++ до python) - как распознать внутренний вектор как прокси-объект? - PullRequest
10 голосов
/ 10 марта 2011

Я сталкиваюсь с проблемой, подобной Wrap std :: vector из std :: vectors, C ++ SWIG Python - но это не просто синтаксический анализ C ++. У меня есть следующее в моем коде C ++

namespace ns {
    typedef unsigned long long uint64_t;
    typedef std::vector<uint64_t> Vector;
    typedef std::vector<Vector> VectorOfVectors;

    class MyClass {
        /// ...

        /// Returns a reference to the internal vector allocated in C++ land
        const VectorOfVectors &GetVectors() const;
    };
}

И в оболочке SWIG

%module myswig    
// ...
%template(Uint64V) std::vector<ns::uint64_t>;
%template(VUint64V) std::vector<std::vector<ns::uint64_t> >;

Итак, обертка работает нормально, включая класс, и я могу получить вектор векторов класса. OK:

import myswig
m = myswig.MyClass()
v = m.GetVectors()
print v

Что дает мне:

<myswig.VUint64V; proxy of <Swig Object of type 'std::vector< std::vector< ns::uint64_t,std::allocator< ns::uint64_t > > > *' at 0x994a050> >

Но если я получу доступ к элементу в векторе, я не получу прокси, который является myswig.Uint64V - и это моя проблема.

x = v[0]
print x

Я бы хотел получить:

<myswig.Uint64V; proxy of <Swig Object of type 'std::vector< ns::uint64_t, std::allocator< ns::uint64_t > > *' at 0x994a080> >

Вместо этого я получаю:

(<Swig Object of type 'ns::uint64_t *' at 0x994a080>, <Swig Object of type 'ns::uint64_t *' at 0x994a098>) 

То есть индекс в векторе векторов дает мне кортеж из 2-х элементов, а не прокси для нужного мне векторного класса (так что доступ к внутреннему вектору так же прост, как и к другим векторам).

Я также получаю предупреждение:

swig/python detected a memory leak of type 'ns::uint64_t *', no destructor found.

потому что, конечно, для этого типа не определен деструктор.

Есть идеи?

1 Ответ

10 голосов
/ 15 марта 2011

Я работал над этим с моим коллегой, и нам удалось найти некоторые решения.

Прежде всего, в SWI-файле .i важно определить эту переменную препроцессора:

%{
#   define SWIG_PYTHON_EXTRA_NATIVE_CONTAINERS 
%}

И затем, чтобы убедиться, что ссылки, возвращаемые такими методами, как front (), back (), operator [] и т. Д., Действительно сопоставлены с правильным типом прокси для внутреннего вектора, помогают следующие сопоставления типов:

// In pop()
%typemap(out) std::vector<std::vector<ns::uint64_t> >::value_type { 
$result = SWIG_NewPointerObj(SWIG_as_voidptr(&$1), $descriptor(std::vector<ns::uint64_t>), 0 |  0 ); 
} 

// In front(), back(), __getitem__()
%typemap(out) std::vector<std::vector<ns::uint64_t> >::value_type & { 
    $result = SWIG_NewPointerObj(SWIG_as_voidptr($1), $descriptor(std::vector<ns::uint64_t>), 0 |  0 ); 
} 

Мы также обнаружили, что если вы хотите, чтобы ns :: uint64_t обрабатывался как переменная long Python (эквивалентно long long без знака C), то потребовалось бы еще несколько типов, чтобы гарантировать, что методы вектора, использующие значения и ссылки, вместо этого будут просто используйте 64-битные целочисленные значения.

// In __getitem__()
%typemap(out) ns::uint64_t {
    $result = PyLong_FromUnsignedLongLong($1);
}
// Not used (but probably useful to have, just in case)
%typemap(in) ns::uint64_t {
    $1 = PyLong_AsUnsignedLongLong($input);
}
// In pop()
%typemap(out) std::vector<ns::uint64_t>::value_type {
    $result = PyLong_FromUnsignedLongLong($1);
}
// In __getitem__(), front(), back()
%typemap(out) std::vector<ns::uint64_t>::value_type & {
    $result = PyLong_FromUnsignedLongLong(*$1);
}
// In __setitem__(), append(), new Uint64Vector, push_back(), assign(), resize(), insert()
// This allows a python long literal number to be used as a parameter to the above methods. 
// Note the use of a local variable declared at the SWIG wrapper function scope,
// by placing the variable declaration in parentheses () prior to the open brace {
%typemap(in) std::vector<ns::uint64_t>::value_type & (std::vector<ns::uint64_t>::value_type temp) {
    temp = PyLong_AsUnsignedLongLong($input);
    $1 = &temp;
}

Я надеюсь, что это решение поможет людям в будущем!

...