Boost logo

Boost :

From: Joaquin M Lopez Munoz (joaquin_at_[hidden])
Date: 2006-04-26 18:06:09


SeskaPeel <seskapeel <at> gmail.com> writes:

>
> Hi list,
>
> I'm looking for a "multi-class container", with access to sub-containers of
> elements depending on their class. All elements are derived from the same
> base class, and I want to have "views" of containers of final classes, or
> even intermediate classes. This container would be associative, like a map.
>
> As instance if I have
>
> // Base class
>
> class A {} ;
>
> // Intermediate classes
>
> class B : public A {} ;
>
> // Final classes
>
> class C : public B {} ;
>
> class D : public B {} ;
>
> class E : public A {} ;
>
> I want a container that can hold any A-derived class, on which I could have
> views of sub containers of class C, D or E, and even a sub container of
> intermediate class B.
>
> I looked at multi-index, but no such feature seems to be feasible. And I
> have no need of multiple indices, as I access all theses elements by their
> ID which is a member of A.
>

Hello,

With a little imagination, you can use Boost.MultiIndex,
and specially the key extraction framework it provides,
to achieve something similar to what you want (if I'm
getting you right):

******BEGIN CODE******
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/identity.hpp>
#include <boost/multi_index/member.hpp>
#include <boost/multi_index/composite_key.hpp>
#include <boost/shared_ptr.hpp>
#include <iostream>
#include <typeinfo>
#include <utility>

struct A
{
  A(int id):id(id){}
  virtual ~A(){}
  
  int id;
};

struct B:A
{
  B(int id):A(id){}
};

struct C:A
{
  C(int id):A(id){}
};

struct D:A
{
  D(int id):A(id){}
};

struct E:A
{
  E(int id):A(id){}
};

using namespace boost::multi_index;

template<typename Base>
struct type_info_extractor
{
  typedef std::type_info result_type;
  
  template<typename T>
  const std::type_info& operator()(const T& t)const
  {
    // identity<> is used so that we can accept chained
    // pointers to Base as argument.
    
    identity<Base> i;
    return typeid(i(t));
  }
};

struct type_info_compare
{
  bool operator()(
    const std::type_info& x,const std::type_info& y)const
  {
    return x.before(y);
  }
};

typedef multi_index_container<
  boost::shared_ptr<A>,
  indexed_by<
    // This index sorts by typeid, then by id.
    // Use it to extract subviews based on the
    // class of the element.
    
    ordered_non_unique<
      composite_key<
        A,
        type_info_extractor<A>,
        member<A,int,&A::id>
>,
      composite_key_compare<
        type_info_compare,
        std::less<int>
>
>,
    
    // Regular index which does not differentiate
    // between element typeid's.
    ordered_non_unique<
      member<A,int,&A::id>
>
>
> multi_class_container;

template<typename InputIterator>
static void dump(InputIterator first,InputIterator last)
{
  while(first!=last){
    std::cout<<(*first++)->id<<" ";
  }
  std::cout<<std::endl;
}

template<typename InputIterator>
static void dump(
  const std::pair<InputIterator,InputIterator>& p)
{
  dump(p.first,p.second);
}

int main()
{
  typedef boost::shared_ptr<A> element_ptr;
  
  // populate the container
  
  multi_class_container m;
  m.insert(element_ptr(new A(0)));
  m.insert(element_ptr(new A(1)));
  m.insert(element_ptr(new A(2)));
  m.insert(element_ptr(new B(3)));
  m.insert(element_ptr(new B(4)));
  m.insert(element_ptr(new C(5)));
  m.insert(element_ptr(new C(6)));
  m.insert(element_ptr(new D(7)));
  m.insert(element_ptr(new E(8)));
  m.insert(element_ptr(new E(9)));

  // extract the different views based on typeid
  
  std::cout<<"A: ";dump(m.equal_range(typeid(A)));
  std::cout<<"B: ";dump(m.equal_range(typeid(B)));
  std::cout<<"C: ";dump(m.equal_range(typeid(C)));
  std::cout<<"D: ";dump(m.equal_range(typeid(D)));
  std::cout<<"E: ";dump(m.equal_range(typeid(E)));
  
  // do a normal query without discriminating typeid's
  
  std::cout<<"not less than five: ";
  dump(m.get<1>().lower_bound(5),m.get<1>().end());
}

******END CODE******

The idea is that composite_key<> can be used to have the
elements sorted first by their class type, then by id,
so that subviews are easily achieved by looking up for
the typeid of the class you're interested in. If you
still need typeid-agnostic semantics, just add additional
indices like the second index in the example above.
What this approach can't do is to produce subviews
of intermediate types as you also request, since typeid(x)
returns a type_info object referring to the final class
of x.

Does this help?

Joaquín M López Muñoz
Telefónica, Investigación y Desarrollo


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk