0

I want to create class inheritance, where my new class creates object of parent class, setting some default parameters (which are pointers to other objects).

At the beginning I had:

btDefaultCollisionConfiguration* collisionConfiguration =
        new btDefaultCollisionConfiguration();
btCollisionDispatcher* dispatcher = new btCollisionDispatcher(collisionConfiguration);
btVector3 worldAabbMin(-1000,-1000,-1000);
btVector3 worldAabbMax(1000,1000,1000);

btAxisSweep3* broadphase = new btAxisSweep3(worldAabbMin,worldAabbMax);

colWorld = new btCollisionWorld(dispatcher,broadphase,collisionConfiguration);
colWorld->setDebugDrawer(dbgdraw);

I want to have something like:

colWorld = new myBtCollisionWorld(dbgdraw);

I tried many things and invented a trick

btVector3 worldAabbMin;
btVector3 worldAabbMax;

class BtOgreCollisionWorld_initializer { //A trick: we need one more constructor to
    // initialize default variables; Then we will pass them to btCollisionWorld
    // as it stands next in inheritance list
public:
    BtOgreCollisionWorld_initializer(btCollisionDispatcher *&dispatcher,
        btAxisSweep3 *&broadphase,
        btDefaultCollisionConfiguration *&collisionConfiguration)
    {
        if (!worldAabbMin) worldAabbMin = btVector3(-1000,-1000,-1000);
        if (!worldAabbMax) worldAabbMax = btVector3(1000,1000,1000);
        if (!collisionConfiguration)
            collisionConfiguration = new btDefaultCollisionConfiguration();
        if (!dispatcher)
            dispatcher = new btCollisionDispatcher(collisionConfiguration);
        if (!broadphase) broadphase = new btAxisSweep3(worldAabbMin,worldAabbMax);
    };
};

class BtOgreCollisionWorld: public BtOgreCollisionWorld_initializer,
        public btCollisionWorld {
public:
    //using btCollisionWorld::btCollisionWorld;
    BtOgreCollisionWorld(BtOgre::DebugDrawer *dbgdraw,
        btCollisionDispatcher* dispatcher = 0, btAxisSweep3* broadphase = 0,
        btDefaultCollisionConfiguration* collisionConfiguration = 0)
        : BtOgreCollisionWorld_initializer(dispatcher, broadphase,
            collisionConfiguration),
        btCollisionWorld(dispatcher, broadphase, collisionConfiguration)
    {
        /*copying variables above*/
        this->setDebugDrawer(dbgdraw);
    };
protected:
    btDefaultCollisionConfiguration* collisionConfiguration;
    btCollisionDispatcher* dispatcher;
    btVector3 worldAabbMin;
    btVector3 worldAabbMax;
    btAxisSweep3* broadphase;
};

The idea behind this was that "initializer" class constructor is called first and overwrites the variables for btCollisionWorld class.

And suddenly it worked.

I understand that it may seem as an attempt to start a pointless discussion, but I'm not skilled at c++ and really haven't found anything like that while googled for it; so I'm just curious if there are any possible pitfalls or other ways to implement this.

Deduplicator
  • 44,692
  • 7
  • 66
  • 118
Slowpoke
  • 1,069
  • 1
  • 13
  • 37
  • 1
    It would really help a lot if you'd trim your example down to the minimum required with brief names, and made it compilable. – Benjamin Bannier Feb 19 '15 at 18:39
  • I'm very confused.... why do you have a whole separate parent class to essentially just be a constructor? Why not just make the constructor of `BtOgreCollisionWorld` do the initialization work? – aruisdante Feb 19 '15 at 18:40
  • 1
    Put the code to initialize the variables directly in `btCollisionWorld`'s initialization list call, if that's what you want to do. – Neil Kirk Feb 19 '15 at 18:44
  • aruisdante, I wanted to create an instance with some default parameters. I don't really know how to do it, because standard way of inheritance needs the same parameters to be passed to child and parent constructor. Object "btCollisionWorld" cannot be created without parameters, and I can't set just something in declaration, because I need to create them as additional objects. – Slowpoke Feb 19 '15 at 18:46
  • Neil Kirk, thank you! That's why I asked this question - to have any advice. – Slowpoke Feb 19 '15 at 18:51
  • Neil Kirk. but my parameters are initialized one-by-one, btCollisionDispatcher depends on collisionConfiguration, so I need them in some namespace outside class, right? – Slowpoke Feb 19 '15 at 18:52
  • @hcl: If you use `@3-characters-minimum`, you will notify the editor, commenter, closing mod (diamond) or dupe-hammer-closer (sun). Use this power wisely. Also, take better care of properly presenting your question, which includes good formatting for code and prose. – Deduplicator Feb 19 '15 at 18:55

2 Answers2

2

