我在 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.Base
至base
,base&
,const base&
,std::shared_ptr<base>
,std::shared_ptr<base>&
,和const std::shared_ptr<base>&
-
example.Derived
至base
,base&
,const base&
,derived
,derived&
,const derived&
,std::shared_ptr<derived>
,std::shared_ptr<derived>&
,和const std::shared_ptr<derived>&
可以进行以下到 Python 的转换:
-
base
或std::shared_ptr<base>
至example.Base
-
derived
或std::shared_ptr<derived
至example.Derived
如果base
是多态的,那么以下到 Python 的转换是可能的:
- 动态类型为
derived*
的对象和静态类型base*
至example.Derived
-
std::shared_ptr<base>
持有derived
的实例至example.Derived
由于通过implicitly_convertible
显式注册,以下右值转换是可能的:
-
example.Derived
至std::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/