Question

I'm trying to solve the following problem. I have a vector (it's a custom structure actually, but a vector is a good enough substitute for this issue) of pointers to a custom class A. Class A can actually store either a type_a pointer or a type_b pointer (these types are quite different and are not related to each other). Right now that's implemented by keeping both, setting them to NULL and then having a bunch of if/else statements later on to check which type it is and perform appropriate action.

class A {
public:
  A() : p1(NULL), p2(NULL) {}

  type_a * p1;
  type_b * p2;
};

std::vector<A *> v;

...

if (v[0]->p1 != NULL) { /* do this */ }
else if (v[0]->p2 != NULL) { /* do that */ }

I plan to add more pointers to class A, and so the above is starting to become a hassle. The solution that I'm currently trying to make work is using boost::variant instead, to have:

class A {
public:
  boost::variant<type_a*, type_b*> p;
};

The problem I have encountered though, is that one of my actions involves calling a function that would assign some values to a variable depending on what type of p I have. This is what it is now and the appropriate process_stuff function is called inside one of the above if/else statements:

class B { /*...*/ };

void process_stuff(type_a * p, B * b) {
  b->m_var = p->fn1();
}

void process_stuff(type_b * p, B * b) {
  b->m_var = p->fn2();
}

I can't get this to work with boost::static_visitor since (as far as I understand) I can't have a non-variant type as an argument in binary visitation, nor can I have a non-const operator() to make the second variable a member of the visitor class and modify that inside operator() with unary visitation. So I'm confused how to convert the above process_stuff function to play along with boost::variant.

Btw I'm not attached to boost::variant and would take other solutions.

Was it helpful?

Solution

You just need a stateful visitor. I'm typing this from a vague memory of exactly what visitors look like, but you should be able to fix any mistakes I make.

class process_stuff_visitor : public boost::static_visitor<void> {
  B* m_b;

public:
  process_stuff_visitor(B* b) : m_b(b) {}
  void visit(type_a* a) const { m_b->m_var = a->fn1(); }
  void visit(type_b* b) const { m_b->m_var = b->fn2(); }
};
// actual processing:
boost::apply_visitor(v[0], process_stuff_visitor(the_b));

Alternatively since you assign to the same member of B, you could just extract the value-generating part.

struct generate_stuff_visitor : public boost::static_visitor<TypeOfMVar> {
  TypeOfMVar visit(type_a* a) const { return a->fn1(); }
  TypeOfMVar visit(type_b* b) const { return b->fn2(); }
};
the_b->m_var = boost::apply_visitor(v[0], generate_stuff_visitor());

OTHER TIPS

A very general object-oriented way of doing what you want to do (if I understand you correctly) is to create a virtual base class for types a and b (and any further types you want) which defines a pure virtual method. This method will return something different for each type (for example, type_b::method could return 'b', while type_a::method could return 'a'), so when you call the method on your unspecified type, you will be told what type it is.

From there, you can use the return value of the identifying method to be the subject of a switch statement, or some other conventional control structure to invoke the correct behavior.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top