views:

161

answers:

5

It's quite hard to explain what I'm trying to do, I'll try: Imagine a base class A which contains some variables, and a set of classes deriving from A which all implement some method bool test() that operates on the variables inherited from A.

class A {
   protected:
   int somevar;
   // ...
};

class B : public A {
   public:
   bool test() {
      return (somevar == 42);
   }
};

class C : public A {
   public:
   bool test() {
      return (somevar > 23);
   }
};

// ... more classes deriving from A

Now I have an instance of class A and I have set the value of somevar.

int main(int, char* []) {
    A a;
    a.somevar = 42;

Now, I need some kind of container that allows me to iterate over the elements i of this container, calling i::test() in the context of a... that is:

    std::vector<...> vec;
    // push B and C into vec, this is pseudo-code
    vec.push_back(&B);
    vec.push_back(&C);

    bool ret = true;
    for(i = vec.begin(); i != vec.end(); ++i) {
       // call B::test(), C::test(), setting *this to a
       ret &= ( a .* (&(*i)::test) )();
    }
    return ret;
 }

How can I do this? I've tried two methods:

  1. forcing a cast from B::* to A::*, adapting a pointer to call a method of a type on an object of a different type (works, but seems to be bad);
  2. using std::bind + the solution above, ugly hack;
  3. changing the signature of bool test() so that it takes an argument of type const A& instead of inheriting from A, I don't really like this solution because somevar must be public.

EDIT:

Solution (1) is:

typedef bool (A::*)() mptr;
std::vector<mptr> vec;
vec.push_back(static_cast<mptr>(&T::test));

std::vector<mptr>::iterator i;
for(i = vec.begin(); i != vec.end(); ++i) {
   (a .* (*i))();
}

I'm not sure the static cast is safe.

+1  A: 

Add "virtual bool test() = 0;" in the definition of A.

Then you can do the following in your loop:

ret = (ret && i->test());

BTW: "&=" does a "bitwise and" and you probably want the logical and to be performed (&&).

Also: the instances of B and C you put pointers to in your vector all contain copies of the inherited variable, they are all independant instantiations of that variable.

I think your code, as shown here, is pretty flawed. Think more about what it is you want to actually achieve?

Do you want to run a multiplicity of boolean tests on a single variable and see if it matches all of them? Or is each contraint really to be tested against its own variable and you want to get the "boolean and" of all those independent tests?

haavee
This isn't what the OP is interested in. `test()` is not supposed to be invoked on `i` but on the object `a`.
Andreas Brinck
I see what you mean but that would be #EPIC FAIL.Even then the only way to get it fixed is by having A have a virtual member function test().
haavee
+3  A: 

You are trying to call B and C methods on an A. Don't do that.

You need to create actual instances of B and C, store pointers to them in a vector<A*> and, during iteration, call a pure virtual test() member function defined in A (which B::test and C::test will override).

Marcelo Cantos
+5  A: 

The cleanest solution is the last one you suggest, make test a (pure) virtual function in A:

virtual bool test(const A& value) = 0;

If you're bothered with making somevar public keep it private and supply only a public get function:

int getvar() const {return somevar;}
Andreas Brinck
haavee
A: 

This is the cleanest solution so far. It uses static:

struct A {
   int somevar;
};

struct B {
   static bool test(const A& a) {
      return (a.somevar == 42);
   }
};

std::vector<bool (*)(const A&)> vec;

template<typename T>
void push(const T&) {
   vec.push_back(&T::test);
}
Helltone
A: 

The simple solution:

Change class A to:

class A {
public:
    virtual bool test() const = 0;
protected:
    int somevar;
    // ...
};

Now, I need some kind of container that allows me to iterate over the elements i of this container, calling i::test() in the context of a.

typedef std::vector<A*> ItemList;
ItemList items;
for(ItemList::const_iterator i = items.begin(); i != items.end(); ++i)
{
    if((*i)->test())
        ; // ???
}

So I'm wondering what the OP wants to do that this doesn't...

Mike DeSimone