0

How do I pass the non-static member function eval of object Problem1 obj to the object Solver solver ?

#include<iostream>
#include<functional>

// non-templated class
struct Solver{
    std::function<void(double&, double&)>* f;
    double solve(){
        double y,x=1;
        for(long int i=0;i<10000;++i){
            (*f)(y,x); (*f)(x,y);
        }
        return y;
    }
};

// non-static class; unknown at implementation time of Solver
struct Problem1{
    double p;
    void eval(double& y, double& x){
        y = x*x - p;
    }
};

int main(){
    Problem1    obj;
    Solver      solver;
    solver.f = &(obj.eval); // pass non-static member function
    solver.solve();
}

Since one solver shall be used to solve very many different instances of various problem classes, I do not want to use templates. Since the solution of one problem object may require very many calls of eval, I am shy to use virtual inheritance from a VirtualProblem class due to the overhead and slow reputation of virtual classes in c++. Does this matter here? What should I do?

  • Unrelated: You can create an empty `std::function` object using [constructor signature `(1)`](https://en.cppreference.com/w/cpp/utility/functional/function/function), so there's usually no need to use a pointer to an `std::function` object. – fabian Aug 11 '23 at 15:53
  • 1
    `std::function` will also have overhead. `virtual`'s overhead isn't that big, typically it adds a couple levels of indirection when you call the function. A `vtable` implementation needs to follow a pointer to a table, then lookup a function pointer in that table. I'm not sure what the overhead on `std::function` is, but it is likely in the same order of magnitude. Since you are trying to save a pointer to a function and call a function through it, you already have a level of indirection from the start. – François Andrieux Aug 11 '23 at 15:54
  • Side note: `double solve(const std::function f) { ... }` might be a little better (with `solver.solve(obj.eval);`) – OrenIshShalom Aug 11 '23 at 15:58

2 Answers2

3

That's not how std::function works.

First of all it should not be a pointer. You can't assign a pointer to a non-static member function to it (and especially not its pointer). And you can't use non-static member function that way.

To solve your problem first lets make f a non-pointer object:

struct Solver{
    std::function<void(double&, double&)> f;
    double solve(){
        double y,x=1;
        for(long int i=0;i<10000;++i){
            f(y,x); f(x,y);
        }
        return y;
    }
};

Then we can use a lambda to call the correct function on the correct object:

solver.f = [&obj](double& a, double& b)
{
    obj.eval(a, b);
};

On another note, if you want to return a value from a function, actually return a value. Please don't abuse reference arguments when it's not really needed.

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
  • Thank you for your help! On the return value comment: I did so because it is a minimum example. Probably, when passing nD arrays, you would agree it is better by reference/pointer. – Darika Ranganathan Aug 11 '23 at 16:04
  • Oh, I just noticed: your proposed code with the bind produces the following compiler error in GNU g++ 12.0.1: mini_main.cpp:27:92: error: no match for 'operator=' (operand types are 'std::function' and 'std::_Bind_helper&, const std::_Placeholder<2>&>::type') @Some-programmer-dude Do you know how to fix this? – Darika Ranganathan Aug 11 '23 at 16:09
  • @DarikaRanganathan Hmm, that's odd, I thought I tested it and it did work, but not it doesn't. Maybe I'm hallucinating... :-) Anyway, I removed that option from the answer. – Some programmer dude Aug 11 '23 at 16:42
  • @DarikaRanganathan Regarding the return issue, if your requirement is to modify the first argument (value, array, etc.) in-place then pass it by reference, and for simple values pass the second argument by value, else pass it by constant reference. If there's no in-place requirement, then even for arrays and objects, I would still recommend returning a n actual value (or object). You could still return a value/object, and assign back to the variable you use for the first argument. The compiler can create pretty good optimized code for you, so don't think too much about that. – Some programmer dude Aug 11 '23 at 16:45
0

I think, you could use 'std::bind'

Misha T
  • 56
  • 5