While both languages provide a solution to the diamond problem, they use different approaches. C++ removes ambiguity with virtual inheritance, causing a single subobject to be shared. On the other hand, Python removes ambiguity with a monotonic superclass linearization lookup. With Python inheritance not causing C++ types to share a single subobject, Boost.Python would need to emulate C++ virtual inheritance when a Python class inherits from two exposed C++ classes that share a common virtual base. Unfortunately, as best as I can tell, Boost.Python does not support this case, as the types provided to boost::python::bases<...>
are not checked for virtual inheritance.
While not scalable and a possible indicator that it may be worth examining composition-based solutions, one simple inheritance solution is to expose a CollidableSprite
C++ class that inherits from ICollidableWrap
and Sprite
, then have the Python class inherit from CollidableSprite
.
Here is a basic example where class C
inherits from B
and A
that virtual inherit from V
:
#include <boost/python.hpp>
/// @brief Mock up forming the following multiple inheritance
/// structure:
///
/// .-->[ V ]<--.
/// | |
/// [ A ] [ B ]
/// | |
/// '---[ C ] --'
struct V
{
V() : x(0) {}
virtual ~V() {}
unsigned int x;
};
struct A : virtual public V {};
struct B : virtual public V {};
struct C : public A, public B {};
/// @brief Functions that force an object's hierarchy chain to be
/// split, disambiguating access to V.x.
void print_a(A& a) { std::cout << a.x << std::endl; }
void print_b(B& b) { std::cout << b.x << std::endl; }
BOOST_PYTHON_MODULE(example)
{
namespace python = boost::python;
// Expose hierarchy.
python::class_<V>("V", python::no_init)
.def_readwrite("x", &V::x)
;
python::class_<A, python::bases<V> >("A");
python::class_<B, python::bases<V> >("B");
python::class_<C, python::bases<A, B> >("C");
// Expose helper functions.
python::def("print_a", &print_a);
python::def("print_b", &print_b);
}
Interactive usage:
>>> import example
>>> class PyC(example.A, example.B):
... def __init__(self):
... example.A.__init__(self)
... example.B.__init__(self)
...
>>> c = PyC()
>>> example.print_a(c)
0
>>> example.print_b(c)
0
>>> c.x = 42
>>> example.print_a(c)
0
>>> example.print_b(c)
42
>>> class PyC(example.C):
... pass
...
>>> c = PyC()
>>> example.print_a(c)
0
>>> example.print_b(c)
0
>>> c.x = 42
>>> example.print_a(c)
42
>>> example.print_b(c)
42