SWIG Python 到 C++:尝试设置类型映射的结构成员时出现 TypeError

发布于 2024-12-28 20:36:45 字数 2566 浏览 0 评论 0原文

SWIG 似乎生成了错误的绑定来转换映射类型的结构字段,导致尝试将映射字段设置为 python 字典时出现 TypeError。我遗漏了一个错误吗?不受支持的用例? SWIG 中的错误?

首先是输出

Traceback (most recent call last):
  File ".\use_test.py", line 4, in <module>
    struct.data = { 'A':1, 'B':2 }
  File "C:\Users\kmahan\Projects\SwigTest\test.py", line 150, in <lambda>
    __setattr__ = lambda self, name, value: _swig_setattr(self, MyStruct, name, value)
  File "C:\Users\kmahan\Projects\SwigTest\test.py", line 49, in _swig_setattr
    return _swig_setattr_nondynamic(self,class_type,name,value,0)
  File "C:\Users\kmahan\Projects\SwigTest\test.py", line 42, in _swig_setattr_nondynamic
    if method: return method(self,value)
TypeError: in method 'MyStruct_data_set', argument 2 of type 'std::map< std::string,unsigned int,std::less< std::string >,std::allocator< std::pair< std::string const,unsigned int > > > *'

,这是我的测试用例:

test.i

%module test

%include "std_string.i"
%include "std_map.i"

namespace std {
    %template(StringIntMap) map<string, unsigned int>;
}

%{
#include "test.h"
%}

%include "test.h"

test.h

#ifndef _TEST_H_
#define _TEST_H_

#include <string>
#include <map>

struct MyStruct 
{
    std::map<std::string, unsigned int> data;
};

#endif //_TEST_H_

test.cpp

#include "test.h"

run_test.py

import test

struct = test.MyStruct()
struct.data = { 'A':1, 'B':2 }

print struct.data

我使用 swig -python -c++ -o test_wrapper.cpp test.i 构建 test_wrapper.cpp,编译其他所有内容,然后运行 ​​run_test.py。

作为解决方法,我可以显式定义一个设置器( void setData(const map& data) )它生成不同的转换代码——它通过traits_asptr而不是SWIG_ConvertPtr——并且似乎可以工作,但不是很Pythonic!

编辑

这是我的 .i 文件,它将属性本身的获取和设置传递给 C++ getters 和 setters。我认为这是内森在他的答案下面的评论中建议的。

%module test

%include "std_string.i"
%include "std_map.i"

namespace std {
    %template(StringIntMap) map<string, unsigned int>;
}

struct MyStruct 
{
    const std::map<std::string, unsigned int>& getData() const;
    void setData(const std::map<std::string, unsigned int>&);

    %pythoncode %{
        __swig_getmethods__["data"] = getData
        __swig_setmethods__["data"] = setData
        if _newclass:
            data = _swig_property(getData, setData)
    %}
};

SWIG seems to be generating incorrect bindings for converting a struct field of type map, resulting in a TypeError trying to set the map field to a python dictionary. Is there an error I am missing? an unsupported use-case? a bug in SWIG?

First the output

Traceback (most recent call last):
  File ".\use_test.py", line 4, in <module>
    struct.data = { 'A':1, 'B':2 }
  File "C:\Users\kmahan\Projects\SwigTest\test.py", line 150, in <lambda>
    __setattr__ = lambda self, name, value: _swig_setattr(self, MyStruct, name, value)
  File "C:\Users\kmahan\Projects\SwigTest\test.py", line 49, in _swig_setattr
    return _swig_setattr_nondynamic(self,class_type,name,value,0)
  File "C:\Users\kmahan\Projects\SwigTest\test.py", line 42, in _swig_setattr_nondynamic
    if method: return method(self,value)
TypeError: in method 'MyStruct_data_set', argument 2 of type 'std::map< std::string,unsigned int,std::less< std::string >,std::allocator< std::pair< std::string const,unsigned int > > > *'

And here is my test case:

test.i

%module test

%include "std_string.i"
%include "std_map.i"

namespace std {
    %template(StringIntMap) map<string, unsigned int>;
}

%{
#include "test.h"
%}

%include "test.h"

test.h

#ifndef _TEST_H_
#define _TEST_H_

#include <string>
#include <map>

struct MyStruct 
{
    std::map<std::string, unsigned int> data;
};

#endif //_TEST_H_

test.cpp

#include "test.h"

run_test.py

import test

struct = test.MyStruct()
struct.data = { 'A':1, 'B':2 }

print struct.data

I build test_wrapper.cpp with swig -python -c++ -o test_wrapper.cpp test.i, compile everything else, and run run_test.py.

As a workaround I can explicitly define a setter instead (
void setData(const map<string, unsigned int>& data)
) which generates different conversion code -- it goes through traits_asptr instead of SWIG_ConvertPtr -- and seems to work but is not very pythonic!

EDIT

Here is my .i file that pipes gets and sets of the attribute itself to C++ getters and setters. I think this is what Nathan suggested in the comment below his answer.

%module test

%include "std_string.i"
%include "std_map.i"

namespace std {
    %template(StringIntMap) map<string, unsigned int>;
}

struct MyStruct 
{
    const std::map<std::string, unsigned int>& getData() const;
    void setData(const std::map<std::string, unsigned int>&);

    %pythoncode %{
        __swig_getmethods__["data"] = getData
        __swig_setmethods__["data"] = setData
        if _newclass:
            data = _swig_property(getData, setData)
    %}
};

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

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

发布评论

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

评论(1

你不是我要的菜∠ 2025-01-04 20:36:45

当您设置 struct.data 时,它需要一个 test.StringIntMap,而不是 python dict。

最简单的事情就是你这样做:

struct.data = test.StringIntMap({ 'A':1, 'B':2 })

When you're setting struct.data, it's expecting a test.StringIntMap, not a python dict.

The easiest thing is for you to do this:

struct.data = test.StringIntMap({ 'A':1, 'B':2 })
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文