Передать функцию от n аргументов как функцию от n-x аргументов

Это очень простой вопрос, и я уверен, что на него уже отвечали, но я не знаю, что искать.

Заявлено, у меня есть функция, которая объединяет математическую функцию:

double integrator(double (*func_to_integrate)(double,double));

Но моя функция для интеграции имеет тип, который позволяет мне манипулировать более чем двумя параметрами, например:

double func_to_integrate(double mu, double w0, double x, double y);

Так что я могу перебрать разные значения mu и w0 и сравнить результаты интегрирования.
Как я могу передать такую ​​функцию, как func_to_integrate интегратору?

Привет

Изменить: Как Ален указал в комментариях, это частично дубликат: Как можно карри в C ++?

Есть ли элегантное решение, выполняющее операцию карри на указателе функции?

2

Решение

Учитывая, что вы можете изменить подпись integrator Функция, есть несколько решений. Основные два направления:

  1. использовать общий параметр шаблона вместо указателя функции (- если вызывающий должен знать, какую подпись нужно передать), или
  2. использование std::function<double(double, double)> в качестве аргумента функции.

Обе альтернативы позволяют передавать объекты общей функции (функторы, лямбды, std::bind-объект и т. д.). Я бы пошел с альтернативой 1. как это обычно дает лучшую производительность.

Тогда вы можете легко настроить лямбду:

double mu = 1.0;
double w0 = 1.0;
auto f = [mu, w0] (double x, double y) { return func_to_integrate(mu, w0, x, y); };

и передать F к вашему (с восхищением) integrator рутина.


Вот еще один вариант, если вы не можете изменить сигнатуру функции — как это часто бывает в сторонних библиотеках.

Сначала я подумал, что в этом случае нет решения, так как вы не можете связать общий функтор с указателем на функцию. Но потом я столкнулся с хорошей идеей в этот ответ (который я немного скорректировал): закодировать все в терминах статики std::function переменной, затем используйте статическую функцию для вызова этого std::function объект. Поскольку статическая функция является просто синтаксическим сахаром для глобальной функции, можно установить на нее указатель функции:

template <typename Res, typename... Args>
struct function_ptr_helper
{
public:
template<typename function_type>
static auto bind(function_type&& f) { func = std::forward<function_type>(f); }

static auto invoke(Args... args) { return func(args...); }
static auto* ptr() { return &invoke; }

private:
static std::function<Res(Args ...)> func;
};

template <typename Res, typename... Args>
std::function<Res(Args ...)> function_ptr_helper<Res, Args...>::func;

template <typename Res, typename ... Args>
auto* get_function_ptr(std::function<Res(Args...)> f)
{
using type = function_ptr_helper<Res, Args...>;

type::bind(std::move(f));
return type::ptr();
}

DEMO

Вы можете использовать его как

double mu = 1.0;
double w0 = 1.0;
std::function<double(double, double)> f
= [mu, w0] (double x, double y) { return func_to_integrate(mu, w0, x, y); };

integrator(get_function_ptr(f));

Имейте в виду, однако, что вы имеете дело с глобальными переменными здесь. Это часто работает, но иногда может привести к незначительным ошибкам (например, когда вы звоните get_function_ptr более одного раза в одном выражении).

2

Другие решения

Как я могу передать такую ​​функцию, как func_to_integrate интегратору?

Кажется, очень легко исправить. Просто добавьте еще два аргумента к вашей подписи функции указателя.

double integrator(double (*func_to_integrate)(double,double,double,double));
1

Как отмечалось в предыдущих комментариях, наиболее элегантным решением было бы использование bind и / или лямбда-выражения. Хорошим решением была бы оболочка класса шаблона проектирования адаптера, где mu и w0 становятся членами класса.

class IntegratorAdaptor {
private:
double _mu, double _w0;

public:
IntegratorAdapter(double arg_mu, double arg_w0)
: _mu(arg_mu), _w0(arg_w0) { }

double twoArgIntegrator( double x, double y )
{ return func_to_intergrate( _mu, _w0, x, y ); }
};

Конструкция этого класса очень низка, поэтому я сделал членов неизменными. Я не придумал очень хороших имен для класса и функций, вы должны больше думать об этих именах, чем я.

0

Большинство ответов, которые я видел для такого рода вопросов, основаны на std::function и / или шаблоны C ++. Я хотел поделиться альтернативным решением, которое может быть менее общим, но для меня проще. Не использует std::function или шаблоны — фактически, он вообще не использует никаких библиотек.

Идея состоит в том, что вместо передачи указателя на функцию вы передаете объект, который реализует определенный «интерфейс». В этом примере

double integrator(double (*func_to_integrate)(double,double))

становится

double integrator(Integratable func_to_integrate)

где Integratable это «интерфейс» (абстрактный базовый класс), определенный как

class Integratable {
public:
virtual double compute(double x, double y) = 0;  // pure virtual function
}

Затем мы можем сделать func_to_integrate в экземпляр этого класса, с дополнительными членами для дополнительных параметров:

class SomeClassName : public Integratable {
public:
double compute(double x, double y);
double mu;
double w0;
}

SomeClassName func_to_integrate;

Чтобы проверить несколько значений mu а также w0 в цикле:

for(double mu : mus) {
for(double w0 : w0s) {
func_to_integrate.mu = mu;
func_to_integrate.w0 = w0;
integrator(func_to_integrate);
}
}

Конечно, мы должны изменить integrator так что вместо вызова указателя функции он вызывает compute() метод объекта, переданного ему, но это тривиально (при условии, что вы можете изменить сигнатуру integrator, что, вероятно, требуется для любого возможного решения этой проблемы).

Мне нравится это решение, потому что оно избегает некоторых более тяжелых функций и библиотек C ++. Тем не менее, он, безусловно, является менее общим, чем многие другие решения, которые часто предлагаются для частичного применения в C ++. Для OP я считаю, что это решение элегантно подходит для данного варианта использования.

0
По вопросам рекламы [email protected]