Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generic function which can take any class member in parameter C++

Tags:

c++

c++11

I have several tests to do on class methods. I have difficulties using a std::function for that. Here an example code to simplify the problem :

#include <iostream>
#include <functional>

template<typename T>
struct Foo
{
    Foo(T sum) : sum_(sum) {};
    Foo<T> method_one(const Foo<T>& foo) const { Foo<T> res(sum_ + foo.sum_); return res; }
    Foo<T> method_two(const Foo<T>& foo) const { Foo<T> res(sum_ + foo.sum_ + 10); return res; }
    Foo<T> method_three(const Foo<T>& foo, T val) const { Foo<T> res(sum_ + foo.sum_ + val); return res;}
    friend std::ostream &operator << (std::ostream & output, const Foo &foo) { output << foo.sum_; return output;}

    T sum_;
};

template<typename T>
void do_the_work(const Foo<T>& a, const Foo<T>& b, const std::function<Foo<T>(const Foo<T>&)> &func)
{
    // I do stuff before [...]
    Foo<T> c = a.func(b);
    std::cout << c << std::endl;
    // I do stuff after [...]
}

int main()
{
    Foo<float> a(1.0);
    Foo<float> b(2.0);


    // I would like to replace this
    Foo<float> c = a.method_two(b);
    std::cout << c;  // 1 + 2 + 10


    // with something like that
    do_the_work(a, b, a.method_one);
    do_the_work(a, b, a.method_two);
    // and if possible make it more generic...
    do_the_work(a, b, a.method_three, 12);
}

I have tried using bind in the main() without success :

std::function<Foo<float>(const Foo<float>&)> f = std::bind(&Foo<float>::method_one, &a);

Any other elegant solutions would be nice. As you can notice this is to prevent redundancy of code and doing several times the "do stuff before or after"

like image 452
coincoin Avatar asked Mar 26 '26 07:03

coincoin


2 Answers

I'd skip using std::function in the signature of do_the_work and resolve it all inside. It makes the inclusion of additional arguments easier:

template<typename T, typename Func, typename... Args>
void do_the_work(const Foo<T>& a,
                 const Foo<T>& b,
                 Func func,
                 Args&&... args)
{ 
  auto f = std::bind(func, a, b, std::forward<Args>(args)...);

  // I do stuff before [...]
  Foo<T> c = f();
  std::cout << c << std::endl;
  // I do stuff after [...]
}

And later

do_the_work(a, b, &decltype(a)::method_one);
do_the_work(a, b, &decltype(a)::method_two);
// and if possible make it more generic...
do_the_work(a, b, &decltype(a)::method_three, 12);
like image 61
Wintermute Avatar answered Mar 27 '26 20:03

Wintermute


You are missing the implicit this parameter to your member function, you must specify it in the std::function signature :

template<typename T>
void do_the_work(const Foo<T>& a, const Foo<T>& b, std::function<Foo<T>(const Foo<T>&, const Foo<T>&)> func)
{
    Foo<T> c = func(a, b);
    ...
}

Note how the std::function is invoked (it's not a.foo(b))

And to call it :

do_the_work<float>(a, b, &Foo<float>::method_one);
do_the_work<float>(a, b, &Foo<float>::method_two);

If you want / can bind the object on which your functions will be called :

template<typename T>
void do_the_work_binded(const Foo<T>& b, std::function<Foo<T>(const Foo<T>&)> func)
{
    // I do stuff before [...]
    Foo<T> c = func(b);
    std::cout << c << std::endl;
    // I do stuff after [...]
}

...

std::function<Foo<float>(const Foo<float>&)> fb = std::bind(&Foo<float>::method_one, a, std::placeholders::_1);
do_the_work_binded(b, fb);

Live demo

like image 33
quantdev Avatar answered Mar 27 '26 19:03

quantdev



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!