SWIG 包裹向量的向量(C++ 到 python)-如何将内部向量识别为代理对象?

发布于 2024-10-21 01:55:47 字数 1799 浏览 4 评论 0原文

我面临着与 Wrap std::vector of std::vectors 类似的问题、C++ SWIG Python - 但这不仅仅是简单的 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;
    };
}

我的 C++ 代码和 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.

因为当然没有为此类型定义析构函数。

有什么想法吗?

I'm facing a similar issue to Wrap std::vector of std::vectors, C++ SWIG Python - but it's not just simple C++ parsing. I have the following in my C++ code

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;
    };
}

And in the SWIG wrapper

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

So the wrapping works fine, including the class, and I can retrieve the class's vector of vectors OK:

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

Which gives me:

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

But if I access an element in the vector, I don't get a proxy which is a myswig.Uint64V - and this is my problem.

x = v[0]
print x

What I'd hope to get is:

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

Instead, I'm getting:

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

That is, the index into the vector of vectors is giving me a 2-entry tuple, and not a proxy to the vector class that I need (so that accessing the internal vector is as easy as accessing other vectors).

I'm also getting the warning:

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

because of course there isn't a destructor defined for this type.

Any ideas?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

月亮邮递员 2024-10-28 01:55:47

我和我的一位同事一起研究了这个问题,我们设法提出了一些解决方案。

首先,在 SWIG .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 被视为 python long 变量(相当于 C unsigned long long),则需要一些进一步的类型映射确保使用值和引用的向量方法将仅使用 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;
}

我希望这个解决方案可以帮助人们将来!

I worked on this with a colleague of mine, and we have managed to come up with some solutions.

First of all, in the SWIG .i file, it's important to define this preprocessor variable:

%{
#   define SWIG_PYTHON_EXTRA_NATIVE_CONTAINERS 
%}

And then, to ensure that the references returned from methods such as front(), back(), operator[], etc are actually mapped to the correct proxy type for the internal vector, the following typemaps help:

// 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 ); 
} 

We also discovered that if you want the ns::uint64_t to be treated as a python long variable (equivalent to a C unsigned long long) then a few further typemaps were required to ensure the vector methods using values and references would instead just use 64-bit integer values.

// 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;
}

I hope this solution helps people in future!

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文