1

Consider the following three functions:

using phase_t = Eigen::VectorXd;
using rhs_t = phase_t (*)(double const &, phase_t const &);
using step_t = phase_t (*)(rhs_t , phase_t const &, double, double);

Eigen::MatrixXd foo_ee_step(rhs_t rhs, phase_t const &z0);
Eigen::MatrixXd foo_int(step_t step, rhs_t rhs, phase_t const & z0);
Eigen::MatrixXd foo_ee(rhs_t rhs, phase_t const &z0);

All of them have one argument of the type rhs_t, which I specified in the second line above. In addition one of the functions has a parameter of the type step_t which depends on a parameter of typerhs_t. My problem is that the variable I'd like to pass to foo_ee_step, foo_int and foo_ee has the from

phase_t my_rhs(double const &, phase_t const &, double);

Since I cannot change foo_ee_step, foo_int and foo_ee I tried using a lambda to redefine the function in a suitable way

Eigen::MatrixXd some_function(.....):
    auto better_rhs = [&c](double const & a, phase_t const & b){return my_rhs(a,b,c);};
    return foo_ee(better_rhs, m);

but this results in

error: cannot convert ‘some_function(....)::<lambda(const double&, const phase_t&)>’ to ‘rhs_t’ {aka ‘Eigen::Matrix<double, -1, 1> (*)(const double&, const Eigen::Matrix<double, -1, 1>&)’}

I think this comes from the fact that I'm trying to pass a lambda that captures something as a function pointer, which seems to be forbidden... I've read here, that one could try to solve this problem by defining a functor. But if I'm not mistaken, for this to work I would need to change foo_ee_step, foo_int and foo_ee, which I can't.. Soo, I don't really know how to tackle this problem.. Is it possible to somehow cast this lambda expression into the form rhs_t? Are there other techniques to solve this problem?

P.S. Not sure if it is important, but so far I have also tried:

  1. Defining just another function inside some_function that is named better_rhs (which obviously also didn't work).
  2. Wrap all of this into a class, and use foo_ee_step, etc. as member functions. Then just define another member function better_rhs and call there my_rhs.. This resulted in an error about not being able to pass a non-static member function, but having to call it explicitly...

Any hints on how to proceed are appreciated.

Sito
  • 494
  • 10
  • 29

3 Answers3

1

The only ways to do that are pretty hacky. You have to realise that a function pointer is just a pointer to a static memory location where a function lives. Hence adding state (i.e. binding the extra double param) with dynamic or automatic storage duration (heap or stack data) is not possible. The function would need to now where that lives but it cannot. (That is the reason why generic function interfaces in C take a void* and a function pointer also taking a void*, so the state location passed as a pointer to void and then interpreted to the right type in the function)

You can add state with static storage duration, so i.e. in the following ways:

// Interface we want to match
void print_call(int (*f)()) {
  std::cout << f() << `\n`;
}
// Way 1: Globals
static int data = 0;
int get_data() { return data; } 

print_call(get_data); // Prints 0
data = 42;
print_call(get_data);  // Prints 42

// Way 2: Class statics
struct state {
  int data = 0;
  static int get_data() { return data; }
};

print_call(state::get_data); // Prints 0
state::data = 42;
print_call(state::get_data); // Prints 42

I would prefer way 2 for reasons of encapsulation and more understandable order of initialization.

Note that using statics has it own problems. You may want to look if you can't somehow change your design to get rid of the whole problem.

n314159
  • 4,990
  • 1
  • 5
  • 20
0

Usually external functions that take function pointers also take a void* data pointer so can pass extra context (Which in your case would be a pointer to a double). Without that, the function just isn't called with enough data.

You could try a static double like:

static double data;
data = c;
return foo_ee([](double const& a, phase_t const& b) { return my_rhs(a, b, data); }, m);

But that isn't thread-safe (And I don't know how the function passed is called, but running this a second time would make the old function pointer point to the new data, probably not what you want).

Maybe phase_t has that context pointer in it?

Artyer
  • 31,034
  • 3
  • 47
  • 75
0

You can store c in a global variable, or a static variable inside some get_global_c function, and reference that inside your lambda, so you don't have to capture it:

// ...
static double c;
void some_function()
{
    // ...
    c = calculate_c();
    auto better_rhs = [](double const &a, phase_t const &b) { return my_rhs(a, b, c); };
    rerturn foo_ee(better_rhs, m);
}
IlCapitano
  • 1,994
  • 1
  • 7
  • 15