Want to protect your cyber security and still get fast solutions? Ask a secure question today.Go Premium

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 225
  • Last Modified:

Function pointers and callbacks (I think!)

My question is with regards to callbacks and function pointers, two topics with which I have little experience but am trying to figure out.

Say I have class A , B and C

each class has a method Run()

classes B and C each contain and instance of A.

classes B and C do not derive from a common base class (while that might simplify things via polymorphism and virtual functions, it would not be appropriate from a modelling standpoint)

when A:Run() is invoked, it needs to to execute B:Run() or A:Run() depending on which class, A or B, is the container.  

(I know, this isn't the best OO design, contained classes shouldn't need to know of whom contains them)

Anyway - if C were not in the picture, I could just do this in A:

private:
  B*  m_pParent;

and then:
A::Run()
{
  m_pParent->Run();
}

But since there are two discrete types of classes (in this example) I can't just store a member pointer to the container object because I don't know of which type it should be defined as.

Isn't this kinda what function pointers are for? when B instantiates its contained instance of A, is there a way for B to tell A  -  "MY Run() method is what you will execute when your Run() is invoked, here is the address to it so you don't need to know if I am B or C" ?

If that is possible, how would I go about doing it?

Thanks!
Paul
0
PMH4514
Asked:
PMH4514
  • 6
  • 4
  • 2
  • +2
1 Solution
 
topdog770Commented:
One option would be to create A::WhichFunctionToRun( someFuncPointer * p )
{
   if( p == null )
   {
        A::Run();
   }
   else
   {
      p();
   }
}

0
 
AxterCommented:
Hi PMH4514,
> >If that is possible, how would I go about doing it?

To do this with a non-static member function, you would need to have an instance of the target object in order to call it's member function.

David Maisonave :-)
Cheers!
0
 
AxterCommented:
PMH4514,
Here's some example code:

class A
{
public:
    template<class T, typename F>
    void Run(T& t, F &f)
    {
        cout << "A::Run" << endl;
        (t.*f)();
    }
};

class B
{
public:
    void Run()
    {
        cout << "B::Run" << endl;
    }
    void FunctionX()
    {
        typedef void (B::*MYFUNC_PTR) ();
        MYFUNC_PTR myfunc_ptr = Run;
        m_A.Run(*this, myfunc_ptr);
    }
    A m_A;
};

class C
{
public:
    void Run()
    {
        cout << "C::Run" << endl;
    }
    void FunctionX()
    {
        typedef void (C::*MYFUNC_PTR) ();
        MYFUNC_PTR myfunc_ptr = Run;
        m_A.Run(*this, myfunc_ptr);
    }
    A m_A;
};

int main()
{
    B b;
    C c;
    b.FunctionX();
    c.FunctionX();


David Maisonave :-}
0
VIDEO: THE CONCERTO CLOUD FOR HEALTHCARE

Modern healthcare requires a modern cloud. View this brief video to understand how the Concerto Cloud for Healthcare can help your organization.

 
AxterCommented:
If you don't like to use templates, heres a method using an abstract helper class.
Class A, B, and C do not have to be related using this method.
class FunctionHelper
{
public:
    virtual void Run()=0;
};

class A
{
public:
    void Run(FunctionHelper &f)
    {
        cout << "A::Run" << endl;
        f.Run();
    }
};

class B
{
public:
    void Run()
    {
        cout << "B::Run" << endl;
    }
    class FunctionHelper_T : public FunctionHelper
    {
    public:
        FunctionHelper_T(B& b):m_B(b){}
        void Run()
        {
            m_B.Run();
        }
        B& m_B;
    };
    void FunctionX()
    {
        m_A.Run(FunctionHelper_T(*this));
    }
    A m_A;
};

class C
{
public:
    void Run()
    {
        cout << "C::Run" << endl;
    }
    class FunctionHelper_T : public FunctionHelper
    {
    public:
        FunctionHelper_T(C& c):m_C(c){}
        void Run()
        {
            m_C.Run();
        }
        C& m_C;
    };
    void FunctionX()
    {
        m_A.Run(FunctionHelper_T(*this));
    }
    A m_A;
};

