具有 Boost.Python 的继承和 shared_ptr ref 参数
Inheritance and shared_ptr ref parameters with Boost.Python
我 运行 遇到了 Boost.Python 的极端情况,这似乎应该有效,但没有。
我有一个 Base 和一个 Derived class,我存储在 python 端的 std::shared_ptr
中。我想做的是将 Derived 类型 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++ 绑定代码
#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 将传递由 rvalue 产生的 temporary 对象通过对函数的 const 引用进行转换。 implicit_convertible<Source, Target>
函数注册一个 rvalue from-Python 转换。由于转换器的结果是 rvalue,因此只能通过值或常量引用来接受它。
当 class 通过 boost::python::class_<T, HeldType, Bases>
注册并且 HeldType
包装时 T
:
- 生成的 Python class 嵌入了
HeldType
的实例
- 注册到-Python从
T
实例到Python实例的转换器class
- 注册 lvalue 从-Python 转换器 Python class 到
T
[=134 的实例=]
- 注册到-Python转换器,从
HeldType
实例到Python对象
- 注册 lvalue 从-Python 转换器 Python class 到
HeldType
[=134 的实例=]
- 对于
Bases
中的每个碱基,将 lvalue 从-Python 转换器注册到 Python class 的实例基地中 T
的实例(不是基地的 HeldType
)
- 对于
Bases
中的每个多态基,注册一个从 T
实例到 Python class 的转换器到 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>>();
}
以下 lvalue 来自-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
是多态的,那么下面的 to-Python 转换是可能的:
- 动态类型
derived*
和静态类型 base*
的对象到 example.Derived
std::shared_ptr<base>
持有 derived
的实例到 example.Derived
由于通过 implicitly_convertible
显式注册,以下 rvalue 转换是可能的:
example.Derived
到 std::shared_ptr<base>
和 const std::shared_ptr<base>&
lvalue 和 rvalue 转换之间的区别在于目标 C++ 对象是否已经存在并保存在Python 对象。例如,example.Derived
到 base&
的 lvalue 转换是可能的,因为 example.Derived
持有 derived
的一个实例,它是一个 base
。另一方面,从 example.Derived
到 std::shared_ptr<base>&
的 lvalue 转换是不可能的,因为 example.Derived
持有 std::shared_ptr<derived>
的一个实例,它不继承自 std::shared_ptr<base>
。因此,构造了一个未指定生命周期的 std::shared_ptr<base>
并作为 rvalue 参数传递给公开的函数。
这里是一个完整的例子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)
我 运行 遇到了 Boost.Python 的极端情况,这似乎应该有效,但没有。
我有一个 Base 和一个 Derived class,我存储在 python 端的 std::shared_ptr
中。我想做的是将 Derived 类型 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++ 绑定代码
#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 将传递由 rvalue 产生的 temporary 对象通过对函数的 const 引用进行转换。 implicit_convertible<Source, Target>
函数注册一个 rvalue from-Python 转换。由于转换器的结果是 rvalue,因此只能通过值或常量引用来接受它。
当 class 通过 boost::python::class_<T, HeldType, Bases>
注册并且 HeldType
包装时 T
:
- 生成的 Python class 嵌入了
HeldType
的实例
- 注册到-Python从
T
实例到Python实例的转换器class - 注册 lvalue 从-Python 转换器 Python class 到
T
[=134 的实例=] - 注册到-Python转换器,从
HeldType
实例到Python对象 - 注册 lvalue 从-Python 转换器 Python class 到
HeldType
[=134 的实例=] - 对于
Bases
中的每个碱基,将 lvalue 从-Python 转换器注册到 Python class 的实例基地中T
的实例(不是基地的HeldType
) - 对于
Bases
中的每个多态基,注册一个从T
实例到 Python class 的转换器到 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>>();
}
以下 lvalue 来自-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
是多态的,那么下面的 to-Python 转换是可能的:
- 动态类型
derived*
和静态类型base*
的对象到example.Derived
std::shared_ptr<base>
持有derived
的实例到example.Derived
由于通过 implicitly_convertible
显式注册,以下 rvalue 转换是可能的:
example.Derived
到std::shared_ptr<base>
和const std::shared_ptr<base>&
lvalue 和 rvalue 转换之间的区别在于目标 C++ 对象是否已经存在并保存在Python 对象。例如,example.Derived
到 base&
的 lvalue 转换是可能的,因为 example.Derived
持有 derived
的一个实例,它是一个 base
。另一方面,从 example.Derived
到 std::shared_ptr<base>&
的 lvalue 转换是不可能的,因为 example.Derived
持有 std::shared_ptr<derived>
的一个实例,它不继承自 std::shared_ptr<base>
。因此,构造了一个未指定生命周期的 std::shared_ptr<base>
并作为 rvalue 参数传递给公开的函数。
这里是一个完整的例子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)