views:

81

answers:

5

Dear reader,

I noticed that I'm often in the need of a container class. For example when working on a particle system, I create a container class Particles which has a member vector<Particle*>. Then I call: Particles* my_particles like my_particles->draw(), and in the Particles.draw() I iterator over the vector<Particle*> and call draw() on each of the particles again. The same works for member functions like update(), addforce() etc.. Now, I'm working on a project and need a collection of Cube on which I need to call tween(), moveTowards() etc..

I know I can use template, but in the case of a template class the member functions need to be knows before. As I want to check if I can make a generic class, that I can use for example both my Cubes and Particles collections.

Someone who has done this before or can give me some advice on this?

Kind regards, Pollux

+1  A: 

Not sure I definitely understand, but would the for_each STL algorithm help? http://www.sgi.com/tech/stl/for_each.html

pdbartlett
Thanks! As Stephen also suggested, I think this is what I need, or would be the best solution.
pollux
+4  A: 

The short answer is that you can't do this in c++. You can, however, use STL algorithms and containers to wrap this behavior up.

First, you'd put your Cube or Particle instances into a std::vector or other container (like you have now).

Then you'd use STL's std::for_each in combination with std::mem_fun.

It'd result in something like this:

  std::vector<Particle*> V;

  V.push_back(new Particle);
  V.push_back(new Particle);
  V.push_back(new Particle);
  V.push_back(new Particle);

  std::for_each(V.begin(), V.end(), std::mem_fun(&Particle::draw));
Stephen
Hi Stephen! Thanks, I think this is exactly what I was looking for, or I think this is the best solution. What is the difference between this and using a iterator and looping over the Particles in a loop? Maybe only performance? (I can imagine using a custom for loop would be a bit faster, which can be important when using thousands of particles). One more question, is there a way to pass parameters as well?
pollux
Actually, for_each will be just as good or even faster than your handwritten loop if you're invoking functions. mem_fun adds a probably negligible overhead. Passing parameters is tricky since for_each only knows how to pass the object itself as a parameter; you need to create a function-like object that receives the parameter via other means. boost::bind can help hugely with this kind of thing, also at a probably negligible overhead.
Owen S.
@pollux : This will probably be as fast as a custom for loop, and not likely dependent on the number of particles. The compiler will be able to generate very similar code, but the `for_each` might not be inlined, resulting in one more function call. The only way to be sure is benchmark. WRT passing parameters, `std::mem_fun` can't really support it, but the `boost` libraries can http://www.boost.org/doc/libs/1_32_0/libs/bind/mem_fn.html used with http://www.boost.org/doc/libs/1_32_0/libs/bind/bind.html . Hope that helps.
Stephen
Would this work considering that we have pointers to `Particle` here ? Don't you need a reference to invoke the function from mem_fun on ?
Matthieu M.
@Matthieu : According to STL documentation it will. `mem_fun_ref` is for when you're storing the objects.
Stephen
@Stephen: thanks, I never played much with those :)
Matthieu M.
A: 

As far as I understood, there is a design problem here, you want to iterate over different object categories (Cube/Particle) using the same interface, but it's pretty clear that they couldn't share the same interface, and if you really want that, you must implement the tween() and moveTowards() on the abstract base class of Particles and Cube, and implement nothing in the Particles class.

Tarantula
I don't think he's trying to put Particles and Cubes in the same container, he's just wants to avoid repeating all the methods in the container object.
Stephen
+1  A: 

I read you asking: "can I make a generic container that can be used for both cubes and particles, even though they have different member functions?" Sure, that's the easy part. You can even put both cubes and particles in the same container if you want. If you're dealing with pointers, you just use void*:

std::vector<void*> objects;
objects.push_back(new Particle(...));
objects.push_back(new Cube(...));

Of course, there's not much you can do with void*s, except cast them back:

for (i = objects.begin(), i != objects.end(), ++i) {
    void* p = objects[i];
    Particle* particle = dynamic_cast<Particle*>(p);
    if (particle) {
        // do particle stuff
        continue;
    }
    Cube* cube = dynamic_cast<Cube*>(p);
    if (cube) {
        // do cube stuff
    }
}

And even if you're only storing Particles in your vector, say, you still have to downcast to work with them:

for (i = objects.begin(), i != objects.end(), ++i) {
    void* p = objects[i];
    Particle* particle = dynamic_cast<Particle*>(p);
    if (particle) {
        // do particle stuff
    } else {
        // error!! I thought someone told me this thing only had Particles...
    }
}

You can see that doing this is much more awkward than just storing them in separate vectors where you know the type of each of the objects in each vector and don't have to perform runtime downcasts to work with them. This is why this kind of container is often considered poor style.

Other possibilities for you to look at in this area are boost::any or boost::variant, which work on things besides pointers.

Owen S.
Hi Owen, this wasn't exactly what I meant. I don't want to create one container and then check their type in the loops. Though it's nice to see this is possible.
pollux
A: 

For completeness, there is also valarray, though using vector/for_each is a better solution.

BlueRaja - Danny Pflughoeft