Best way to deal with an object that can't be instantiated?

I have probably asked a few similar questions before, but I was hitting around the bush. I think this is a real problem that I cannot completely calm down.

I deal with a third-party library , and there is an object that can not create itself b2Body

. b2World

should instantiate . I personally don't like this design pattern; I think I b2Body

should be able to exist independently of the world and then be added to the world when needed. Anyway, I wrapped b2Body

my class with a class Body

because I still need to add extra things to it. Likewise, I have a wrapper World

. Now I suppose I have 3 options:

  • The constructor Body

    takes a pointer to World

    so that it can be fully instantiated (called b2World::CreateBody

    somewhere inside) - i.e. has a constructor likeBody *b = new Body(world_ptr)

  • Pass Body

    to some method World::CreateBody

    , for example, how the library already does it - i.e.Body *b = world.CreateBody(params);

  • Duplicate all the data in b2Body

    so you can use it however you want, and then after adding it to the world, it will "switch" to use the data b2Body

    - ie. Body b

    and later world.addBody(b)

    .

(1) and (2) means you can't have Body

without World

, which I probably won't need, but it would be nice to have this option [so I can use this as a template for other objects, etc.]. Not sure what other pros and cons are. (3) seems nicer, but there is a lot more work to do, and that means I have to duplicate most of the data that is already in the b2Body

.

What are your thoughts? I'll make CW

sure no one gets along.


I still cannot calm this down. Here's what each of the options would look like:

Option 1: (which I prefer)

World w;
Body b;
Fixture f;
b.addFixture(f);
w.addBody(b);

      

Option 2: (somewhere in between)

World w;
Body b(w);
Fixture f(b);

      

Option 3: (how Box2D does it)

World *w = new World;
Body *b = w->CreateBody(args);
Fixture *f = b->CreateFixture(args);

      

Choices 2 and 3 are not all that different, but they change who has control over the creation of objects.

How would I actually implement option 3? World::CreateBody()

should call b2World::CreateBody(args)

, which calls b2Body::b2Body()

and returns b2Body

, but never calls Body::Body(args)

, which is the problem. b2Body

will be fully initialized, but my wrapper has nowhere to do this ... Specifically, how would I write World::CreateBody(const BodyDef &bd)

? Assuming BodyDef inherits from b2BodyDef, Body from b2Body, World from b2World, etc.

+2


source to share


5 answers


I think if you are going to use a third party library you should only struggle with its design, if you have a much better reason than oh, I don't like this design pattern. There is a way in your library to do things - presumably with a factory object - and struggle, which will increase your code complexity, perhaps significantly.



+6


source


It looks like the b2World object is a factory for b2Body, so the author decided that b2Body doesn't make sense without his world.

My first reaction will be that this is an interface, so live with it. Let your World object be the factory for your body. So, to approximate (1), except that you don't have a public constructor, the World object has a makeBody () method.

Do you think Bodies without Peace make sense? If so, you might find that some subsets of body methods might be useful without a world, I don't understand how you would implement them - they obviously cannot be implemented by b2Body because it cannot exist without b2World. of the possibilities is that you have a set of configuration information

 class Body {
        int howBig;
        String name;
        Flavour flavour;
        // and getter/setters
 } 

      



Now these (or in the east, vagrants) could clearly make sense with or without the World.

With this in mind, I think you may find that you have two "states" of the Body, one when it is not connected with the World, one when it is. And the actual possibilities are different . Hence, you actually have two interfaces.

So you have an IndependentBody class and a Body class. World factory method can be signed

World {

    Body makeBody(IndependentBody);

}

      

+2


source


I agree that you shouldn't struggle with the design of the third party library you are using. Directing this path can cause many problems in the future.

By exploring under the covers and creating wrappers, you can block the behavior of the third party library the way the current implementation does.

What happens if a future version of the API remains the same, but the underlying semantics change?

Suddenly everything is broken in terms of your wrapper.

Only my 0.02.

+1


source


Following your link I see that createBody

b2Body does not return, but a pointer to one:

 b2Body* b2World::CreateBody  ( const b2BodyDef*  def );     

      

