0

I have three classes which adhere to the same concept class but differ on the underlying data structure used for storage. As an illustration take the three classes given below.

template< typename T >
class A{
  std::vector<T> storage;
  //etc
};

template<>
class A<bool>{
  boost::dynamic_bitset<> storage;
  //etc
};

class B{
   ComplexUDT storage;
   //etc
};

Class A is the generic class that uses a vector. To avoid the use of vector<bool> a full specialisation of class A is provided that uses the boost::dynamic_bitset<> as the underlying storage. Finally, Class B uses a user defined data type as storage. The above design leads to a lot of redundancy in code. To remove this redundancy I thought of using boost::mpl

template< typename T >
class vec_impl{
   std::vector< T >  storage;
   //impl of methods
};

class bitset_impl{
   boost::dynamic_bitset<> storage;
   //impl of methods
 };

 class udt_impl{
    ComplexUDT storage;
    //impl of methods
 };

 template<Derived,Storage>
 class main_class_gen{
     typedef typename boost::mpl::if_<is_complexudt<Storage>,
                                      udt_impl,
                                      typename boost::mpl::if_< is_bool<Storage>,
                                                                bitset_impl,
                                                                vec_impl<Storage> >::type >::type type
 };

 class main_class:
                  public main_class_gen< main_class<Storage>,Storage >::type{
        //impl. of methods
 };

As boost::dynamic_bitset does not model a Container the implementation of some of the class methods are different than the vector class. The complexUDT bases class is very different from the two other classes but do have some small code segements common. But the current refactoring approach still results in code redundancy. All the methods mentioned in the concept class have to implemented in the each of the implementations.

So I my question is two fold.

  1. How can we implement the Builder design pattern using boost::mpl ?
  2. Would CRTP help in any way in the above example ?
Samrat Roy
  • 105
  • 8

1 Answers1

1

If i understand correctly what you wish to do, this may work for you:

First of all, every containers must implement the same interface. Create adapters as needed (for ComplexUDT for example):

struct AdapterUDT {
  typedef double const_reference;
  void push_back( double );
  private: ComplexUDT complexUDT;
};

While it is generally not recommended, you could subclass dynamic_bitset to add/modify functionality. Just be aware that you cannot use it polymorphically because it lacks a virtual destructor. Ensure that it's not used elsewhere.

struct AdapterBitset : dynamic_bitset<> { // not recommended
  typedef my_biterator iterator;
  AdapterBitset( int n ) : dynamic_bitset(n) {}
  iterator begin();
};

Define traits to associate types with containers:

template<typename T> struct Trait {
  typedef vector<T> type;
};
template<> struct Trait<bool> {
  typedef dynamic_bitset<> type;
};

Create a template that implements the interface for the Storage (minus the container):

template<class T, class Derived> struct Base {
  typedef Base<T,Derived> base_t;
  typedef typename Trait<T>::type container_type;
  typedef typename container_type::const_reference const_reference;
  void append( const_reference value ) {
    // static polymorphism to access the standardized containers/adapters
    static_cast<Derived*>(this)->container.push_back(value);
  }
  // etc
};

Since you need constructors based on the storage type, the Storage template has to manage the instantiation:

template<class T> struct Storage : Base<T,Storage<T>> {
  friend struct base_t;
  protected: container_type container;
};
// specialize for types with ctor arguments:
template<> struct Storage<bool> : Base<bool,Storage<bool>> {
  friend struct base_t;
  Storage( int bits ) : container(bits) {}
  protected: container_type container;
};

Usage example:

Storage<int> astore; // generic version uses default ctor
astore.append(314);
Storage<bool> bstore(7); // bool specialization requires an int
bstore.append(true);

This should reduce most of the redundant code. If this isn't what you're looking for, i need a more concrete example of the problem you wish to solve.

Anonymous Coward
  • 6,186
  • 1
  • 23
  • 29
  • Thank you for your reply! This is indeed a cleaner way of implementing but `ComplexUDT` has parameterised ctors. Delegation of construction would needs a factory method which unfortunately cannot be given in the ctor of `GenericStorage` or is there another way which I am not aware of ? – Samrat Roy Oct 23 '12 at 12:02
  • Addendum to my update: if your compiler supports C++11, you could use a variadic template for the constructor. – Anonymous Coward Oct 23 '12 at 13:35
  • @Samrat i have revised my answer. Please check if this is more like what you had in mind. – Anonymous Coward Oct 27 '12 at 15:18