这个问题特定于使用pybind11库将C ++连接到python。问题在于包装C ++类,这些类参与多重继承,并在python中使用用户定义的持有者类型(而不是unique_ptr或shared_ptr)。
假设有两个纯抽象接口类, A 和 B ,它们声明 foo()和 bar()< / strong>纯粹的抽象方法。 C 类公开继承自 A 和 B ,并实现 foo()和 C(<)< / strong>即可。类 MyHolder 是一个自定义的非拥有智能指针,类似于原始指针,但没有实现指针算术和其他操作符的安全性。
此外,还有两个功能, A 和 B 包含在 MyHolder 中。代码在C ++中按预期工作,但在从python调用时抛出运行时错误。
我尝试将代码示例尽可能地减少。因此,python示例实际上泄漏了内存。由于这不会影响可重复性,因此我更倾向于使用更少的代码来对付更正确的代码。
#include <iostream>
#include <memory>
template< typename T >
class MyHolder
{
public:
explicit MyHolder( T* ptr = nullptr ) : ptr_( ptr ) {}
template< typename U > MyHolder( MyHolder< U > ptr ) :
ptr_( dynamic_cast< T* >( ptr.get() ) ) {}
T* get() const { return ptr_; }
T* operator-> () const { return ptr_; }
private:
T* ptr_;
};
class A
{
public:
virtual ~A() = default;
virtual void foo() const = 0;
};
class B
{
public:
virtual ~B() = default;
virtual void bar() const = 0;
};
class C : public A, public B
{
public:
C() = default;
virtual ~C() = default;
virtual void foo() const override
{
std::cout << "foo" << std::endl;
}
virtual void bar() const override
{
std::cout << "bar" << std::endl;
}
};
void testA( MyHolder< A > ptr )
{
ptr->foo();
}
void testB( MyHolder< B > ptr )
{
ptr->bar();
}
#include <pybind11/pybind11.h>
#include "Test.h"
PYBIND11_DECLARE_HOLDER_TYPE( T, MyHolder< T >, true );
PYBIND11_MODULE( test, m )
{
pybind11::class_< A, MyHolder< A > >( m, "A" )
.def( "foo", &A::foo );
pybind11::class_< B, MyHolder< B > >( m, "B" )
.def( "bar", &B::bar );
pybind11::class_< C, MyHolder< C >, A, B >( m, "C" )
.def( pybind11::init<>() );
m.def( "testA", &testA );
m.def( "testB", &testB );
}
#include "Test.h"
int main()
{
auto uptr = std::unique_ptr< C >( new C() );
auto c = MyHolder< C >( uptr.get() );
testA( c );
testB( c );
return 0;
}
输出:
FOO
杆
import test
c = test.C()
test.testA( c )
test.testB( c )
输出:
TypeError:testA():不兼容的函数参数。支持以下参数类型:
1.(arg0:test.A) - &gt;无
调用:test.C对象位于0x ...
答案 0 :(得分:0)
我也在pybind11问题页面上发布了这个帖子,并收到了一个答案,我将在这里为其他遇到同样问题的用户复制。
jagerman的回答:
这里的问题是,当涉及多重继承时,我们依赖于与std :: shared_ptr的别名构造函数(此处为构造函数(8))兼容的构造函数,这允许我们创建包含重铸指针的持有者(即保持原始C *活着的A *)(如果它最终成为最后一个对象,则销毁C *而不是A *)。
如果你添加:
MyHolder( const MyHolder &other, T* ptr ) : ptr_(ptr) {}
该示例将起作用。 (当然,你的示例持有者并没有做任何实际的内存管理;实际上它需要支持上面描述的那种别名)。