This is probably due to the fact that b2World

  • manages the lifetime of b2Body (i.e. removes it and the memory it uses when B2World goes out of scope / itself is removed) or

  • Since B2Wsorld needs to support pointers to b2Bodies for example. to iterate over them to accomplish some of the functionality of B2World.

I also note that all it takes (except b2World

) to create b2Body

is a pointer to b2BodyDef

.

So, if you want b2Body not tied to b2World, but can be tied to one later, why not go through b2BodyDefs or pointers to them?

I could create a thin wrapper for b2BodyDef, for example:

 class b2BodyDefWrapper {
   public const b2BodyDef& b2bodyDef;
   public b2BodyDefWrapper( const b2BodyDef& bodydef ) : b2bodyDef(bodydef) {}
   public const b2Body* reifyOn( b2World& world) const { 
     return world.CreateBody( b2bodyDef ) ;
   }
 }

      

Note that I could attach this b2BodyDefWrapper to multiple worlds or to the same world more than once.

Now it may be that you can do something with b2Body that you cannot do with b2BodyDef and therefore traversing the (possibly wrapped) b2BodyDefs is not suitable for your purposes. In this case, I can use the Command Pattern to "attach" a list of functions to b2BodyDefWrapper

that will "play" on each reified b2Body:

 class b2BodyDefWrapper {
   private std::vector<Command&> commandStack;
   public const b2BodyDef& b2bodyDef;
   public b2BodyDefWrapper( const b2BodyDef& bodydef ) : b2bodyDef(bodydef) {}
   public const b2Body* reify( b2World& world) const { 
     b2body* ret = world.CreateBody( &b2bodyDef ) ;
     for (int i=0; i< commandStack.size(); i++) {
        v[i].applyTo( ret ) ;
     }
     return ret;
   }

   public void addCommand( const Command& command ) {
      commandStack.push_back( command );
   }
 }

      

Where Command

is the abstract base class for functors, for example:

  class Command {
     virtual ~Command() {}
     virtual void applyTo( b2Body* body ) = 0 ;
  }

      

with specific subclasses:

 class ApplyForce : public Command {
   private const b2Vec2& force;
   private const b2Vec2& point;
   ApplyForce(const b2Vec2& f, const b2Vec2& p) : force(f), point(p) {}
   virtual void applyTo( b2Body* body ) {
      body->ApplyForce( force, point ) ;
   }
 }

      

Then I could use my wrapper like this:

extern b2BodyDef& makeb2BodyDef();
b2BodyDefWrapper w( makeb2BodyDef()  ) ; 
ApplyForce a( ..., ... );
w.addCommand( a ) ;
...
b2World myworld;
b2World hisWorld;
w.reifyOn( myWorld ) ;
w.reifyOn( hisWorld) ;

      

Please note that I am not covering some details, mainly about object ownership and memory management, and who calls delete in CommandStacks; I also didn't follow the three-fold rule in my class sketches. You can fill them in as you like.

I also left any condition for calls from Command, b2Body functions that return non-void and return these values; you can probably cover this (if you need to) if ApplyTo returns some kind of union.

More fundamentally, I haven't talked about how one particular Command can provide its return value (if any) to another specific Command. The complete solution would be to not have Vector of Commands, but an n-ary tree of them, where the child commands are applied first and their return values ​​(if any) are passed to their parent command. If you want that kind of complexity, this is a question I obviously cannot answer. (And I have already given a rather detailed answer, considering that I am not getting paid to do this, and I am not getting the reputation as you, the Wiki'd Community, are asking this question.)

+1


source


One of the reasons box2D uses bodyDef objects to create b2Body objects is because you can reuse def to create multiple bodies. Code:

b2BodyDef myDef;
// fill out def

for (int i=0; i < 100; ++i) {
   for (int j=0; j < 100; ++j) {
      myDef.x = i;
      myDef.y = j
      b2Body* body = world.CreateBody(myDef)
   }
}

      

This is a very efficient and compact way to create many objects with the same characteristics. It also doesn't have to be in the same loop, you can maintain def objects as metadata and create bodies from them as needed.

Don't fight him because you are not.

+1


source







All Articles