0

I'm trying to process classes instance two by two. I have a abstract base class (IBase here) that contains a doStuff method. This method will be overriden in extended class in order to process all other defined classes.

This is part of a library I'm building. I want Base objects to be written by the library user. Each Base class need to interact with another Base objects through the doStuff methode. The container is needed to handle multiples Base objects.

It is not the first time I run into this problem, but I can't remember how I did the last times. This kind of class can be used for a lot of thing. Here, it is a collision detection system. IBase represent an abstract HitBox and Container represente the Scene where collision occures. In this case, Container::process checks for transitions between hit boxes and Container::process is used to implement the optimizing algorithm (quadtree, ...).

I built those class in this way:

class IBase {
    public:
        virtual void doStuff(IBase* base) = 0;
}

class Base {
    public:
        virtual void doStuff(Base* base) {
            foobar();
        }
        virtual void doStuff(IBase* base) {
            // irrelevant
        }
}

class Container {
    public:
        void process() {
            for (std::list<IBase*>::iterator it=base_list.begin() ; it!=base_list.end() ; it++) {
                for (std::list<IBase*>::itarator jt=std::next(it) ; jt!=base_list.end() ; jt++) {
                    (*it)->doStuff(*jt);
                }
            }
        }
    private:
        std::list<Ibase*> base_list;
}

But in the loop, I can't reach void Base::doStuff(Base*) when working with two Base objects. I can only call Base::doStuff(IBase*) which is not something I want.

Any help on this one ? I understand the problem, but I can't see a solution to it. Is this the good way to handle it or do I need to think again my architecture ? How would you do this ? I think a design pattern must exists for such a problem, but I didn't find any that fits.

Thanks

John Smith
  • 1,256
  • 1
  • 9
  • 10
  • 1
    I don't see any Base objects in `process()`. Am I missing something? – zindorsky Jun 10 '13 at 19:57
  • It seems that you are trying to create a Container holding a list of Base* objects (or pointers to objects derived from IBase), am I correct ? And then iterate over the list and call polymorphic doStuff ? You should be able to do this, just declare your virtual doStuff() public (or make Container a friend of IBase) – AlexK Jun 10 '13 at 20:02
  • 3
    I smell an [XY problem](http://meta.stackexchange.com/questions/66377/what-is-the-xy-problem) . What are you *really* trying to do? – John Dibling Jun 10 '13 at 20:12
  • Looks like you really want [double-dispatch](http://stackoverflow.com/questions/3262811/what-is-single-and-double-dispatch), sometimes implemented as the [Visitor Pattern](https://en.wikipedia.org/wiki/Visitor_pattern). – Peter Wood Jun 10 '13 at 20:16

2 Answers2

2

C++ does not support contravariance for arguments. See also Why is there no parameter contra-variance for overriding?.

You might be better off explicitly invoking doStuff(Base* base) from within the doStuff(IBase* base) body.

Community
  • 1
  • 1
Gabriele Giuseppini
  • 1,541
  • 11
  • 19
1

Your objects, when dereferenced from *it and *jt, are referenced as IBase objects, not Base objects. This means that only methods from IBase can be called.

Your virtual method:

virtual void doStuff(Base* base) { ... }

is not overriding anything. It is creating a new virtual method that is accessible from Base downward only. When you call doStuff from a IBase pointer, it's going to call:

virtual void doStuff(IBase* base) { ... }

which matches the signature defined in IBase.

If you want to execute your foobar function, you should do some kind of check on base when it's based into the overriding doStuff, cast it to Base* once you're sure it's safe, then work with it as needed.

virtual void doStuff(IBase* base) {
  // not irrelevant
  if (base->isBase()) 
  {
    foobar();
  }
}

And finally, as previously suggested, make doStuff public.

Matt Houser
  • 33,983
  • 6
  • 70
  • 88