Это очень простой вопрос, и я уверен, что на него уже отвечали, но я не знаю, что искать.
Заявлено, у меня есть функция, которая объединяет математическую функцию:
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 ++?
Есть ли элегантное решение, выполняющее операцию карри на указателе функции?
Учитывая, что вы можете изменить подпись integrator
Функция, есть несколько решений. Основные два направления:
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();
}
Вы можете использовать его как
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
более одного раза в одном выражении).
Как я могу передать такую функцию, как func_to_integrate интегратору?
Кажется, очень легко исправить. Просто добавьте еще два аргумента к вашей подписи функции указателя.
double integrator(double (*func_to_integrate)(double,double,double,double));
Как отмечалось в предыдущих комментариях, наиболее элегантным решением было бы использование 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 ); }
};
Конструкция этого класса очень низка, поэтому я сделал членов неизменными. Я не придумал очень хороших имен для класса и функций, вы должны больше думать об этих именах, чем я.
Большинство ответов, которые я видел для такого рода вопросов, основаны на 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 я считаю, что это решение элегантно подходит для данного варианта использования.