Generic function that any class member can take in a C ++ parameter

I have some tests for class methods. I am having difficulty using std :: function for this. Here's some sample 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 tried using bind in main () with no success:

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

      

Any other elegant solution would be nice. As you can see, this prevents code redundancy and makes it "do things before or after" multiple times.

+3


source to share


2 answers


I would skip the use std::function

in the signature do_the_work

and allow it all internally. This makes it easier to include additional arguments:

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);

      

+2


source


You are missing an implicit parameter this

for your member function, you must specify it in the signature std::function

:

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);
    ...
}

      

Notice how std::function

(it is not a.foo(b)

) is called

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 an object that your functions will be called on:

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

+2


source







All Articles