tags:

views:

53

answers:

3

Hello,

I am quite new to real use of templates, so I have the following design question.

I am designing classes Bunch2d and Bunch4d that derive from a abstract base class Bunch:

class Bunch {virtual void create()=0;};
class Bunch2d : public Bunch {void create();};
class Bunch4d : public Bunch {void create();};

The class Bunch will contain a container, a deque or a vector (see this question: Choice of the most performant container (array)) of Particle's:

typedef Blitz::TinyVector<double,DIMENSIONS> Particle;

You therefore see my question: Bunch has to contain this container, because the "base" operations on my bunch are "dimension independant" (such a "size of the container", "clear container", etc.), so I think that the container belongs to the base class ("Bunch 'has a' container).

But this container has to know the dimensions (2 or 4) of the derived class.

So my idea would be to use a templated base class to give the typedef the correct dimension of the container:

enum Dimensions {TwoDimensions = 2, FourDimensions = 4, SixDimensions = 6};
template<Dimensions D> class Bunch
{
  protected:
    typedef Blitz::TinyVector<double,D> Particle;
    std::deque<Particle> particles_store;
  public:
    virtual void create() = 0;
    virtual ~Bunch();
};

class Bunch2d : public Bunch<TwoDimensions>
{
  public:
    ~Bunch2d();
    void create();
};

class Bunch4d : public Bunch<FourDimensions>
{
  public:
    ~Bunch4d();
    void create();
};

Can you give me your opinion on this design ? Would it be correct use of templates ? What about the validity of the OO concepts ? With a templated base class ?

Thanks for you help/answer/opinion.

+2  A: 

There is one single note: different template instances (ie template classes with different types in the parameters) are of different types, and therefore are NOT a single base class.

If you need polymorphism, you will need to add a layer in your design:

class Bunch
{
public:
  virtual void create() = 0;
  virtual ~Bunch();
};

template <Dimensions D>
class TBunch: public Bunch
{
private:
  typedef Blitz::TinyVector<double,D> Particle;
  std::deque<Particle> mParticles;
};

class Bunch2d : public TBunch<TwoDimensions>
{
public:
  ~Bunch2d();
  void create();
};

On another note: protected should be banned for attributes.

The issue is one of coupling, since protected exposes the attributes / methods to an unknown number of classes, it's no different than public in that it's impossible to reliably state how many methods will be affected by a change of implementation.

For methods, it's acceptable, because methods can be kept backward compatible (sometimes at the cost of some tricks / etc... but still).

For attributes, it's just unacceptable because an attribute is an implementation detail, not an interface, and a change cannot be made backward compatible.

Therefore I urge you not to EVER use protected for an attribute. In this particular case, it would be a good idea to factor the accesses to mParticles in the template class, without exposing the underlying implementation.

Small hint: if you cannot switch between deque and vector without breaking something beyond the class that holds them, then you have a design issue.

Matthieu M.
But the problem is that the 'Particles' contained in the container (of millions of elements) have to be manipulated by the derived classes, and it doesn't seem a good idea to make it private, and then access each element one by one. Or I could use a function pointer, or function object and give it to TBunch, inside TBunch a method will loop over the elements and apply the function to them. It can keep it private. Would it be a good idea ?
Cedric H.
It's perfectly acceptable to use either a function pointer or a predicate and a `TBunch::foreach` method, yes. I did not recommended to hide the fact that you use `Particles`, simply to hide how you store them which is an implementation detail. You could also provide a `ParticleIterator`, wrapping the `std::deque<Particle>::iterator`. Boost.Iterator provides such facilities as iterator_adaptor and iterator_facade.
Matthieu M.
+2  A: 

You'd then loose the ability to have a pointer of Bunch class pointing to either Bunch2d or Bunch4d objects at runtime, and manipulate those objects polymorphically through that pointer. If it's important to you not to loose that, don't make the base class templated. Otherwise there is no point in having virtual functions and abstract base class here at all, so then I'd recommend going just with the template.

usta
A: 

For a start, Bunch<TwoDimensions> and Bunch<FourDimensions> are completely unrelated classes, so far as inheritance is concerned. Therefore, Bunch2d and Bunch4d have no common base class!

If this is going to be a problem for you, you'll have to do away with the templating, and have DIMENSIONS parameterised at run-time.

Oli Charlesworth