11
\$\begingroup\$

The following program is a demonstration of variant and double visitation used to obtain double dispatch between the elements of two inhomogeneous containers. Most of the work is done at compile time. What do you think about it compared to the dynamic version in explained in Wikipedia?

boost::variant does not employ variadic templates and has a hand coded limit to 20 different types which can be easily extended to 50 but not beyond. Is it a crazy idea to use this pattern (with an appropriate variant implementation) with more than 50 types, say 100 spaceships and 10 different asteroids or so?

#include <iostream>
#include <vector>
#include "boost/variant.hpp"
using std::cout;
using std::endl;

////////// some basic objects

class SpaceShip {};
class ApolloSpacecraft : public SpaceShip {};

class Asteroid {
public:
  void CollideWith(SpaceShip&) 
       { cout << "Asteroid hit a SpaceShip" << endl; }
  void CollideWith(ApolloSpacecraft&) 
       { cout << "Asteroid hit an ApolloSpacecraft" << endl; }
};

class ExplodingAsteroid : public Asteroid {
public:
  void CollideWith(SpaceShip&) 
       { cout << "ExplodingAsteroid hit a SpaceShip" << endl; }
  void CollideWith(ApolloSpacecraft&)
       { cout << "ExplodingAsteroid hit an ApolloSpacecraft" << endl; }
};
////////// visitor

struct CollideVisitor : public boost::static_visitor<void> {
  template <typename A, typename S>
  void operator()( A & a, S & s) const { a.CollideWith(s);}
};

////////// demo

int main() {
  std::vector<boost::variant<Asteroid,ExplodingAsteroid>> asteroids;
  asteroids.emplace_back(Asteroid());
  asteroids.emplace_back(ExplodingAsteroid());

  std::vector<boost::variant<SpaceShip,ApolloSpacecraft>> spaceships;
  spaceships.emplace_back(SpaceShip());
  spaceships.emplace_back(ApolloSpacecraft());

  for (auto & a : asteroids) {
    for (auto & s : spaceships) {
      boost::apply_visitor(CollideVisitor(), a, s);
    }
  }

  return 0;
}
\$\endgroup\$
2
\$\begingroup\$

It's a good example of multimethods in C++. But you should be aware that variant takes at least as much space as the biggest of its argument. It's done so to minimize overhead of walking an additional pointer. Probably, you need a variant with another allocation policy, as asteroids and spaceships are quite numerous.

But, to be serious, there's an even bigger problem: extension. You would like to

  • define new functions that do something with asteroids, spaceships or tuples of them;
  • define new types of asteroids and spaceships;
  • modify code only locally.

Your solution needs to have variant<list, of, all, objects, of, the, kind> in several places of your code in addition to defining a new classes for new types of game objects. That's a non-local change.

\$\endgroup\$
  • \$\begingroup\$ Yes: since I'm not reassigning the variant often (probably never) I'm planning to shrink the memory to the type that is actually contained. Regarding the second point is enough to typedef variant<list, of, all, objects, of, the, kind> objects at some global scope and I'll just have to change one line ;) \$\endgroup\$ – DarioP Jun 6 '14 at 13:13
2
\$\begingroup\$

This is exactly a very good implementation of using a variant to do a Collision detection between multiple objects. Thats exactly how I do it in my projects for multibody simulations having a variant of different geometry shared pointers (boost::variant<shared_ptr<Sphere>, shared_ptr<Cube> >) (because a body can share a geometry with other bodies) and I then use two body's variant's to dispatch into the right collision routine as you have done in your example. I don't know about the performance actually but it should be reasonably fast since the dispatch is done with a switch statement and can be optimized, hm... You can easily use a typedef to adjust your variant if you want to add more types.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.