views:

124

answers:

2

Hi there,

I'm trying to write a container class using boost::ptr_vector. Inside the ptr_vector I would like to include different classes. I'm trying to achieve that using static templates, but so far I'm not able to do that. For example, the container class is

class model {
private:
  boost::ptr_vector<elem_type> elements;
public:
  void insert_element(elem_type *a) {
element_list.push_back(a);
  }
};

and what I'm trying to achieve is be able to use different elem_type classes. The code below doesn't satisfy my requirements:

template <typename T>class model {
private:
  boost::ptr_vector<T> elements;
public:
  void insert_element(T *a) {
element_list.push_back(a);
  }
};

because when I initialize the container class I can only use one class as template:

model <elem_type_1> model_thing;
model_thing.insert_element(new elem_type_1)

but not elem_type_2:

model_thing.insert_element(new elem_type_2)//error, of course

It is possible to do something like using templates only on the member?

 class model {
private:
 template <typename T> boost::ptr_vector<T> elements;
public:
  void insert_element(T *a) {
element_list.push_back(a);
  }
}; //wrong

So I can call the insert_element on the specific class that I want to insert? Note that I do not want to use virtual members. Thanks!

A: 

Vector is contains elements where each element has the same type as others. If you want to create vector of elements of different classes you could use vector of elements of type boost::any.

Kirill V. Lyadvinsky
When using boost::any, I have to typecast by hand all the return elements from the boost::ptr_vector, what is not desirable.
Ivan
@Ivan: As far as I know, in C++, there is no way for a heterogenous collection (i.e. "bag") to return an element of a specific, non-polymorphic type (without the user specifying the type). Your options are to return a reference to the element's base class (polymorphism), use variant types (boost::any or boost::variant), use the Visitor pattern, or use template member functions where the template parameter specifies the return type.
Emile Cormier
That is a big downside for C++, as it fits all the other requirements for my simulation - speed, easy coding, easy maintenance. I will do some tests with the code you provided using variants and the visitor, with the functor for visiting. But if this shows problematic, maybe is easier to use the same approach and use Fortran? Speed is a very important factor, only precision is more important.
Ivan
+1  A: 

Try using a vector of boost::variant:

#include <iostream>
#include <vector>
#include <boost/variant.hpp>
#include <boost/foreach.hpp>

struct Foo
{
    Foo(int v=0) : a(v) {}
    int a;
};

struct Bar
{
    Bar(int v=0) : b(v) {}
    int b;
};

struct print_visitor : public boost::static_visitor<>
{
    void operator()(const Foo& foo) const
    {
        std::cout << "Foo " << foo.a << "\n";
    }

    void operator()(const Bar& bar) const
    {
        std::cout << "Bar " << bar.b << "\n";
    }
};

int main()
{
    typedef boost::variant<Foo, Bar> Variant;
    std::vector<Variant> bag;
    bag.push_back(Foo(123));
    bag.push_back(Bar(456));

    BOOST_FOREACH(const Variant& element, bag)
    {
        boost::apply_visitor(print_visitor(), element);
    }
}

boost::variant's apply_visitor functions are useful for avoiding excessive casting back to the original type.

Emile Cormier