Boost.Python

时间:2016-03-22 07:40:05

标签: python c++ boost binding boost-python

我已经遇到了Boost.Python的边缘情况,看起来它应该可以工作但不会。

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

我做了一些研究并了解了implicitly_convertible,并试图用它来解决问题,但没有成功(虽然它在其他一些情况下确实有帮助)。将Derived传递给接受Base&amp ;;的功能。适用于此但如果它们被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 ++绑定代码

#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})

1 个答案:

答案 0 :(得分:3)

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

当通过boost::python::class_<T, HeldType, Bases>注册课程时,HeldType正在包裹T

  • 生成的Python类嵌入了HeldType
  • 的实例
  • T的实例注册到Python转换器到Python类的实例
  • 将来自Python转换器的 lvalue 注册为Python类的实例到T的实例
  • HeldType的实例注册到Python转换器到Python对象
  • 将来自Python转换器的 lvalue 注册为Python类的实例到HeldType的实例
  • 对于Bases中的每个基类,将来自Python转换器的 lvalue 注册到Python类的实例到基础中T的实例(不是基数{{1} }})
  • 对于HeldType中的每个多态基,从基数持有的Bases实例注册到Python转换器到Python类

使用以下设置:

T

以下 lvalue from-Python转换是可能的,因为Python对象包含C ++对象的实例:

  • 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>>(); } example.Basebasebase&const base&std::shared_ptr<base>std::shared_ptr<base>&
  • const std::shared_ptr<base>&example.Derivedbasebase&const base&derivedderived&const derived&std::shared_ptr<derived>std::shared_ptr<derived>&

以下的to-Python转换是可能的:

  • const std::shared_ptr<derived>&basestd::shared_ptr<base>
  • example.Basederivedstd::shared_ptr<derived

如果example.Derived是多态的,则可以进行以下to-Python转换:

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

由于example.Derived明确注册,因此可以进行以下 rvalue 转换:

  • implicitly_convertibleexample.Derivedstd::shared_ptr<base>

lvalue rvalue 转换之间的区别在于目标C ++对象是否已存在并且是否保存在Python对象中。例如,const std::shared_ptr<base>&example.Derived左值转换是可能的,因为base&包含example.Derived的实例,即derived }。另一方面,从baseexample.Derived左值转换是不可能的,因为std::shared_ptr<base>&拥有example.Derived的实例,该实例不会继承来自std::shared_ptr<derived>。因此,构造具有未指定生存期的std::shared_ptr<base>并将其作为 rvalue 参数传递给公开函数。

以下是这些转换的完整示例demonstrating

std::shared_ptr<base>

交互式使用:

#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>&){});
}