我正在尝试学习SWIG,试图将它与python中的C ++的其他包装器进行比较。我想写的功能之一是这样的:
obj = MyObj()
obj.add([(1,2,), (3,4.0,), (5,"six","string",)])
add
需要获取元组列表,这些元组的大小为2或3,而不是所有统一类型(因为,你知道,Python)。在SWIG中执行此操作的最简单方法是什么(最终我希望此代码在实际的C ++对象上调用obj.add_int(1, 2)
,obj.add_double(3, 4.0)
。
答案 0 :(得分:1)
如果是我在SWIG中这样做的话,我会尽量写出最少量的奇怪的额外代码。所以我最初的方法是编写一些Python代码来处理异构列表,并找出为列表中的每个条目调用哪个add_TYPE
。
举个例子:
%module test
%{
#include <iostream>
#include <limits>
%}
%include <std_string.i>
%inline %{
struct MyObj {
void add_double(unsigned a, const double& b, const double& c=std::numeric_limits<double>::quiet_NaN()) {
std::cout << "add_double(): " << a << ", " << b << ", " << c << "\n";
}
void add_int(unsigned a, int b, int c=~0) {
std::cout << "add_int(): " << a << ", " << b << ", " << c << "\n";
}
void add_string(unsigned a, const std::string& b, const std::string& c="") {
std::cout << "add_string(): " << a << ", " << b << ", " << c << "\n";
}
};
%}
%pythoncode %{
def obj_add(self, items):
tries = (self.add_int, self.add_double, self.add_string)
for args in items:
for method in tries:
good = False
try:
method(*args)
except NotImplementedError:
pass # Just pick a different version
else:
good = True
break
if not good: raise TypeError('No matches')
MyObj.add = obj_add
%}
为了方便演示,我在一个MyObj
指令中声明,定义并包装了所有%inline
。您可能会略有不同,例如:重载而不是默认参数来区分两个/三个arg版本可能很有意义,并且只能与SWIG一起工作。
add
的魔力完全用Python编写,使用了老式的鸭式。作为普通的Python,您可能想要采用的所有常用方法都是开放式的 - 迭代列表并使用try
来处理失败,直到我选择了一个工作。 (值得注意的是你在重要事项中尝试它们的顺序 - 如果在int之前加倍,那么你将永远不会选择整数加倍)。
这足以让我跑:
from test import *
obj = MyObj()
obj.add([(1,2,), (3,4.0,), (5,"six","string",)])
print('Done')
我用它编译后:
swig -py3 -c++ -python -Wall test.i
g++ -Wall -Wextra test_wrap.cxx -I/usr/include/python3.4/ -lpython3.4m -shared -o _test.so
结果如预期:
add_int(): 1, 2, -1
add_double(): 3, 4, nan
add_string(): 5, six, string
Done
您可以通过其他几种方式解决这个问题,最明显的另一种选择是使用大量Python C API调用直接编写add
(PyFloat_Check
,PyInt_Check
等。 )但是当我们可以依赖现有的SWIG行为时,这似乎需要付出很多努力来维护代码。