int main()
{
    B b;
    C c;
    b.FunctionX();
    c.FunctionX();
0
 
rstaveleyCommented:
Possibly, you could make B and C include a class derived from A, but which takes a pointer or reference to the B/C parent. The derived class can call the run function in B or C as applicable, because it knows about B/C. Class A needs to call a virtual function to get to the derived class's run implementation. In the following example A's run calls the virtual function v_run, which is implemented in the derived class, which I've implemented as a template class.

--------8<--------
#include <iostream>
using std::cout;

class A {
      virtual void v_run() = 0; // Pure virtual function
public:
      void run() {
            cout << "A::run(): begin\n";
              v_run();
            cout << "A::run(): end\n";
      }
};

// Template class wraps itself around A and takes a reference to a parent, which is
// some class which is expected to have a public run method.
template<typename T> class D : public A {
      T& parent;
      void v_run() { // Overrides the PVF
            cout << "D::run(): begin\n";
              parent.run();
            cout << "D::f(): end\n";
      }
public:
      D(T& parent) : parent(parent) {}
};

class B {
      D<B> d;      // Contains D - derived from A but instantiated for C
public:
      B() : d(*this) {} // Compiler will probably warn you about using this
      void run() {cout << "B::run\n";}
      void f() {
            cout << "B::f(): begin\n";
              d.run();
            cout << "B::f(): end\n";
      }
};

class C {
      D<C> d;      // Contains D - derived from A but instantiated for D
public:
      C() : d(*this) {} // Compiler will probably warn you about using this
      void run() {cout << "C::run\n";}
      void f() {
            cout << "C::f(): begin\n";
              d.run();
            cout << "C::f(): end\n";
      }
};

int main()
{
      B b;
      b.f();
      cout << '\n';
      C c;
      c.f();
}
--------8<--------
0
 
andrewjbCommented:
I've only skimmed the previous answers, but don't think anyone's mentioned...

The boost library (boost.org) has a useful 'bind' template function which could help. You can do things like:


class A
{
  boost:function0<void> Callback;
.. and a constructor that takes a boost::function0<void> and assigns to callback;
.. then you'll call it via simply Callback() when you want to
}

then to pass into the constructor:

B BInstance;

new A( boost::function0<void> F = boost::bind( &B.Fire , BInstance ) )

(and similarly for C )

.. which is basically an implementaion of a helper wrapper as above.


0
 
PMH4514Author Commented:
wow, lots of good comments, I have to take a bit more time to review them all. Is this a design problem you guys encounter frequently? I took some time and was able to refactor a piece of my design and in fact found a base class that I hadn't thought of before that is appropriate to the design, and elliminates my need for this type of solution.. but it's interesting none-the-less, so keep the comments coming!
0
 
PMH4514Author Commented:
it seems to me that function pointers which cross class boundries, are a workaround to an improper or incomplete OO design. Would you agree?
0
 
AxterCommented:
>>are a workaround to an improper or incomplete OO design.  Would you agree?

No.
This is especially not so, when you don't have full control of all the objects original design.
For example, if you're using MFC, and you want to give a particular functionality to a CEdit derived class and a CComboBox derived class.
If you have full control of the MFC designed, then you could modify the base class (CWnd) and add the common functionality to that class.
However, in the real world, you don't always have access to the base class design, and therefore you need to develop workaround solutions that will allow separate classes to share the same logic.

The use of function pointers is just another way to produce polymorphism behavior.

FYI:
I recently had the exact above scenario, however I didn’t use function pointers, and instead I used a proxy class.
See following links:
http://code.axter.com/BrowseCtrl.h
http://code.axter.com/BrowseCtrl.cpp

In the code in the above link, the AssociatedButton class works like a proxy class.  And the derived classes like CEditBrowseFileCtrl and CComboBoxBrowseFileCtrl are able to pass their derived detail implementations to the parent BrowseCtrl class via the proxy class.


IMHO, a proxy class would be a better design for your requirements.
0
 
PMH4514Author Commented:
ahh gotcha..

I like the proxy idea. I should go back to my design patterns book and read up on it again.
0
 
AxterCommented:
Design Patterns is a good book, but I really hate their examples.

I've been thinking of creating an articale that would give good examples that are easy to understand for each pattern in the Design Patterns book.
0
 
PMH4514Author Commented:
You're talking about the Gamma one right? It's been a good reference for me for years.. I wish I had gotten into C++ earlier! I've always only been "sorta" able to implement some of the patterns in whatever language I was working with.


0
 
AxterCommented:
>>You're talking about the Gamma one right?
Yes (Erich Gamma)

Have you tried reading the descriptions for each pattern that's located in the inside jacket of the book?

It gives me a headache trying to determine the difference between one pattern and another using the description.

Maybe my comprehension is poor, but when I read the pattern description, I find it difficult to understand the difference between one pattern and another.


Thanks for the points
0
 
rstaveleyCommented:
> It gives me a headache trying to determine the difference between one pattern and another using the description

You're not alone, David :-)
0

Featured Post

What does it mean to be "Always On"?

Is your cloud always on? With an Always On cloud you won't have to worry about downtime for maintenance or software application code updates, ensuring that your bottom line isn't affected.

  • 6
  • 4
  • 2
  • +2
Tackle projects and never again get stuck behind a technical roadblock.
Join Now