2013-04-11 158 views
8

长时间浏览器,这里首次提问。我写了很多脚本来完成各种1D数值积分方法,并将它们编译成一个库。我希望图书馆尽可能灵活地考虑其能够整合的内容。C++:以任意数量参数作为参数传递函数

这里我举一个例子:一个非常简单的梯形法则示例,其中我将一个指针传递给要集成的函数。

// Numerically integrate (*f) from a to b 
// using the trapezoidal rule. 
double trap(double (*f)(double), double a, double b) { 
    int N = 10000; 
    double step = (b-a)/N; 
    double s = 0; 
    for (int i=0; i<=N; i++) { 
    double xi = a + i*step; 
    if (i == 0 || i == N) { s += (*f)(xi); } 
    else { s += 2*(*f)(xi); } 
    } 
    s *= (b-a)/(2*N); 
    return s; 
} 

这适用于只有一个参数的简单函数。例如:

double a = trap(sin,0,1); 

但是,有时我可能想要集成更多参数的东西,如二次多项式。在这个例子中,系数将在整合之前由用户定义。示例代码:

// arbitrary quadratic polynomial 
double quad(double A, double B, double C, double x) { 
    return (A*pow(x,2) + B*x + C); 
} 

理想情况下,我将能够做这样的事情来整合它:

double b = trap(quad(1,2,3),0,1); 

但显然不起作用。我已经通过定义具有系数作为成员的利益为成员函数的函数的类解决此问题得到:

class Model { 
    double A,B,C; 
public: 
    Model() { A = 0; B = 0; C = 0; } 
    Model(double x, double y, double z) { A = x; B = y; C = z; } 
    double func(double x) { return (A*pow(x,2)+B*x+C); } 
}; 

然而,然后我的积分功能需要改变把对象作为输入,而不是一个函数指针:

// Numerically integrate model.func from a to b 
// using the trapezoidal rule. 
double trap(Model poly, double a, double b) { 
    int N = 10000; 
    double step = (b-a)/N; 
    double s = 0; 
    for (int i=0; i<=N; i++) { 
    double xi = a + i*step; 
    if (i == 0 || i == N) { s += poly.func(xi); } 
    else { s += 2*poly.func(xi); } 
    } 
    s *= (b-a)/(2*N); 
    return s; 
} 

这工作得很好,但由此产生的图书馆是不是很独立的,因为它需要在某处被定义的类模型。此外,理想情况下,模型应该能够从用户到用户改变,所以我不想修复它在一个头文件。我试图使用函数模板和函数来获得这个工作,但它不是很独立,因为模板应该在头文件中定义(除非你想显式实例化,我不这样做)。总结:有没有什么办法可以让我的集成函数接受具有可变数量输入参数的任意1D函数,同时仍然保持足够的独立性,以便可以将它们编译为独立的库?在此先感谢您的建议。

+0

