python - Boost.Python 的继承和共享指针引用参数

标签 python c++ boost binding boost-python

我在 Boost.Python 中遇到了一个边缘情况,它看起来应该可以工作,但实际上却不起作用。

我拥有的是一个基类和一个派生类,我将它们存储在 python 端的 std::shared_ptr 中。我想要做的是将派生类型 shared_ptr 传递给通过引用接受 Base shared_ptr 的函数。

我做了一些研究并了解了 implicitly_convertible并尝试使用它来解决问题但没有成功(尽管它在其他一些情况下确实有帮助)。将 Derived 传递给接受 Base& 的函数可以使用此方法,但如果它们包装在 shared_ptr 中,则会失败。

我当前收到的是以下消息:

Boost.Python.ArgumentError: Python argument types in
    test_bed_bindings.acceptBaseSharedPtrRef(Derived) did not match C++ signature:
    acceptBaseSharedPtrRef(std::shared_ptr<(anonymous namespace)::Base> {lvalue})

请参阅下面的示例代码:

C++ 绑定(bind)代码

#define BOOST_PYTHON_STATIC_LIB
#define BOOST_PYTHON_USE_GCC_SYMBOL_VISIBILITY 1

#include <boost/optional.hpp>
#include <boost/python.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/make_shared.hpp>

#include <iostream>
#include <memory>


namespace
{

  class Base
  {
  };

  class Derived : public Base
  {
  };

  std::shared_ptr<Base> getBaseSharedPtr()
  {
    auto retVal = std::make_shared<Base>();
    std::cout << "Creating Base shared_ptr - " << retVal.get() << std::endl;
    return retVal;
  }

  std::shared_ptr<Derived> getDerivedSharedPtr()
  {
    auto retVal = std::make_shared<Derived>();
    std::cout << "Creating Derived shared_ptr - " << retVal.get() << std::endl;
    return retVal;
  }

  void acceptBaseSharedPtrRef(std::shared_ptr<Base>& base)
  {
    std::cout << "acceptBaseSharedPtrRef() with " << base.get() << std::endl;
  }

  void acceptBaseSharedPtrConstRef(const std::shared_ptr<Base>& base)
  {
    std::cout << "acceptBaseSharedPtrConstRef() with " << base.get() << std::endl;
  }

  void acceptBaseSharedPtrCopy(std::shared_ptr<Base> base)
  {
    std::cout << "acceptBaseSharedPtrCopy() with " << base.get() << std::endl;
  }

  //

  void acceptBaseRef(Base base)
  {

  }

} // namespace

namespace bindings
{
  BOOST_PYTHON_MODULE(test_bed_bindings)
  {
    PyEval_InitThreads();
    Py_Initialize();

    using namespace boost::python;

    def("getBaseSharedPtr",            &::getBaseSharedPtr);
    def("getDerivedSharedPtr",         &::getDerivedSharedPtr);
    def("acceptBaseSharedPtrRef",      &::acceptBaseSharedPtrRef);
    def("acceptBaseSharedPtrConstRef", &::acceptBaseSharedPtrConstRef);
    def("acceptBaseSharedPtrCopy",     &::acceptBaseSharedPtrCopy);

    def("acceptBaseRef",     &::acceptBaseRef);

    class_<Base, std::shared_ptr<Base> >("Base")
        .def(init<>())
        ;

    class_<Derived, bases<Base>, std::shared_ptr<Derived> >("Derived")
        .def(init<>())
        ;

    implicitly_convertible<Derived, Base>();
    implicitly_convertible<std::shared_ptr<Derived>, std::shared_ptr<Base>>();

  } // BOOST_PYTHON

} // namespace bindings

Python执行代码

import test_bed_bindings

baseObj = test_bed_bindings.Base()
derivedObj = test_bed_bindings.Derived()

test_bed_bindings.acceptBaseRef( baseObj )
test_bed_bindings.acceptBaseRef( derivedObj )

baseSharedPtr = test_bed_bindings.getBaseSharedPtr()
derivedSharedPtr = test_bed_bindings.getDerivedSharedPtr()

test_bed_bindings.acceptBaseSharedPtrCopy( baseSharedPtr )
test_bed_bindings.acceptBaseSharedPtrCopy( derivedSharedPtr )

test_bed_bindings.acceptBaseSharedPtrConstRef( baseSharedPtr )
test_bed_bindings.acceptBaseSharedPtrConstRef( derivedSharedPtr )

test_bed_bindings.acceptBaseSharedPtrRef( baseSharedPtr )
test_bed_bindings.acceptBaseSharedPtrRef( derivedSharedPtr )

示例输出

Creating Base shared_ptr - 0x276fdb8
Creating Derived shared_ptr - 0x276fde8
acceptBaseSharedPtrCopy() with 0x276fdb8
acceptBaseSharedPtrCopy() with 0x276fde8
acceptBaseSharedPtrConstRef() with 0x276fdb8
acceptBaseSharedPtrConstRef() with 0x276fde8
acceptBaseSharedPtrRef() with 0x276fdb8
Traceback (most recent call last):
  File "test_script.py", line 21, in <module>
    test_bed_bindings.acceptBaseSharedPtrRef( derivedSharedPtr )
Boost.Python.ArgumentError: Python argument types in
    test_bed_bindings.acceptBaseSharedPtrRef(Derived)
did not match C++ signature:
    acceptBaseSharedPtrRef(std::shared_ptr<(anonymous namespace)::Base> {lvalue})

最佳答案

这是故意的。为了减少悬空引用的可能性并在语言之间提供明确的方向性,Boost.Python 将通过对函数的 const 引用传递由右值转换产生的临时对象。 implicit_convertible<Source, Target> 函数注册一个来自 Python 的右值转换。由于转换器的结果是一个右值,因此只能通过值或常量引用来接受它。