It seems too overcomplicated. Inheriting from a class which sets some hard-coded values doesn't looks great. If you really want the derived class to be logically separate (based on the init parameters) it would better to template it. It will allow you to create more such classes based on different initialization params.

vcargats
  • 41
  • 1
  • 2
  • The source library is big and complicated, suppose I have declared a template from btCollisionWorld class, how can I create a derived class with given parameters without initializing them in outer namespace? – Slowpoke Feb 19 '15 at 19:19
  • Do you mean something like this class derived_btCollisionWorld : btCollisionWorld ? – vcargats Feb 19 '15 at 19:33
  • I just want to create an instances of "btCollisionWorld", let it be "derived_btCollisionWorld" 's with my default parameters and additional functions. My parameters "dispatcher" and "collisionConfiguration" depend on each other, so they require some constructor that will declare them. Of course, I wish to override defaults if needed, just placing them as arguments: `colWorld = new myBtCollisionWorld(dbgdraw, , mybroadphase);` – Slowpoke Feb 19 '15 at 19:43
  • One more question: do I need to copy the code of btCollisionWorld from source library to create template, or there is a way to create a template of existing class in my program? The structure of source library is very complicated with many connections, and I haven't used templates before. – Slowpoke Feb 19 '15 at 20:10
  • Short answer, I would suggest to do what you are confident with. It's not crucial as I get it, and also it doesn't look like you are developing a library, which will be re-used many times by many programmers, so go ahead – vcargats Feb 19 '15 at 20:17
1

Here is what I understand (tell me if I'm wrong, I'll delete the answer):

Your design seems very complicated. At first I thought that you didn't make use of the inheritance. But then I realised that the problem should be more complex. Here is what I understood:

You have a base class whith a constructor that is dependent on its environment (for example some external variables). I try with a simpler example:

Dispatcher* dispatcher = new Dispatcher(xyz);  // global parameter
class World {
private: 
    Dispatcher *my_dispatcher;     // some private that you can't change later on
public: 
    World() { 
        my_dispatcher = dispatcher;  // take over the global.  
        ...
    }
    ...
};

You then want to derive a class, with a constructor parameter that should affect the environment for the base class.

class DWorld : public World {
public: 
    DWorld(Dispatcher* d) : World() { 
        dispatcher = d;  // too late:  the base is already created !!!    
        ...
    }
    ...
};

This logic is defined in the standard:

  • the base class part is always constructed before the derived class
  • first the base initializers are executed (potentially initializing base, if not it's default base constructor) then the other members, and only then the constructor's body instructions.

In short: there is no way the DWorld could do something before initializing the base !

Here is how your trick works:

So your trick is to use multiple inheritance. It's clever !

  class Helper {
  public: 
      Helper (Dispatcher* d) {
           dispatcher = d; 
           // do whatever you want to do before World base is intialized    
           }
  };
  class DWorld : public Helper, public World {
public: 
    DWorld(Dispatcher* d) : Helper(d), World() { 
        ...  // everything is fine
    }
    ...
};

This is not coincidence: this is also defined very precisely in the C++ standard: in case of multiple inheritance, the base initializers operate in the order in which you defined them in the inheritance statement. So here, first Helper then World.

However you then have to deal with multiple inheritance, which can be difficult, especially if the same helper is needed at several different layers of the inheritance tree.

Now another trick:

Unfortunately, it works under one condition: your base constructor must take at least one parameter.

Suppose I had a constructor World(int a). In this case I could a helper function avoiding multiple inheritance:

int helper(Dispatcher*d, int a) {
    dispatcher = d;  
    return a; 
}   
class DWorld : public World {   // single inheritance 
public:
DWorld(Dispatcher* d) : World(helper(0)) { }   // but function pipeline !
};

This works using the fact that the function must be evaluated first in order to call the World constructor.

The variant with lambda functions, although less readable, permits you even to capture any variables in scope and use them as best fits:

DWorld(Dispatcher* d) : World([&]()->int {dispatcher = d; return 0;  }()) {...}

Conclusions

You have here some tools to solve your immedite problem. However a trick remains a trick. It's a workaround. he best solution would be to redesign the base class.

Christophe
  • 68,716
  • 7
  • 72
  • 138
  • You're not wrong, although you solved more complicated(I suppose) task: in my case I have "dispatcher" passed to "World()" as an argument, but as long as I want to create multiple instances with different parameters, I want to keep them in my derived instance. Plus, in my case there are few parameters that depend on each other and must be declared in some order. – Slowpoke Feb 19 '15 at 20:22
  • 1
    I understand the approach. But the constraints of the construction process do not facilitate its implementation. This why many APIs use a two step approach: 1)objects get constructed, 2) initialising function is called. In step2 you are then compeltely free. But I agree, this is not always possible. – Christophe Feb 19 '15 at 20:39