Pergunta

In a game I'm working on I have two C++ classes (ICollidable and Sprite) which both virtually inherit another C++ class called Object, for its properties Position and Size. I have these three classes exposed to python with boost python. The Player class constructor in python looks like this for example:

class Player(Game.ICollidable, Game.Sprite, Game.IKeyboardListener, Game.IEntity):
    def __init__(self):
        Game.IKeyboardListener.__init__(self)
        Game.IEntity.__init__(self)    
        Game.ICollidable.__init__(self)
        Game.Sprite.__init__(self)

The problem is that when the Sprite init comes before ICollidable's init the Sprite position doesn't work correctly, and I cant reposition the sprite on the screen. I think whats happening is that there are separate instances of the position from Sprite and ICollidable which is what I thought would happen before I made the ICollidable class.

I've tried a variety of things to try and solve this problem and scoured the internets to find solutions to a problem like this one but I've come up empty handed. Any advice, solutions or hints would be greatly appreciated.

Edit:

These are the python bindings to my C++ classes:

class_<Object>("GameObject")
    .def_readwrite("Pos", &Object::pos)
    .def_readwrite("Size",&Object::size)
;
class_<Sprite, bases<Object>>("Sprite", init<>())
    // Sprite(TexID, Pos)
    .def(init<GLuint, glm::vec2>())
    // ----------------------------
    .def_readwrite("TexId", &Sprite::texid)
;
class_<ICollidableWrap, bases<Object>,boost::noncopyable>("ICollidable", init<>())
    //.def("Pos", &ICollidable::Object::pos)
    .def("Test", pure_virtual(&ICollidable::Test))
    .def("OnCollision",pure_virtual(&ICollidable::OnCollision))
;
Foi útil?

Solução

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
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top