Ответ 1
Абстрактные классы С++ не могут быть показаны таким образом на Boost.Python. Boost.Python tutorial дает примеры того, как выставлять чистые виртуальные функции. Короче говоря, при декорировании методов с помощью boost::python::pure_virtual
необходимо создать тип-оболочку, чтобы позволить С++ полиморфно разрешать виртуальную функцию, а реализация виртуальной функции будет делегировать функцию полиморфно в иерархии объектов 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
: указывает тип, который фактически встроен в объект Python, обертывающий экземплярT
[...]. По умолчаниюT
.
Следовательно, boost::python::class_<Base>
завершится с ошибкой, так как T = Base
и HeldType = Base
, а Boost.Python попытается создать объект HeldType
в объект Python, который представляет экземпляр Base
. Это экземпляр не будет выполнен, так как 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
, несущественна. Если 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'