2016-03-06 4 views
2

Это очень простой вопрос, и я уверен, что на это был дан ответ раньше, но я не знаю, что искать.Передайте функцию 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?

Приветствие

Edit: Как Alain отметил в комментариях, отчасти это дубликат: How can currying be done in C++?

Есть ли элегантное решение делать операцию Карринга на указателе функции?

+2

Когда 'интегратор' называет' func_to_integrate', какими должны быть значения 'mu' и' w0'? Возможно, вы захотите изучить [лямбда-функции] (http://en.cppreference.com/w/cpp/language/lambda) или ['std :: bind'] (http://en.cppreference.com/w/cpp/utility/functional/bind) – Cornstalks

+0

Они должны быть в нескольких двойниках, которыми я могу манипулировать в цикле. Я пробовал функции лямбда и связывал. Для обоих я не смог создать объект, который бы принял интегратор. – Cukhen

+0

Почему ваша функция и интегратор не возвращают 'double'? –

ответ

2

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

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

Обе альтернативы позволяют передавать общие функциональные объекты (функторы, lambdas, 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); }; 

и передать е к вашему (adusted) integrator рутина.


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

Сначала я подумал, что в этом случае нет решения, так как вы не можете привязать общий функтор к указателю на функцию. Но затем я столкнулся с хорошей идеей в this answer (которую я немного скорректировал): закодируйте все в терминах статической переменной 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)); 

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

1

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

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

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

Как и в предыдущих комментариях, самое элегантное решение было бы использовать 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 я считаю, что это решение является элегантным подгонки для данного варианта использования.