Boost.Python无法解析传递给函数的派生类型

时间:2015-04-23 21:49:47

标签: python c++ boost boost-python

我在Boost.Python冒险中遇到了另一个障碍。

我定义了以下Python模块:

#include <Python.h>
#include <iostream>
#include <boost/python.hpp>

using namespace boost;
using namespace boost::python;

struct Base { };

void foo(boost::shared_ptr<Base>)
{
    std::cout << "yay\n";
}

BOOST_PYTHON_MODULE(Test)
{
    class_<Base, shared_ptr<Base>, noncopyable>("Base", init<>());

    def("foo", foo);
}

运行以下脚本:

from Test import *

class Bar(Base):
    def __init__(self):
        pass

foo(Base()) #works
foo(Bar()) #error

最后一行会产生错误:

Python argument types in
    foo(Bar)
did not match C++ signature:
    foo(class boost::shared_ptr<struct Base>)

现在我的问题是为什么这不起作用?当然,类型系统应该能够计算BarBase个实例吗?

http://coliru.stacked-crooked.com/a/43f111fb3032a20a

感谢任何帮助!

1 个答案:

答案 0 :(得分:2)

在这种特殊情况下,错误消息具有误导性。该函数接收具有正确类型的参数;但是,该参数具有不合适的Bar初始值设定项未初始化其层次结构的Python Base部分。 Python实例不包含boost::shared_ptr<Base>实例,导致Boost.Python无法调度到C ++函数:

class Bar(Base):
    def __init__(self):
        pass # Base is not initialized.

fun(Base()) # No boost::shared_ptr<Base> instance.

要解决此问题,请在Base.__init__()中明确调用Bar.__init__()

class Bar(Base):
    def __init__(self):
        Base.__init__(self) # Instantiates boost::shared_ptr<Base>.

fun(Bar()) # Boost.Python will extract boost::shared_ptr<Base> from Bar().

有关详细信息,在Python中,如果派生类定义了__init__()方法,那么它应该显式地调用父类&#39; __init__()方法。 Python文档states

  

如果基类具有__init__()方法,则派生类的__init__()方法(如果有)必须显式调用它以确保正确初始化实例的基类部分;例如:BaseClass.__init__(self, [args...])

在Boost.Python中,C ++类包装器有一个instance_holder。这些对象在其Python对象包装器中包含C ++实例,并且C ++对象的实例化发生在Python对象的__init__函数中:

  

当调用包装的C ++类的__init__函数时,会创建一个新的instance_holder实例并安装在Python对象中[...]

因此,如果未调用Python对象的__init__()方法,则不会实例化内部C ++对象。当从Python调用暴露的C ++函数时,Boost.Python将检查调用参数,尝试在一组公开函数中标识匹配的C ++函数。如果找不到匹配项,它将引发Boost.Python.ArgumentError异常,列出参数类型和它无法匹配的C ++函数集。

这是一个完整的示例demonstrating,有两种不同的Python类型继承自暴露的C ++类型,其中一个层次结构正确初始化而另一个层次结构不是:

#include <boost/python.hpp>

struct base {};

void foo(boost::shared_ptr<base>) {}

BOOST_PYTHON_MODULE(example)
{
  namespace python = boost::python;
  python::class_<base, boost::shared_ptr<base>, boost::noncopyable>(
      "Base", python::init<>())
    ;

  python::def("foo", &foo);
}

交互式使用:

>>> import example
>>> class GoodDerived(example.Base):
...     def __init__(self):
...         example.Base.__init__(self)
...
>>> class BadDerived(example.Base):
...     def __init__(self):
...         pass
...
>>> assert(isinstance(GoodDerived(), example.Base))
>>> assert(isinstance(BadDerived(), example.Base))
>>> try:
...     example.foo(GoodDerived())
...     got_exception = False
... except:
...     got_exception = True
... finally:
...     assert(not got_exception)
...
>>> try:
...     example.foo(BadDerived())
...     got_exception = False
... except:
...     got_exception = True
... finally:
...     assert(got_exception)

请注意,虽然类型层次结构通过isinstance(()是正确且可验证的,但类型并不表示实例是否具有适当的值。