使用SWIG和Python/C API包装返回std::map的函数
Using SWIG and the Python/C API to wrap a function which returns a std::map
我想包装一个C++例程,它返回一个由整数和指向C++类实例的指针组成的std::map
。我很难与SWIG合作,如果能提供任何帮助,我将不胜感激。我试图通过一个简单的例子将这个问题归结为其本质。
标题test.h
定义如下:
/* File test.h */
#include <stdlib.h>
#include <stdio.h>
#include <map>
class Test {
private:
static int n;
int id;
public:
Test();
void printId();
};
std::map<int, Test*> get_tests(int num_tests);
实现在test.cpp
中定义如下:
/* File test.cpp */
#include "test.h"
std::map<int, Test*> get_tests(int num_tests) {
std::map<int, Test*> tests;
for (int i=0; i < num_tests; i++)
tests[i] = new Test();
return tests;
}
int Test::n = 0;
Test::Test() {
id = n;
n++;
}
void Test::printId() {
printf("Test ID = %d", id);
}
我已经编写了一个SWIG接口文件test.i
来尝试适应这个例程,这样我就可以在Python:中以字典的形式返回std::map<int, Test*>
%module test
%{
#define SWIG_FILE_WITH_INIT
#include "test.h"
%}
%include <std_map.i>
%typemap(out) std::map<int, Test*> {
$result = PyDict_New();
int size = $1.size();
std::map<int, Test*>::iterator iter;
Test* test;
int count;
for (iter = $1.begin(); iter != $1.end(); ++iter) {
count = iter->first;
test = iter->second;
PyDict_SetItem($result, PyInt_FromLong(count),
SWIG_NewPointerObj(SWIG_as_voidptr(test), SWIGTYPE_p_Test, SWIG_POINTER_NEW | 0));
}
}
%include "test.h"
我包装例程并编译SWIG生成的包装器代码,并将其链接为共享库,如下所示:
> swig -python -c++ -o test_wrap.cpp test.i
> gcc -c test.cpp -o test.o -fpic -std=c++0x
> gcc -I/usr/include/python2.7 -c test_wrap.cpp -o test_wrap.o -fpic -std=c++0x
> g++ test_wrap.o test.o -o _test.so -shared -Wl,-soname,_test.so
然后我希望能够在Python中执行以下操作:
import test
tests = test.get_tests(3)
print tests
for test in tests.values():
test.printId()
然而,如果我将其作为脚本example.py
运行,我会得到以下输出:
> python example.py
{0: <Swig Object of type 'Test *' at 0x7f056a7327e0>, 1: <Swig Object of type 'Test *' at
0x7f056a732750>, 2: <Swig Object of type 'Test *' at 0x7f056a7329f0>}
Traceback (most recent call last):
File "example.py", line 8, in <module>
test.printId()
AttributeError: 'SwigPyObject' object has no attribute 'printId'
有什么想法吗?为什么我得到SwigPyObject
实例作为输出,而不是Test
的SWIG代理?如有任何帮助,我们将不胜感激!
目前,您看到的问题是由SWIG提供的std_map.i中的默认行为引起的。它提供了试图合理包装所有std::map
使用的类型映射。
其中之一是干扰您自己的输出类型映射,所以如果我们将您的接口文件更改为:
%module test
%{
#define SWIG_FILE_WITH_INIT
#include "test.h"
%}
%include <std_map.i>
%clear std::map<int, Test*>;
%typemap(out) std::map<int, Test*> {
$result = PyDict_New();
int size = $1.size();
std::map<int, Test*>::iterator iter;
Test* test;
int count;
for (iter = $1.begin(); iter != $1.end(); ++iter) {
count = iter->first;
test = iter->second;
PyObject *value = SWIG_NewPointerObj(SWIG_as_voidptr(test), SWIGTYPE_p_Test, 0);
PyDict_SetItem($result, PyInt_FromLong(count), value);
}
}
%include "test.h"
那么你的例子就行了。%clear
抑制std_map.i中的默认类型映射,但保留定义本身。我不太清楚是什么原因导致了问题,但如果没有更多的挖掘,你可以只使用%template
和默认行为,除非有充分的理由不这样做。
顺便说一句,你的电话:
SWIG_NewPointerObj(SWIG_as_voidptr(test), SWIGTYPE_p_Test, SWIG_POINTER_NEW | 0));
可能没有达到你想要的效果——它将指针的所有权转移到Python,这意味着一旦Python代理完成,它将为你调用delete
,并在你的映射中留下一个悬空指针。
您也可以使用$descriptor
来避免计算SWIG的内部名称篡改方案,因此它变成:
// No ownership, lookup descriptor:
SWIG_NewPointerObj(SWIG_as_voidptr(test), $descriptor(Test*), 0);
- 来自 std::list 的迭代器 .end() 按预期返回"0xcdcdcdcdcdcdcdcd"但 .begin()
- 如何获取std::result_of函数的返回类型
- 架构决策:返回std::future还是提供回调
- 如果我std::dynamic_pointer_cast并且底层dynamic_cast的结果为null,那么返回的sh
- 使用std::mt19937从字符串中返回一个随机单词
- 如何使用std::min和std::less返回对象
- <Windows>为什么 std::thread::native_handle 返回类型为"long long unsigned int"的值,而不是 void*(又名 HANDLE)?
- 返回一个带有 std::move 的对象并链接函数
- 推导 std::vector::back() 的返回类型
- 为什么 C++ std::unordered_map 从 emplace/ 找到返回一个迭代器?
- 如何从 std::thread 返回值
- 从 std<Derived>::shared_ptr 的向量返回 std::shared_ptr<Base>
- 在带有尾部斜杠的路径上返回 std::filesystem::create_directories() 的值
- std::find,返回所有找到的值的替代方法,而不仅仅是存在重复的向量的第一个值
- 如何从 std::vector 搜索和返回项目
- 从嵌套循环中的 std::list 中删除将返回访问冲突
- 我无法使用C++指针指向类方法返回的 std::vector
- 如何使用 uint64_t 键类型从 std::map<int, std::string> 返回值?
- 使用 std::p air 进行返回值优化
- 将 std::map::emplace 与返回 shared_ptr 的函数一起使用是否正确?