0

There are other questions regarding how to pass an external function as an argument to a function in C++, but I am struggling to apply those answers to the case where the target function is the constructor function for a novel class.

Here is what I am trying to do:

#include <iostream>

double external_function(double x, double y) {
    return x * y;
}

class foo {
public:
    // Initialize a function (?) as a method of class foo
    double func(double, double);

    // Construct an instance of the class by supplying a pointer to an external function
    foo(double(*func_in)(double, double)) {
        func = *func_in;
    }

    // When calling this method, evaluate the external function
    double eval(double x, double y) {
        return func(x, y);
    }
};

int main() {
    foo foo_instance(&external_function);
    std::cout << foo_instance.eval(1.5, 2); // Should print 3
    return 0;
}

I would like to have func as a method of the foo class because I later will write methods of foo that do other things with func, e.g. search for a local minimum.

By analogy, here is working code that passes an external constant instead of a function:

#include <iostream>

double external_value = 1.234;

class bar {
public:
    // Initialize a value as a method of class bar
    double val;

    // Construct an instance of the class by supplying a pointer to an external value
    bar(double* val_in) {
        val = *val_in;
    }

    // When calling this method, return the external function
    double eval() {
        return val;
    }
};

int main() {
    bar bar_instance(&external_value);
    std::cout << bar_instance.eval(); // 1.234
    return 0;
}
Max
  • 323
  • 1
  • 11
  • 2
    I recommend that you learn about [`std::function`](https://en.cppreference.com/w/cpp/utility/functional/function). That way you can use function-***pointers*** (hint), functor objects, or [lambdas](https://en.cppreference.com/w/cpp/language/lambda). – Some programmer dude Jul 09 '20 at 07:03

2 Answers2

1

Like this

class foo {
public:
    // Initialize a function pointer as a method of class foo
    double (*func)(double, double);

    // Construct an instance of the class by supplying a pointer to an external function
    foo(double(*func_in)(double, double)) {
        func = func_in;
    }

    // When calling this method, evaluate the external function
    double eval(double x, double y) {
        return func(x, y);
    }
};

In your version func was an undefined method of the class not the function pointer you wanted it to be.

john
  • 71,156
  • 4
  • 49
  • 68
  • You should also use initializer of the constructor and not override in the body. In this case, it has no side effects, but it is bad style! – Klaus Jul 09 '20 at 07:04
  • Thank you. How come, in the analogous case where I construct the class instance using a pointer to a constant, I initialize the constant inside the class definition using `int val;` instead of `int *val;`? – Max Jul 09 '20 at 07:04
  • Your line commented `Initialize a function pointer` doesn't do any initialisation. – Alan Birtles Jul 09 '20 at 07:05
  • @Max I'd like to see the code for that, because it doesn't sound correct to me. – john Jul 09 '20 at 07:05
  • @Klaus Absolutely, but especially with beginner I tend to make the minimum changes necessary in order to illustrate the error that was made. Unsing an initializer in this case would not have fixed the error and would perhaps have confused the OP. – john Jul 09 '20 at 07:07
  • 1
    @Max Well in the second case you are initialising a `double` with `double*` so obviously `*` is required. But function pointers are different, you can't dereference a function pointer to get the function *as a value*. So in this case what's being stored is the function pointer itself, not the function it is pointing at. If you want to treat functions as values then look into `std::function` as suggested elsewhere in this post. – john Jul 09 '20 at 07:10
  • 1
    I believe you should do the change and explain why. As 50k+ user you should not copy bad code. In case of objects the code will do "something" but not initialization. And this is typical beginner mistake, so it should be part of any answer. My two cents... – Klaus Jul 09 '20 at 07:11
  • @Klaus Well sometimes I will extend my answer to include improvments to the code. But sometimes you see this taken to ridiculous extremes where an answer will be given using the latest highly abstract modern C++, to a user who has trouble with basic syntax. So it's a judgement call, not a black and white issue in my opinion. – john Jul 09 '20 at 07:14
  • @Klaus I would be interested in seeing the change you are suggesting. I am following along with the second example here, where the constructor is included outright in the class definition without any "initialization": https://www.w3schools.com/cpp/cpp_constructors.asp – Max Jul 10 '20 at 23:28
  • 1
    @Max: I read your link. Please do not use it anymore. Never saw such wrong examples on official sites! Take a look for initializer list: https://en.cppreference.com/w/cpp/language/constructor. Your constructor should be: `foo(double(*func_in)(double, double)):func{func_in}{}` – Klaus Jul 10 '20 at 23:44
  • 1
    @Max Got to agree, that's a low quailty tutorial. Plenty of missing information and this statement 'Constructors are always public' is just plain wrong. – john Jul 11 '20 at 04:44
1

Here is the example using std::function:

#include <functional>

class foo {
public:
    // Initialize a function as a method of class foo
    std::function<double(double, double)> func;

    // Construct an instance of the class by supplying a pointer to an external function
    foo(std::function<double(double, double)> func_in) {
        func = func_in;
    }

    // When calling this method, evaluate the external function
    double eval(double x, double y) {
        return func(x, y);
    }
};
Superfly Jon
  • 811
  • 9
  • 7
  • Using this approach, how would I call the constructor in `main()`? Something like `foo foo_instance(external_function)` or `foo foo_instance(&external_function)` or do I need to make the external function into a `function` object too? – Max Jul 09 '20 at 21:01
  • You can call it like this: `foo foo_instance(external_function);` Adding & (or *) to function names has no effect – Superfly Jon Jul 10 '20 at 12:05