假设我已经包装了我的C类Foo
和Bar
,并且可以通过SWIG生成的模块wrap_py
从Python访问它们:
// C++ class Bar { int i; Bar(int i) { this.i = i; } } class Foo { public: Foo(Bar* bar) { this.bar = bar; } Bar* GetBar() { return this.bar; } private: Bar* bar; }
在Python中,我创建了面向用户的类,它是一个浅层代理,主要添加docstring,并允许IDE对参数名称进行制表符补全:
// Python class Bar(wrap_py.Bar): '''Some description ... Args: i (int): ... ''' def __init__(self, i): super(Bar, self).__init__(i) class Foo(wrap_py.Foo): '''Some description ... Args: bar (instance of Bar): ... ''' def __init__(self, bar): super(Foo, self).__init__(bar)
问题在于Foo。从C类自动生成的GetBar()
,返回类型为wrap\u py的swig实例。条形图
,它没有docstring,也不显示参数名称(swig将所有参数公开为*args
)。相反,我希望它提供我自己的浅代理条
。
那么,我如何告诉SWIG自动返回
Bar
,而不是裸wrap\u py。条形码?
编辑:理想情况下,这对于返回类型
Bar
是可能的,而不仅仅是对于具体的函数签名:
编辑2:我提出了以下装饰器,我需要将其放在每个返回SWIG类型的函数/方法前面:
def typemap(f):
当然,这不好,需要省略。
从functools导入wrapps@wrapps(f)def wrapper(*args,**kwds):typemap={wrap_py.Bar:Bar,#更多类型将出现…}result=f(*参数,**kwds),如果是instance(result,(tuple,list,set)):对于result中的r:r.。\uuuu class\uuuu=typemap。获取(r。items():k.。\uuuuuu类\uuuuu=typemap。获取(k。获取(v__类=类型映射。get(result.\uuuuu class\uuuuu,result.\uuuuu class\uuuuuu)返回结果返回包装器
你提出的两种解决方案都有问题。考虑下面的测试用例:
b=Bar(1)
b.woof=2
print(b.woof)
g=(Foo(b).GetBar())
print(type(g))
print(g.woof)
在该示例中,我们希望最终的print语句的“woof”属性值与我们创建的原始条对象的值相同。也就是说,我们不仅希望类型匹配,而且希望它是相同的实例。使用shadow和decorator方法包装东西,每次返回相同的底层C-Bar实例时,您仍然在创建新的Python对象。
为了解决这个问题,您可能需要设置一个字典,将原始C对象1:1映射到Python代理对象,并在返回Bar对象的任何地方使用它。
作为说明这一点的起点,我设置了以下示例。你的C有多个问题在它的固定和成为test.hh:
class Bar
{
int i;
public:
Bar(int i) { this->i = i; }
};
class Foo
{
public:
Foo(Bar* bar) { this->bar = bar; }
Bar* GetBar() { return this->bar; }
private:
Bar* bar;
};
我写了一个test. i SWIG包装器,它扩展了Bar以提供基于C对象地址的__hash__
:
%module test
%{
#include "test.hh"
%}
%include <stdint.i>
%include "test.hh"
%extend Bar {
intptr_t __hash__() {
return reinterpret_cast<intptr_t>($self);
}
}
然后最后包装。py是从Python扩展而来的,用于实现对象映射和实例查找,包括重写GetBar
以使用以下机制:
import test as wrap_py
class Bar(wrap_py.Bar):
'''Some description ...
Args:
i (int): ...
'''
def __init__(self, i):
super(Bar, self).__init__(i)
Bar._storenative(self)
_objs={}
@classmethod
def _storenative(cls, o):
print('Storing: %d' % hash(o))
cls._objs[hash(o)]=o
@classmethod
def _lookup(cls, o):
print('Lookup: %d' % hash(o))
return cls._objs.get(hash(o), o)
class Foo(wrap_py.Foo):
'''Some description ...
Args:
bar (instance of Bar): ...
'''
def __init__(self, bar):
super(Foo, self).__init__(bar)
def GetBar(self):
return Bar._lookup(super(Foo, self).GetBar())
if __name__=='__main__':
b=Bar(1)
b.woof=2
print(b.woof)
g=(Foo(b).GetBar())
print(type(g))
print(g.woof)
不过,这第一次削减存在一些问题。首先,正如您所注意到的,我们仍然必须手动重写每个可能返回Bar实例的函数,以添加额外的查找调用。其次,查找字典可能会导致Python代理对象的寿命超过其C对应对象的寿命(在最坏的情况下,会将Python Bar代理错误地映射到一个C对象上,而该C对象实际上没有被任何Python派生对象代理。要解决后一个问题,我们可以查看弱引用,但这也有缺陷(Python对象可能会过早地被销毁)。
要使其对所有返回条实例的方法透明地工作,您可以选择以下两种方法之一:
\uuuu getattribute\uuuu
,让它返回一个函数,该函数根据返回类型进行适当的包装和查找
要实现#2,您只需编写一个%typemap(out)Bar*
,查看这是否是我们第一次在给定地址看到Bar实例,如果以前看到过,则返回对同一对象的引用,或者创建一个新的引用。请注意,如果您还没有阻止中间代理对象使其变得比需要的更困难,则需要使用swig-builtin
。因此,我们的界面可以简单地变成:
%module test
%{
#include "test.hh"
#include <map>
namespace {
typedef std::map<Bar*,PyObject*> proxy_map_t;
proxy_map_t proxy_map;
}
%}
%typemap(out) Bar* {
assert($1);
const auto it = proxy_map.find($1);
if (it != proxy_map.end()) {
$result = it->second;
}
else {
$result = SWIG_NewPointerObj(SWIG_as_voidptr($1), $1_descriptor, $owner);
proxy_map.insert(std::make_pair($1, $result));
}
Py_INCREF($result);
}
%include "test.hh"
然后编译并使用上面未修改的Python运行。
swig3.0 -python -c++ -Wall -builtin test.i
g++ -shared -o _test.so test_wrap.cxx -Wall -Wextra -fPIC -I/usr/include/python2.7/ -std=c++11
python wrap.py
这仍然有一个突出的问题:我们无法看到Bar*
实例何时被删除,因此我们可能会在多个C对象的生命周期中意外地循环使用Python代理对象。根据您的目标,您可以在映射内使用弱引用来解决此问题,或者(ab)使用operator new()
来钩住Bar*
实例的创建。
下面的代码来自一个名为ButterKnife的Android库。我正在弄清楚它是怎么工作的。 我试图重新创建此函数的行为: 和用法: 但是异常并非永远不会被捕获,而是在调用方法时在行中抛出。为什么? 还有,这到底是如何工作的?该方法如何知道要转换到什么?
问题内容: 当我尝试编译时,它给了我错误 我应该如何解决这个问题? 问题答案: 该错误是由于以下事实导致的:调用将是不明确的- 应该调用两种方法中的哪一种?从JLS§8.4.2开始: 在类中声明两个具有重写等效签名的方法是编译时错误。 方法的返回类型不是其签名的一部分,因此根据上述说明,您将收到错误。 假设您不能简单地重命名冲突的方法,在这种情况下就不能使用继承,并且需要使用诸如compositi
问题内容: 在JavaScript中,每个对象同时是一个实例和一个类。要进行继承,可以将任何对象实例用作原型。 在Python,C ++等中,有类和实例作为单独的概念。为了进行继承,您必须使用基类创建一个新类,然后可以使用该新类来生成派生实例。 为什么JavaScript朝这个方向发展(基于原型的面向对象)?与传统的基于类的OO相比,基于原型的OO有哪些优点和缺点? 问题答案: 这里大约有一百个术
我试着写一个小函数,它接受两个列表,并根据另一个列表的元素对一个进行排序。所以类似于: 将产生一个排序列表。 然而,可能是一个不同的列表,比如整数、浮点数或其他列表。理想情况下,我希望我的程序能够获取我抛出的任何列表,根据
我可以看到它不工作,因为我尝试了它。我只是无法解释为什么一定要这样。 第二个方法来自一个类,该类是实现第一个getValue()方法的类的子类。 为什么同名不足以覆盖该方法?我想到的唯一论点是,它将反对“是一个”关系,因为用A扩展的第二个方法的类必须具有与A相同的能力,如果你重写返回类型,你就打破了那个法律,对吧?
我想写一个函数返回一个数组,其所有子数组的长度必须为2。例如,返回将是。 我定义: (1); 和 我觉得(1)太复杂了。有更简单的吗?