当通过 boost::python::class_<T, HeldType, Bases> 注册类(class)时和HeldType正在包装 T :

  • 生成的 Python 类嵌入 HeldType 的实例
  • T 的实例注册到 Python 转换器Python 类的实例
  • 将 Python 类实例的左值从 Python 转换器注册到 T 的实例
  • HeldType 的实例注册到 Python 转换器到Python对象
  • 将 Python 类实例的左值从 Python 转换器注册到 HeldType 的实例
  • 对于Bases中的每个基地,将 Python 类实例的左值 from-Python 转换器注册为 T 的实例在基地(不是基地的 HeldType )
  • 对于 Bases 中的每个多态碱基,从T的实例注册一个到Python的转换器。由 Python 类的基类持有

使用以下设置:

class base {};
class derived: public base {};

BOOST_PYTHON_MODULE(example)
{
  namespace python = boost::python;

  python::class_<base, std::shared_ptr<base>>("Base");
  python::class_<derived, python::bases<base>,
    std::shared_ptr<derived>>("Derived");

  python::implicitly_convertible<std::shared_ptr<derived>,
                                 std::shared_ptr<base>>();
}

以下左值从 Python 转换是可能的,因为 Python 对象包含 C++ 对象的实例:

  • example.Basebase , base& , const base& , std::shared_ptr<base> , std::shared_ptr<base>& ,和const std::shared_ptr<base>&
  • example.Derivedbase , base& , const base& , derived , derived& , const derived& , std::shared_ptr<derived> , std::shared_ptr<derived>& ,和const std::shared_ptr<derived>&

可以进行以下到 Python 的转换:

  • basestd::shared_ptr<base>example.Base
  • derivedstd::shared_ptr<derivedexample.Derived

如果base是多态的,那么以下到 Python 的转换是可能的:

  • 动态类型为 derived* 的对象和静态类型 base*example.Derived
  • std::shared_ptr<base>持有 derived 的实例至example.Derived

由于通过implicitly_convertible显式注册,以下右值转换是可能的:

  • example.Derivedstd::shared_ptr<base>const std::shared_ptr<base>&

左值和右值转换之间的区别在于 objective-c ++ 对象是否已存在并保存在 Python 对象中。例如,example.Derived左值转换至base&是可能的,因为 example.Derived持有 derived 的实例这是-a base 。另一方面,从 example.Derived 进行左值转换至std::shared_ptr<base>&不可能,因为 example.Derived持有 std::shared_ptr<derived> 的实例,它不继承自 std::shared_ptr<base> 。因此,std::shared_ptr<base>具有未指定生命周期的构造并作为右值参数传递给公开的函数。


这是一个完整的示例 demonstrating这些转换:

#include <boost/python.hpp>
#include <memory> // std::shared_ptr

class base {};
class derived: public base {};

BOOST_PYTHON_MODULE(example)
{
  namespace python = boost::python;
  python::class_<base, std::shared_ptr<base>>("Base");
  python::class_<derived, python::bases<base>, 
    std::shared_ptr<derived>>("Derived");

  python::implicitly_convertible<std::shared_ptr<derived>,
                                 std::shared_ptr<base>>();

  python::def("base_value", +[](base){});
  python::def("base_ref", +[](base&){});
  python::def("base_cref", +[](const base&){});

  python::def("shared_base_value", +[](std::shared_ptr<base>){});
  python::def("shared_base_ref", +[](std::shared_ptr<base>&){});
  python::def("shared_base_cref", +[](const std::shared_ptr<base>&){});

  python::def("derived_value", +[](derived){});
  python::def("derived_ref", +[](derived&){});
  python::def("derived_cref", +[](const derived&){});

  python::def("shared_derived_value", +[](std::shared_ptr<derived>){});
  python::def("shared_derived_ref", +[](std::shared_ptr<derived>&){});
  python::def("shared_derived_cref", +[](const std::shared_ptr<derived>&){});
}

交互式使用:

>>> base = example.Base()
>>> example.base_value(base)
>>> example.base_ref(base)
>>> example.base_cref(base)
>>> example.shared_base_value(base)
>>> example.shared_base_ref(base)
>>> example.shared_base_cref(base)
>>> 
>>> derived = example.Derived()
>>> example.base_value(derived)
>>> example.base_ref(derived)
>>> example.base_cref(derived)
>>> example.shared_base_value(derived)
>>> try:
...     got_exception = False
...     example.shared_base_ref(derived)
... except TypeError:
...     got_exception = True
... finally:
...     assert(got_exception)
...
>>> example.shared_base_cref(derived)
>>> example.derived_value(derived)
>>> example.derived_ref(derived)
>>> example.derived_cref(derived)
>>> example.shared_derived_value(derived)
>>> example.shared_derived_ref(derived)
>>> example.shared_derived_cref(derived)

关于python - Boost.Python 的继承和共享指针引用参数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36148632/

相关文章:

python - 在 Python 中访问共享内存的段错误

c++ - RH Linux 互斥锁调试

c++ - 如何使用 C++ 模板魔法来对类型进行模式匹配

c++ - 如何使用语义操作中的查找将已解析的字符串更改为 int (boost::spirit)?

python - 如何提取 Keras 层权重作为可训练参数?

python - 重新排序图像骨架坐标以使 interp1d 更好地工作

python - Django & mypy : ValuesQuerySet type hint

c++ - OpenGL - 将变换应用于 3D 空间中的多边形

c++ - 如何遍历一个0、两个0,一直到n个0的所有位串?

c++ - boost named_condition 实现