抽象 C++ 类不能以这种方式暴露给 Boost.Python。 Boost.Python tutorial 给出了如何公开纯虚函数的示例。简而言之,当使用boost::python::pure_virtual装饰方法时,需要创建一个包装器类型以允许C++对虚函数进行多态解析,而虚函数实现将在Python对象的层次结构中委托多态解析函数。
struct BaseWrap : Base, boost::python::wrapper<Base>
{
int foo()
{
return this->get_override("foo")();
}
};
...
boost::python::class_<BaseWrap>("Base", ...)
.def("foo", boost::python::pure_virtual(&Base::foo))
;
详细来说,当一个类型通过boost::python::class_公开时,HeldType默认为被公开的类型,HeldType是在Python对象中构造的。 class_ 文档指出:
模板参数:
-
T: 被包装的类
-
HeldType:指定实际嵌入在包装 T 实例 [...] 的 Python 对象中的类型。默认为T。
因此,boost::python::class_<Base> 将失败,因为T = Base 和HeldType = Base,Boost.Python 将尝试将HeldType 的对象实例化为代表Base 实例的Python 对象。此实例化将失败,因为Base 是一个抽象类。
这是一个完整的例子,展示了BaseWrap 类的使用。
#include <boost/python.hpp>
struct Base
{
virtual int foo() = 0;
virtual ~Base() {}
};
struct Derived : public Base
{
virtual int foo()
{
return 42;
}
};
Base* get_base()
{
return new Derived;
}
namespace python = boost::python;
/// @brief Wrapper that will provide a non-abstract type for Base.
struct BaseWrap : Base, python::wrapper<Base>
{
BaseWrap() {}
BaseWrap(const Base& rhs)
: Base(rhs)
{}
int foo()
{
return this->get_override("foo")();
}
};
BOOST_PYTHON_MODULE(example)
{
python::class_<BaseWrap>("Base")
.def("foo", python::pure_virtual(&Base::foo));
;
python::def("get_base", &get_base,
python::return_value_policy<python::manage_new_object>());
}
及其用法:
>>> import example
>>> class Spam(example.Base):
... pass
...
>>> Spam().foo()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
RuntimeError: Pure virtual function called
>>> class Egg(example.Base):
... def foo(self):
... return 100
...
>>> e = Egg()
>>> e.foo()
100
>>> d = example.get_base()
>>> d.foo()
42
可以在 Boost.Python 中公开一个抽象类,方法是在没有默认初始化程序 (boost::python::no_init) 和不可复制 (boost::noncopyable) 的情况下公开它。缺少初始化程序会阻止 Python 类型从它派生,从而有效地防止覆盖。此外,Base::foo() 由Derived 在 C++ 中实现的实现细节无关紧要。如果 Python 根本不应该知道 foo() 方法,则省略通过 def() 公开它。
#include <boost/python.hpp>
struct Base
{
virtual int foo() = 0;
virtual ~Base() {}
};
struct Derived
: public Base
{
virtual int foo()
{
return 42;
}
};
struct OtherDerived
: public Base
{
virtual int foo()
{
return 24;
}
};
Base* get_base()
{
return new Derived;
}
Base* get_other_base()
{
return new OtherDerived;
}
BOOST_PYTHON_MODULE(example)
{
namespace python = boost::python;
python::class_<Base, boost::noncopyable>("Base", python::no_init)
;
python::class_<Derived, python::bases<Base> >("Derived", python::no_init)
.def("foo", &Base::foo)
;
python::class_<OtherDerived, python::bases<Base> >(
"OtherDerived", python::no_init)
;
python::def("get_base", &get_base,
python::return_value_policy<python::manage_new_object>());
python::def("get_other_base", &get_other_base,
python::return_value_policy<python::manage_new_object>());
}
互动使用:
>>> import example
>>> b = example.get_base()
>>> b.foo()
42
>>> b = example.get_other_base()
>>> b.foo()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'OtherDerived' object has no attribute 'foo'