Boost.Python:来自 python 中C++对象:无法添加到基类列表中C++
Boost.Python: inheret from C++ object in python: Cannot add to C++ base class list
我正在尝试通过继承来扩展Python中现有的C++对象。我可以成功地做到这一点,并运行在 Python 中覆盖的虚拟方法。但是,当我尝试将 python 对象添加到 C++ Base 对象类型(python 类已覆盖的基本对象(的指针列表中时,我收到一个类型错误:"尝试附加无效类型">
我确信此错误是由于从派生*到base*开始没有"implicitly_convertible"功能。在C++中,这将定义为:implicitly_convertible<[Derived_from_base],Base>((;。是否可以在python中定义它?
我怎样才能做到这一点?
下面是重现此行为的示例代码。
C++
struct Base {
virtual ~Base() {}
virtual int f() = 0;
};
struct A {
std::vector<Base*>& GetBaseList() { return m_base_List; }
std::vector<Base*> m_base_List;
};
struct BaseWrap : Base, wrapper<Base> {
int f() { return this->get_override("f")(); }
};
BOOST_PYTHON_MODULE(sandbox)
{
class_<BaseWrap, Base*, boost::noncopyable>("Base", no_init)
.def("f", pure_virtual(&Base::f));
class_<A, A*>("A", init<>())
.add_property("baseList", make_function(&A::GetBaseList, return_internal_reference<>()));
//implicitly_convertible<[Derived_from_base]*,Base*>();
class_<std::vector<Base*>>("BaseList").def(vector_indexing_suite<std::vector<Base*>>());
}
蟒 从沙盒导入 *
class derived(Base):
def __init__(self):
self.name = "test"
def f(self):
print("Hello Derived!")
d = derived()
d.f() # Output: Hello Derived!
a = A()
a.baseList.append(d) # TypeError: Attempting to append an invalid type
任何帮助或想法将不胜感激。
BaseList.append()
函数接收具有正确类型的参数;但是,该参数具有不适当的值。 在 Python 中,derived
初始值设定项不会初始化其层次结构的sandbox.Base
部分。 这会导致 Boost.Python 对象不包含C++ BaseWrap
对象。 因此,当BaseList.append()
尝试提取C++ BaseWrap
对象时,它会失败并引发错误。
class derived(Base):
def __init__(self):
self.name = "test"
# Base is not initialized.
def f(self):
print("Hello Derived!")
d = derived()
d.f() # `derived.f()` is resolved through Python's method-resolution-order.
# It is not invoking `BaseWrap::f()`.
a = A()
a.baseList.append(d) # d does not contain a BaseWrap object, so this throws.
要解决此问题,请在derived.__init__()
中显式调用Base.__init__()
:
class derived(Base):
def __init__(self):
self.name = "test"
Base.__init__(self)
但是,尝试这样做将暴露有关BaseWrap
公开方式的其他问题:
sandbox.Base
类必须是可从 Python 构造的,因此绑定不能提供boost::python::no_init
作为其初始值设定项规范。 通常,仅当C++对象从C++显式实例化并传递给 Python 时,例如通过工厂函数,才需要使用boost::python::no_init
。- 当
T
BaseWrap
时,Base*
的HeldType
不能满足HeldType
的要求。 特别是,HeldType
要么需要:BaseWrap
,一个派生自BaseWrap
的类,要么是boost::python::pointee<Base*>::type
BaseWrap
的可取消引用类型,要么是派生自BaseWrap
的类。 有关要求详细信息,请参阅class_
规范。
这些问题可以通过公开类来解决,如下所示:
namespace python = boost::python;
python::class_<BaseWrap, boost::noncopyable>("Base", python::init<>())
.def("f", python::pure_virtual(&Base::f))
;
下面是一个完整的示例,演示如何将派生自C++公开类的对象传递给通过vector_indexing_suite
公开的C++向量:
#include <vector>
#include <boost/python.hpp>
#include <boost/python/suite/indexing/vector_indexing_suite.hpp>
struct base
{
virtual ~base() {}
virtual int perform() = 0;
};
struct base_wrap: base, boost::python::wrapper<base>
{
int perform() { return int(this->get_override("perform")()) - 10; }
};
BOOST_PYTHON_MODULE(example)
{
namespace python = boost::python;
python::class_<base_wrap, boost::noncopyable>("Base", python::init<>())
.def("perform", python::pure_virtual(&base::perform))
;
python::class_<std::vector<base*>>("BaseList")
.def(python::vector_indexing_suite<std::vector<base*>>())
;
python::def("do_perform", +[](base* object) {
return object->perform();
});
}
交互式用法:
>>> import example
>>> class derived(example.Base):
... def __init__(self):
... self.name = "test"
... example.Base.__init__(self)
... def perform(self):
... return 42
...
>>> d = derived()
>>> base_list = example.BaseList()
>>> base_list.append(d)
>>> assert(len(base_list) == 1)
>>> assert(base_list[0].perform() == 42)
>>> assert(example.do_perform(base_list[0]) == 32)
对于集合和指针,通常会有一些警告。 在这种情况下:
BaseList
对象对其元素引用的对象没有共享所有权。 请注意保证容器引用的对象生存期至少与容器本身一样长。 在上面的示例中,如果删除了对象d
,则调用base_list[0].perform()
可能会导致未定义的行为。- 不能遍历
base_list
,因为迭代器的值将尝试执行base*
到Python的转换,而该转换不存在。
上面的例子还演示了函数调度的差异。 如果Python可以直接调用一个方法,它将使用自己的方法解析机制来实现。 请注意 base_list[0].perform()
和 example.do_perform(base_list[0])
如何返回不同的值,因为一个值通过操作结果的base_wrap::perform()
调度,而另一个则不返回。
在原始代码中:
class derived(sandbox.Base):
...
def f(self):
print("Hello Derived!")
d = derived()
d.f()
由于Python知道derived.f()
,调用d.f()
不会通过BaseWrap::f()
调度。 如果BaseWrap::f()
被调用,它会抛出,因为derived.f()
返回None
,这将无法转换为int
:
struct BaseWrap : Base, wrapper<Base> {
int f() { return this->get_override("f")(); }
// ^~~ returns a boost::python::object, faling to
// extract `int` will throw.
};
- 将成员变量添加到共享库中的类中,不会破坏二进制兼容性吗
- 如何在C++中从两个单独的for循环中添加两个数组
- POCO::PostgreSQL:如何将std::vector支持添加到`Binder::bind`
- 如何仅为一个函数添加延迟
- 如何防止 c++ 在从浮点型转换为双精度型(不适用于 IO)时添加额外的小数?
- 使用std::transform将一个范围的元素添加到另一个范围中
- 如何将更多文件夹添加到c++include路径
- 如何将元素添加到数组的线程安全函数?
- QT通过C++添加映射QML项目
- 如何将点击的信号和插槽添加到qt中的自定义按钮中
- 如何使用重载的相等(==)运算符向测试用例添加描述
- 为什么Mat类的两个对象可以在不重载运算符+的情况下添加
- 如何防止clang格式在流运算符调用之间添加换行符<<
- 只能向C++添加一定数量的字符
- Qt和C++:将QLineEdit添加到QTabWidget中
- 将QIcon添加到QTableView单元格
- 为什么我必须在C++中添加一个赋值符号来声明一个数组
- 为什么除非添加括号,否则构造函数上的模板替换会失败?
- 将图像添加到资源文件夹UWP C++
- 当我尝试添加 2 个大字符串时,我无法弄清楚出了什么问题