【问题标题】:Boost.Python failing to resolve derived type passed to functionBoost.Python 无法解析传递给函数的派生类型
【发布时间】:2015-04-23 21:49:47
【问题描述】:

我在 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

感谢任何帮助!

【问题讨论】:

    标签: python c++ boost boost-python


    【解决方案1】:

    在这种特殊情况下,错误消息具有误导性。该函数接收具有正确 type 的参数;但是,该参数有一个不适当的Bar 初始化程序未初始化其层次结构的 Python Base 部分。 Python 实例不包含 boost::shared_ptr&lt;Base&gt; 实例,导致 Boost.Python 无法调度到 C++ 函数:

    class Bar(Base):
        def __init__(self):
            pass # Base is not initialized.
    
    fun(Base()) # No boost::shared_ptr<Base> instance.
    

    要解决此问题,请在 Bar.__init__() 内显式调用 Base.__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__() 方法,那么它应该显式调用父类的 __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(() 验证,但类型并不表示实例是否具有适当的值。

    【讨论】:

    • 我回到电脑前会试试这个!
    猜你喜欢
    • 2015-08-20
    • 2023-04-07
    • 1970-01-01
    • 1970-01-01
    • 2014-04-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-27
    相关资源
    最近更新 更多