Как обернуть функцию, чтобы она соответствовала требуемому типу другой функции - PullRequest
1 голос
/ 28 января 2020

Рассмотрим следующие три функции:

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

Все они имеют один аргумент типа rhs_t, который я указал во второй строке выше. Кроме того, одна из функций имеет параметр типа step_t, который зависит от параметра типа rhs_t. Моя проблема в том, что переменная, которую я хотел бы передать foo_ee_step, foo_int и foo_ee, имеет значение от

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

, поскольку я не могу изменить foo_ee_step, foo_int и foo_ee Я пытался использовать лямбду, чтобы переопределить функцию подходящим способом

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

, но это приводит к

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>&)’}

Я думаю, что это происходит из-за того, что я пытаюсь передать лямбда, которая захватывает что-то в виде указателя на функцию, которая, кажется, запрещена ... Я прочитал здесь , что можно попытаться решить эту проблему, определив функтор. Но если я не ошибаюсь, чтобы это работало, мне нужно изменить foo_ee_step, foo_int и foo_ee, чего я не могу .. Так, я действительно не знаю, как решить эту проблему. Можно ли как-нибудь привести это лямбда-выражение в форму rhs_t? Существуют ли другие методы для решения этой проблемы?

PS Не уверен, что это важно, но до сих пор я также пытался:

  1. Определение еще одной функции внутри some_function, которая называется better_rhs (которая, очевидно, также не не работает).
  2. Оберните все это в класс и используйте foo_ee_step, et c. как функции-члены. Затем просто определите другую функцию-член better_rhs и вызовите ее my_rhs .. Это привело к ошибке из-за невозможности передать функцию-член c, не относящуюся к состоянию, но из-за необходимости вызывать ее явно ...

Любые советы о том, как действовать, приветствуются.

Ответы [ 3 ]

1 голос
/ 28 января 2020

Единственные способы сделать это довольно хакерские. Вы должны понимать, что указатель на функцию - это просто указатель на область памяти c, в которой находится функция. Следовательно, добавление состояния (то есть связывание дополнительного двойного параметра) с помощью динамической c или автоматической c длительности хранения (кучи или данных стека) невозможно. Функция должна была бы сейчас, где это живет, но не может. (Именно по этой причине универсальные c интерфейсы функций в C принимают void*, а указатель функции также принимает void*, поэтому местоположение состояния передается как указатель на void, а затем интерпретируется в правильный тип в функция)

Вы можете добавить состояние с stati c длительностью хранения, то есть следующими способами:

// 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

Я бы предпочел способ 2 по причинам инкапсуляции и более понятного порядка инициализации.

Обратите внимание, что использование статики имеет свои проблемы. Вы можете посмотреть, не можете ли вы как-то изменить свой дизайн, чтобы избавиться от всей проблемы.

0 голосов
/ 28 января 2020

Вы можете хранить c в глобальной переменной или в переменной stati c внутри некоторой функции get_global_c и ссылаться на нее внутри своей лямбды, так что вам не придется ее захватывать:

// ...
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);
}
0 голосов
/ 28 января 2020

Обычно внешние функции, которые принимают указатели на функции, также принимают указатель данных void*, поэтому могут передавать дополнительный контекст (который в вашем случае будет указателем на двойное число). Без этого функция просто не вызывается с достаточным количеством данных.

Вы можете попробовать удвоить c как:

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

Но это не поточно-ориентированный (И Я не знаю, как вызывается передаваемая функция, но при повторном запуске этой функции указатель старой функции будет указывать на новые данные, вероятно, не то, что вам нужно).

Может быть, phase_t имеет такой контекст указатель в нем?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...