tags:

views:

159

answers:

4

I have a class, Fixture, that I want to cast to a 3rd party library class, b2FixtureDef. Currently, my function looks like this:

Fixture::operator b2FixtureDef() const {
    b2FixtureDef fd;
    fd.density = m_density;
    fd.friction = m_friction;
    fd.isSensor = m_isSensor;
    fd.restitution = m_restitution;
    fd.shape = &b2PolygonShape(m_polygon); // <-- the problem
    return fd;
}

Although this doesn't produce any compile-time errors, I expect this will be a problem: fd.shape is a pointer. I'm casting my polygon object, to their polygon object, and then saving that location in memory. If I'm not mistaken, this will become immediately invalid after that line finishes executing.

So what's a nice workaround for this? I could use the new operator, but my class shouldn't really be allocating memory for some other class to clean up. I could actually store the b2PolygonShape in my class, but then I essentially have two copies of the same data (my polgon object, and their polygon object); I could get rid of mine, but it's a lot easier to work with, which is why I created it in the first place. I could remove the entire function and make other classes responsible for doing the conversion themselves, but that wouldn't be consistent with my other wrappers, and it isn't a very nice solution. Is there any happy middle ground I'm overlooking?


Code now looks like this:

Fixture::operator b2FixtureDef() const {
    b2FixtureDef fd;
    fd.density = m_density;
    fd.friction = m_friction;
    fd.isSensor = m_isSensor;
    fd.restitution = m_restitution;
    fd.shape = new b2PolygonShape(m_polygon);
    return fd;
}

And gets used like this:

void Body::addFixture(const Fixture& fixture) {
    m_fixtures.append(fixture);
    b2FixtureDef fd = fixture;
    m_body->CreateFixture(&fd);
    delete fd.shape;
}

My goal was that I could just go m_body->CreateFixture(&fixture) and it would cast fixture to a b2FixtureDef and free up the resources by itself... but it doesn't look like that's possible with this stupid pointer dangling around.

A: 

It isn't a problem as long as your class derives from their class. As far as c++ is concerned a pointer is a pointer. It doesn't matter if what it points to is heap or stack allocated. If however they aren't in the same inheritance hierarchy then it won't work.

stonemetal
He's returning an object that saves a pointer to a temporary object (allocated on the stack). The `fd.shape` pointer will become invalid as soon as the object goes away. So taking the address of stack-allocated objects can be dangerous.
gavinb
gavinb has it right.
Mark
Ah, mistook that for a part of the example didn't realize he wasn't controlling his object life times properly.
stonemetal
+1  A: 

Yes, it looks like you are constructing a temporary b2PolygonShape, taking the address of that and storing it in the fd.shape member, so that is a problem - it would indeed become invalid.

Given fd.shape is a pointer, it would presumably point to something allocated on the heap anyway (without your wrapper). So what would the ordinary lifecycle of the Fixture and Polygon objects be?

It appears you are wrapping your own polygon object m_polygon and returning that. So can't you just create your own b2PolygonShape in the containing object (as a member alongside m_polygon) and return a pointer to that? Then the lifecycle would be the same as the original polygon and the object it came from. This way, even if you allocated the b2PolygonShape on the heap, you can clean it up in the destructor of your wrapper class.

gavinb
Well, like I said, then I'd essentially have two copies of the same data, which I'd prefer to avoid.
Mark
Doesn't your polygon object derive from theirs? If not, the pointers won't be compatible (eg. vtable layout, data members, etc). Assuming they are, you would already be sharing the storage.
gavinb
No... it doesn't. I'm actually working with two third party libraries... one is pretty clean and well established (Qt), the other is still undergoing rapid changes (Box2D). I chose to inherit from Qt, but not Box2D so that I wouldn't have two copies of all the internal data, and instead add an operator to do the casting. Thus, I'm casting my Polygon to the type Box2D wants.
Mark
+1  A: 

In your case it seems that heap allocation is the only way to go. You'll have to think about who has ownership of the object and make it responsible for deleting the object later.

StackedCrooked
Uhh.. I can't change the type of `fd.shape`. And you can't assign a smart pointer to a regular old pointer.... can you?
Mark
Sorry, I just realized that myself :SI modified my answer.
StackedCrooked
I suppose my other object can clean it up... breaks a little bit of encapsulation I think, but... it might be the lesser of evils.
Mark
+1  A: 

A conversion operator always has to make a copy of everything, so that the result is valid even if the original object is freed. Which means that you need to make a copy of m_polygon as b2PolygonShape and assign a pointer to it to b2FixtureDef::shape.

You can make a copy by implementing a copy constructor in b2PolygonShape or a conversion operator in whatever type m_polygon is. [...]

b2FixtureDef::shape looks like a has-a relation, so the destructor of b2FixtureDef should free it anyway, doesn't it?


Hmm, you say b2FixtureDef::shape is not freed automatically, what if you create a proxy class that has a b2FixtureDef as member and a conversion operator b2FixtureDef?

When you create the proxy object you create b2PolygonShape on the heap and fill the b2FixtureDef members just like in your code. In the conversion operator you simply return the b2FixtureDef member and in the proxy destructor you free the b2PolygonShape:

class FixtureProxy
{
  b2FixtureDef m_Fixture;
  FixtureProxy(Fixture aFixture) {m_Fixture.Shape = new b2PolygonShape(aFixture.m_polygon);...}
  ~FixtureProxy() {delete m_Fixture.Shape}
  operator b2FixtureDef() const {return m_Fixture}
}

you then use it like this

void Body::addFixture(const Fixture& fixture) {
    m_fixtures.append(fixture);
    m_body->CreateFixture(&b2FixtureDef(FixtureProxy(fixture)));
}
Ozan
`m_polygon` is of type `Polygon` (my own class). It *does* have a conversion operator, which is why I'm allowed to cast it like that, and it does indeed copy all the elements so that b2PolygonShape can exist independently.b2FixtureDef doesn't free anything, which is one of the many reasons I'm getting a bit irritated with this library; it's very unfriendly to work with. It's really just used as a temporary object to set the params to instantiate a `b2Fixture`. It's intended use is to create a b2FixtureDef on the stack, assign that to b2FixtureDef.shape, use the b2Fixture to create a Fixture,
Mark
...and then let FixtureDef and b2PolygonDef go out of scope so that they're freed AFTER the Fixture gets created (that's when it actually gets copied and then you're allowed to free it).
Mark
Err... supposed to create both b2PolygonDef and b2FixtureDef on the stack**
Mark
I see. What do you think of my proxy suggestion?
Ozan
Didn't we go in a circle here? Essentially my `Fixture` class already acts like your suggested `Proxy` class. I can certainly store a `b2PolygonShape` in my class rather than a `Polygon`, but I much prefer working with `Polygon` and don't want to have both. Looks like there's no good way out of this one.
Mark
No, FixtureProxy is only used for conversion to b2FixtureDef. When it goes out of scope the b2PolygonShape is deleted.
Ozan