As far as I know, there is no way to access a class without exposing the class. Boost.Python requires the static types to be exposed. However, it is possible to obtain virtual function resolution to instance whose dynamic type has not been exposed through Boost.Python, as long as the static type has been exposed. For example, if:
Base
andBase::foo()
exposed to Boost.PythonDerived
inherits fromBase
Derived
overridesBase::foo()
Then a handle to an instance of Derived
can be passed through Boost.Python with a static type of Base*
or Base&
, and invoking foo()
on the resulting Python object will resolve to Derived::foo()
.
Here is a complete code example:
#include <boost/python.hpp>
struct Base
{
virtual int foo() = 0;
virtual ~Base() {}
};
struct Derived
: public Base
{
int foo()
{
return 42;
}
};
/// @brief Factory method to create Derived objects, but return as
/// Base*. If the instance was returned as a Derived*, then
/// Derived would need to be directly exposed to Boost.Python.
Base* make_derived()
{
return new Derived;
}
namespace python = boost::python;
/// @brief Wrapper that will provide a non-abstract type for Base.
struct BaseWrap
: public Base, public python::wrapper<Base>
{
int foo()
{
return this->get_override("foo")();
}
};
BOOST_PYTHON_MODULE(example)
{
// Exposes BaseWrap in Python as Base. This also causes C++ Base to be
// indirectly exposed for conversions via Boost.Python's wrapper.
python::class_<BaseWrap, boost::noncopyable>("Base")
.def("foo", python::pure_virtual(&Base::foo))
;
// Make an instance of Derived, but return the instance as Base*. As
// Base is indirectly exposed via the wrapper, a handle to the object
// is passed through the Boost.Python layer.
python::def("make_derived", &make_derived,
python::return_value_policy<python::manage_new_object>());
}
Interactive usage:
>>> import example
>>> d = example.make_derived()
>>> d.foo()
42
>>> class Spam(example.Base):
... def foo(self):
... return 101
...
>>> s = Spam()
>>> s.foo()
101