['std :: bind'](http://en.cppreference.com/w/cpp/utility/functional/bind) – 2013-04-11 23:21:09

回答

8

您需要的是模板和std::bind()(或其对应的boost::bind(),如果您买不起C++ 11)。举例来说,这是你的trap()功能会变成什么样子:

template<typename F> 
double trap(F&& f, double a, double b) { 
    int N = 10000; 
    double step = (b-a)/N; 
    double s = 0; 
    for (int i=0; i<=N; i++) { 
    double xi = a + i*step; 
    if (i == 0 || i == N) { s += f(xi); } 
//        ^
    else { s += 2* f(xi); } 
//    ^
    } 
    s *= (b-a)/(2*N); 
    return s; 
} 

请注意,我们从函数指针推广,并允许任何类型可调用的对象的(包括C++ 11的λ,为实例)因此,调用用户提供的函数的语法不是*f(param)(仅适用于函数指针),而只是f(param)

关于灵活性,让我们考虑两个硬编码功能(并假装他们是有意义的):

double foo(double x) 
{ 
    return x * 2; 
} 

double bar(double x, double y, double z, double t) 
{ 
    return x + y * (z - t); 
} 

现在你可以直接输入到trap()同时提供了第一个功能,或者结合的最后结果第二个函数的一些特定值的三个参数(你有哪些参数绑定自由选择):

#include <functional> 

int main() 
{ 
    trap(foo, 0, 42); 
    trap(std::bind(bar, std::placeholders::_1, 42, 1729, 0), 0, 42); 
} 

当然,你可以用lambda表达式获得更大的灵活性:

#include <functional> 
#include <iostream> 

int main() 
{ 
    trap(foo, 0, 42); 
    trap(std::bind(bar, std::placeholders::_1, 42, 1729, 0), 0, 42); 

    int x = 1729; // Or the result of some computation... 
    int y = 42; // Or some particular state information... 
    trap([&] (double d) -> double 
    { 
     x += 42 * d; // Or some meaningful computation... 
     y = 1; // Or some meaningful operation... 
     return x; 
    }, 0, 42); 

    std::cout << y; // Prints 1 
} 

而且你还可以通过自己的状态函子TP trap(),或包裹在一个std::function对象一些可调用的对象(或boost::function,如果您不能负担C++ 11)。选择很广泛。

这是live example

+0

谢谢!我喜欢模板的例子,因为它非常优雅,但我不确定它会对我有用 - 我的理解是模板函数必须在同一个地方定义和声明,而我想定义'trap( )'在一个单独的库中,然后声明它用于其他项目。我把你的建议与'std :: bind'一起使用'std :: function',并将'trap()'重新定义为'双重陷阱(std :: function f,double a,double b )';唯一令人讨厌的部分是我必须重新定义简单的函数,如sin作为'std :: function'。 – t354 2013-04-12 14:19:11

+2

@ t354:是的,模板受到这种分离问题的困扰:定义必须位于头文件中。或者,您可以*将定义放在'.cpp'文件中,并为您的应用程序中使用的所有可能参数提供模板的所谓*显式实例*,但如果您的应用程序是库,则无法预测你的模板将如何实例化。把'std :: function'放在签名中是OK的:要知道有一个运行时间的开销,所以你必须测量它是否对你有用。 – 2013-04-12 14:27:49

+1

@ t354:我不明白最后一部分:你是什么意思“重新定义简单的函数,如'罪'”?如果你的意思是这样的话:'std :: function = sin;陷阱(罪,0,42);',而不是不需要。你可以直接调用'trap(sin,0,42);',而不用声明std :: function'对象。 – 2013-04-12 14:28:59

3

你所要做的就是让这一切成为可能

trap(quad, 1, 2, 3, 0, 1); 

用C++ 11我们有别名模板和可变参数模板

template< typename... Ts > 
using custom_function_t = double (*f) (double, Ts...); 

上述定义custom_function_t是采取双重和可变参数数量。

所以你trap功能变得

template< typename... Ts > 
double trap(custom_function_t<Ts...> f, Ts... args, double a, double b) { 
    int N = 10000; 
    double step = (b-a)/N; 
    double s = 0; 
    for (int i=0; i<=N; i++) { 
     double xi = a + i*step; 
     if (i == 0 || i == N) { s += f(xi, args...); } 
     else { s += 2*f(xi, args...); } 
    } 
    s *= (b-a)/(2*N); 
    return s; 
} 

用法:

double foo (double X) { 
    return X; 
} 

double quad(double X, double A, double B, double C) { 
    return(A*pow(x,2) + B*x + C); 
} 

int main() { 
    double result_foo = trap(foo, 0, 1); 
    double result_quad = trap(quad, 1, 2, 3, 0, 1); // 1, 2, 3 == A, B, C respectively 
} 

测试苹果LLVM编译器4.2。

+0

这只是令人困惑 – 2013-04-11 23:22:44

+0

@MooingDuck我同意... – yngccc 2013-04-11 23:31:42