Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++: Binding to a base class

Tags:

c++

bind

boost

EDIT:

In the following code container::push takes an object of type T that derives from base as argument and stores in a vector a pointer to the method bool T::test().

container::call calls each of the stored methods in the context of to the member object p, which has type base, not T. It works as long as the called method does not refer to any member outside base and if test() is not declared virtual.

I know this is ugly and may not be even correct.

How can I accomplish the same thing in a better way?

#include <iostream>
#include <tr1/functional>
#include <vector>

class base {
   public:
   base(int v) : x(v)
   {}
   bool test() const { // this is NOT called
      return false;
   }

   protected:
   int x;
};

class derived : public base {
   public:
   bool test() const { // this is called instead
      return (x == 42);
   }
};

class container {
   public:
   container() : p(42)
   {}
   template<typename T>
   void push(const T&) {
      vec.push_back((bool (base::*)() const) &T::test);
   }
   void call() {
      std::vector<bool (base::*)() const>::iterator i;
      for(i = vec.begin(); i != vec.end(); ++i) {
         if( (p .* (*i))() ) {
            std::cout << "ok\n";
         }
      }
   }

   private:
   std::vector<bool (base::*)() const> vec;
   base p;
};

int main(int argc, char* argv[]) {
   container c;
   c.push(derived());
   c.call();
   return 0;
}
like image 425
Giovanni Funchal Avatar asked Dec 08 '25 15:12

Giovanni Funchal


2 Answers

What you are doing with your "boost::bind" statement is to call derived::test and pass "b" as a "this" pointer. It's important to remmember that the "this" pointer for derived::test is supposed to be a pointer to a "derived" object - which is not the case for you. It works in your particular situation since you have no vtable and the memory layout is identical - but as soon as that will change, your program will likely break.

And besides, it's just plain wrong - ugly, unreadable, bug-prone code. What are you really trying to do?

[Edit] New answer to the edited question: You should use boost::bind to create a functional closure, that wraps both the object & the member function in a single object - and store that object in your collection. Then when you invoke it, it is always reliable. If you can't use boost in your application... well, you could do something like boost::bind yourself (just look on how it is done in boost), but it's more likely that you'll get it wrong and have bugs.

like image 152
Virgil Avatar answered Dec 10 '25 05:12

Virgil


To the updated question:

Calling a derived member function on a base object is Undefined Behavior. What you are trying to achieve (code) is wrong. Try to post what you need and people will help with a sensible design.

like image 35
David Rodríguez - dribeas Avatar answered Dec 10 '25 03:12

David Rodríguez - dribeas