Boost.Python教程:函数

版权声明:本文为博主原创文章,欢迎转载(请声明出处),私信通知即可 https://blog.csdn.net/xinqingwuji/article/details/89227511

在本章中,我们将更详细地介绍Boost.Python驱动的函数。我们将看到一些工具,可以将C ++函数暴露给Python,避免潜在的pifall,例如悬空指针和引用。
我们还将看到一些工具,使我们更容易公开利用C ++特性(如重载和默认参数)的C ++函数。

继续阅读......

但在此之前,您可能想要启动Python 2.2或更高版本并键入

 >>> import this.
The Zen of Python, by Tim Peters
Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than right now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!

Tim Peters的Python之禅
美丽胜过丑陋。
显式优于隐式。
简单比复杂更好。
复杂比困难更好。
普通优于嵌套。
稀疏优于密集。
可读性很重要。
特殊情况不足以打破规则。
虽然实用性胜过纯洁。
错误不应该默默地传递。
除非明确沉默。
面对模棱两可,拒绝猜测的诱惑。
应该有一个 - 最好只有一个 - 明显的方法来做到这一点
虽然这种方式起初可能并不明显,除非你是荷兰人。
现在比永远好。
虽然从来没有比现在更好。
如果实施很难解释,
这是一个坏主意。
如果实现很容易解释,那可能是个好主意。
命名空间是一个很棒的主意 - 让我们做更多的事情吧!

调用策略
在C ++中,我们经常处理参数和返回类型,如指针和引用。这种原始类型相当,嗯....,低水平,他们能够传递给我们的信息也很不多。至少,我们不知道指针的所有者或引用的对象。
难怪Java和Python等语言从不处理这种低级实体。在C ++中,通常认为使用精确描述所有权语义的智能指针是一种很好的做法。尽管如此,即使是好的C ++接口有时也会使用原始引用和指针,因此Boost.Python必须处理它们。要做到这一点,它可能需要你的帮助。
考虑以下C ++函数:

X&f(Y&y,Z * z);

库如何包装此函数?一种天真的方法围绕结果引用构建Python X对象。这种策略可能会或可能不会成功。这是一个没有的例子

>>> x = f(y, z) # x refers to some C++ X
>>> del y
>>> x.some_method() # CRASH!

有什么问题?

如果实现了f(),如下所示:

X& f(Y& y, Z* z)
{
    y.z = z;
    return y.x;
}

问题是结果X&的生命周期与y的生命周期有关,因为f()返回对y对象成员的引用。这个成语在C ++的上下文中并不罕见且完全可以接受。然而,Python用户不应该只使用我们的C ++接口来崩溃系统。在这种情况下,删除y将使对X的引用无效。我们有一个悬空引用。

这是发生了什么:

  1. f被称为传递对y的引用和指向z的指针
  2. 返回对y.x的引用
  3. y被删除。 x是悬空参考
  4. x.some_method() 被调用
  5. boom!奔溃啦

我们可以将结果复制到一个新对象:

扫描二维码关注公众号,回复: 5858910 查看本文章
>>> f(y,z)#set(42)#结果消失
>>> y.x.get()#没有崩溃,但仍然很糟糕
3.14

这不是我们C ++界面的意图。我们已经打破了Python接口应该尽可能地反映C ++接口的承诺。
我们的问题不止于此。假设Y实现如下:

struct Y
{
    X x; Z* z;
    int z_value() { return z->value(); }
};

请注意,数据成员z由类Y使用原始指针保存。现在我们在Y里面有一个潜在的悬空指针问题:

>>> x = f(y, z) # y refers to z
>>> del z       # Kill the z object
>>> y.z_value() # CRASH!

作为参考,这里再次执行f:

X& f(Y& y, Z* z)
{
    y.z = z;
    return y.x;
}

这是发生了什么:

  1. f被称为传递对y的引用和指向z的指针
  2. 指向z的指针由y保存
  3. 返回对y.x的引用
  4. z被删除。 y.z是一个悬垂的指针
  5. y.z_value()被调用
  6. 调用z-> value()
  7. BOOM!

调用策略
呼叫策略可用于诸如上面详述的示例的情况。在我们的示例中,return_internal_reference和with_custodian_and_ward是我们的朋友:

def("f", f,
    return_internal_reference<1,
        with_custodian_and_ward<1, 2> >());

你问的1和2参数是什么?

return_internal_reference<1

通知Boost.Python第一个参数,在我们的例子中是Y&y,是返回引用的所有者:X&。 “1”只是指定第一个参数。简而言之:“返回内部引用X并由第一个参数Y&y拥有”。

with_custodian_and_ward<1, 2>

通知Boost.Python,由病房指示的参数的生命周期(即
第二个参数:Z * z)取决于保管人指示的参数的生命周期(即第一个参数:Y和y)。

同样重要的是要注意我们已经定义了上述两个政策。可以通过链接组成两个或更多策略。这是一般语法:

policy1<args...,
    policy2<args...,
        policy3<args...> > >

以下是预定义的呼叫策略列表。可在此处找到详细说明这些内容的完整参考。

with_custodian_and_ward:关联参数的生命周期
with_custodian_and_ward_postcall:关联参数和结果的生命周期
return_internal_reference:将一个参数的生存期与结果的生命周期关联起来
return_value_policy <T>与T之一:
reference_existing_object:天真(危险)的方法
copy_const_reference:Boost.Python v1方法
copy_non_const_reference
manage_new_object:采用指针并保存实例


 记住禅宗,卢克:

“明确比隐含更好”

“面对模棱两可,拒绝猜测的诱惑”
 

猜你喜欢

转载自blog.csdn.net/xinqingwuji/article/details/89227511