user3423757
user3423757

Reputation: 23

Vector of pointers to base type, find all instances of a given derived type stored in a base type

Suppose you have a base class inside of a library:

class A {};

and derived classes

class B: public A {};
class C: public A {};

Now Instances of B and C are stored in a std::vector of boost::shared_ptr<A>:

std::vector<boost::shared_ptr<A> > A_vec;
A_vec.push_back(boost::shared_ptr<B>(new B()));
A_vec.push_back(boost::shared_ptr<C>(new C()));

Adding instances of B and C is done by a user, and there is no way to determine in advance the order, in which they will be added.

However, inside of the library, there may be a need to perform specific actions on B and C, so the pointer to the base class needs to be casted to B and C.

I can of course do "trial and error" conversions, i.e. try to cast to Band C(and any other derivative of the base class), until I find a conversion that doesn't throw. However, this method seems very crude and error-prone, and I'm looking for a more elegant (and better performing) way.

I am looking for a solution that will also work with C++98, but may involve boost functionality.

Any ideas ?


EDIT:

O.k., thanks for all the answers so far!

I'd like to give some more details regarding the use-case. All of this happens in the context of parametric optimization.

Users define the optimization problem by:

Different optimization algorithms then act on the problem definitions, including their parameters.

There is a number of predefined parameter objects for common cases, but users may also create their own parameter objects, by deriving from one of my base classes. So from a library perspective, apart from the fact that the parameter objects need to comply with a given (base-class) API, I cannot assume much about parameter objects.

The problem definition is a user-defined C++-class, derived from a base-class with a std::vector interface. The user adds his (predefined or home-grown) parameter objects and overloads a fitness-function.

Access to the parameter objects may happen

This works fine.

There may however be special cases where

Under these circumstances it would be great to have an easy method to access all parameter objects of a given derived type inside of the collection of base types.

I already have a templated "conversion_iterator". It iterates over the vector of base objects and skips those that do not comply with the desired target type. However, this is based on "trial and error" conversion (i.e. I check whether the converted smart pointer is NULL), which I find very unelegant and error-prone.

I'd love to have a better solution.

NB: The optimization library is targetted at use-cases, where the evaluation step for a given parameter set may last arbitrarily long (usually seconds, possibly hours or longer). So speed of access to parameter types is not much of an issue. But stability and maintainability is ...

Upvotes: 2

Views: 374

Answers (5)

sehe
sehe

Reputation: 393934

This answer might interest you: Generating an interface without virtual functions?

This shows you both approaches

  • variant w/visitor in a single collection
  • separate collections,

as have been suggested by others (Fred and Konrad, notably). The latter is more efficient for iteration, the former could well be more pure and maintainable. It could even be more efficient too, depending on the usage patterns.

Upvotes: 0

tsragravorogh
tsragravorogh

Reputation: 3173

I would suggest to implement a method in the base class (e.g. TypeOf()), which will return the type of the particular object. Make sure you define that method as virtual and abstract so that you will be enforced to implement in the derived types. As for the type itself, you can define an enum for each type (e.g. class).

enum class ClassType { ClassA, ClassB, ClassC };

Upvotes: 0

fredoverflow
fredoverflow

Reputation: 263360

Adding instances of B and C is done by a user, and there is no way to determine in advance the order, in which they will be added.

Okay, so just put them in two different containers?

std::vector<boost::shared_ptr<A> > A_vec;
std::vector<boost::shared_ptr<B> > B_vec;
std::vector<boost::shared_ptr<C> > C_vec;

void add(B * p)
{
    B_vec.push_back(boost::shared_ptr<B>(p));
    A_vec.push_back(b.back());
}

void add(C * p)
{
    C_vec.push_back(boost::shared_ptr<C>(p));
    A_vec.push_back(c.back());
}

Then you can iterate over the Bs or Cs to your hearts content.

Upvotes: 1

aschepler
aschepler

Reputation: 72473

If possible, add virtual methods to class A to do the "specific actions on B and C".

If that's not possible or not reasonable, use the pointer form of dynamic_cast, so there are no exceptions involved.

for (boost::shared_ptr<A> a : A_vec)
{
    if (B* b = dynamic_cast<B*>(a.get()))
    {
        b->do_something();
    }
    else if (C* c = dynamic_cast<C*>(a.get()))
    {
        something_else(*c);
    }
}

Upvotes: 2

Konrad Rudolph
Konrad Rudolph

Reputation: 546133

There’s no better general solution than trying to cast and seeing whether it succeeds. You can alternatively derive the dynamic typeid and compare it to all types in turn, but that is effectively the same amount of work.

More fundamentally, your need to do this hints at a design problem: the whole purpose of a base class is to be able to treat children as if they were parents. There are certain situations where this is necessary though, in which case you’d use a visitor to dispatch them.

Upvotes: 5

Related Questions