Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing member function (or workaround)

Tags:

c++

I want to do the following:

class A{
private:
  //some data
  B b;
  double f(double);
public:
  A(){
    b = new B(f); // does not work of course
  }
};

class B{
public:
  B(double (*func)(double));
};

Class B is supposed to solve a mathematical problem specified by the function func. Class A should now use B to solve this problem for func=f. The member function f accesses the private data members of A. The problem is, of course that I cannot simply pass a pointer to member function. I know there are ways to do that, but B should still be able to take any function, not only members of A. Until now, I just made f and the members of A static, but I think this is a rather bad design. Can you think of any workaround for this?

like image 437
cthl Avatar asked Jul 23 '14 12:07

cthl


3 Answers

You can use the standard std::function<> template as a polymorphic wrapper for your functions.


Your class B simply store an instance of std::function<double (double)> and call it through foo :

class B
{
  public:
    B(const std::function<double (double)>& func) : func(func) {}

    void foo(double d) 
    {
       std::cout << func(d);   
    }

  private:
    std::function<double (double)> func;
};

While your class A build its B member with one of its member function (f), thanks to std::bind :

class A
{   
  public:
    double md;
    B b;

    double f(double d) const
    {
      return md * d;
    }

  public:
    A(double d) : md(d), b(std::bind(&A::f, this,  std::placeholders::_1)) { }
};

We can now simply use it :

int main() {
   A a(42);
   a.b.foo(2); // Output : 84
}

Live demo here.

like image 178
quantdev Avatar answered Nov 08 '22 00:11

quantdev


How about using polymorphism as an alternative to your current design?

For example:

class A
{
protected:
    virtual double f(double x) = 0;
};

class B1 : public A
{
public:
    double f(double x) {return x+1.0;}
};

class B2 : public A
{
public:
    double f(double x) {return x+2.0;}
};

...

A* arr[4];
arr[0] = new B1;
arr[1] = new B2;
arr[2] = new B1;
arr[3] = new B2;
for (int i=0; i<4; i++)
    cout << arr[i]->f(0.0);
like image 23
barak manos Avatar answered Nov 08 '22 00:11

barak manos


This a fully working program for your sample.

#include <memory>
#include <iostream>
#include <functional>

class B
{
    public:
        B(std::function<double(double)> func)
        {
            std::cout<<func(1.0);
        }
};

class A
{
    private:
        std::unique_ptr<B> b;

        double f(double)
        {
            std::cout<<"A::f";
            return 2.0;
        }

    public:
        A() : b(new B(std::bind(&A::f, this, std::placeholders::_1)))
        {
        }
};

int main()
{
    A a;
}

Please note you don't destroy b there and also keep in mind you are passing this to B, thing that might be dangerous (B might be alive after A is destroyed and if you use this inside f... boom!).

I would also suggest avoiding pointers and if this is not possible using std::unique_ptr

EDIT: And a version without b as pointer, std::function and std::bind

#include <iostream>

class A;

class B
{
    public:
        B(A* obj, double(A::*func)(double))
        {
            std::cout<<(obj->*func)(1.0);
        }
};

class A
{
    private:
        B b;
        double f(double)
        {
            std::cout<<"A::f";
            return 2.0;
        }

    public:
        A():b(this, &A::f)
        {
        }
};

int main()
{
    A a;
}
like image 2
Mircea Ispas Avatar answered Nov 08 '22 01:11

Mircea Ispas