函数模板由具有不同参数数量的其他函数参数化

时间:2015-06-11 12:15:21

标签: c++ templates metaprogramming

我能够通过其他函数对函数模板进行参数化,但是,当我想通过具有不同数量的参数的函数对其进行参数化时,我不知道该怎么做。

请参阅此代码:

#include <stdio.h>
#include <math.h>

template < double FUNC( double a ) >
void seq_op( int n, double * as ){
    for (int i=0; i<n; i++){  printf( " %f \n", FUNC( as[i] )  ); }
} 

template < double FUNC( double a, double b ) >
void seq_op_2( int n, double * as, double * bs ){
    for (int i=0; i<n; i++){  printf( " %f \n", FUNC( as[i], bs[i] )  ); }
} 

double a_plus_1  ( double a ){ return a + 1.0; }
double a_sq      ( double a ){ return a*a;     }

double a_plus_b ( double a, double b ){ return a + b; }
double a_times_b( double a, double b ){ return a * b; }


double as[5] = {1,2,3,4};
double bs[5] = {2,2,2,2};

// FUNCTION ======  main
int main(){
    printf( "seq_op   <a_plus_1>  ( 5, as );\n");      seq_op   <a_plus_1>  ( 4, as );
    printf( "seq_op   <a_sq>      ( 5, as );\n");      seq_op   <a_sq>      ( 4, as );
    printf( "seq_op_2 <a_plus_b>  ( 5, as, bs );\n");  seq_op_2 <a_plus_b>  ( 4, as, bs );
    printf( "seq_op_2 <a_times_b> ( 5, as, bs );\n");  seq_op_2 <a_times_b> ( 4, as, bs );
}

是否有办法为这两种情况制作通用模板?

为什么我需要这么傻的东西?一个更实际的例子是这两个函数只有一行不同:

#define i3D( ix, iy, iz )  ( iz*nxy + iy*nx + ix  ) 

void getLenardJonesFF( int natom, double * Rs_, double * C6, double * C12 ){
    Vec3d * Rs = (Vec3d*) Rs_;
    int nx  = FF::n.x;
    int ny  = FF::n.y;
    int nz  = FF::n.z;
    int nxy = ny * nx;
    Vec3d rProbe;  rProbe.set( 0.0, 0.0, 0.0 ); // we may shift here
    for ( int ia=0; ia<nx; ia++ ){ 
        printf( " ia %i \n", ia );
        rProbe.add( FF::dCell.a );  
        for ( int ib=0; ib<ny; ib++ ){ 
            rProbe.add( FF::dCell.b );
            for ( int ic=0; ic<nz; ic++ ){
                rProbe.add( FF::dCell.c );
                Vec3d f; f.set(0.0,0.0,0.0);
                for(int iatom=0; iatom<natom; iatom++){
                    // only this line differs
                    f.add( forceLJ( Rs[iatom] - rProbe, C6[iatom], C12[iatom] ) );
                }
                FF::grid[ i3D( ia, ib, ic ) ].add( f );
            } 
            rProbe.add_mul( FF::dCell.c, -nz );
        } 
        rProbe.add_mul( FF::dCell.b, -ny );
    }
}

void getCoulombFF( int natom, double * Rs_, double * kQQs ){
    Vec3d * Rs = (Vec3d*) Rs_;
    int nx  = FF::n.x;
    int ny  = FF::n.y;
    int nz  = FF::n.z;
    int nxy = ny * nx;
    Vec3d rProbe;  rProbe.set( 0.0, 0.0, 0.0 ); // we may shift here
    for ( int ia=0; ia<nx; ia++ ){ 
        printf( " ia %i \n", ia );
        rProbe.add( FF::dCell.a );  
        for ( int ib=0; ib<ny; ib++ ){ 
            rProbe.add( FF::dCell.b );
            for ( int ic=0; ic<nz; ic++ ){
                rProbe.add( FF::dCell.c );
                Vec3d f; f.set(0.0,0.0,0.0);
                for(int iatom=0; iatom<natom; iatom++){
                    // only this line differs
                    f.add( forceCoulomb( Rs[iatom] - rProbe, kQQs[iatom] );
                }
                FF::grid[ i3D( ia, ib, ic ) ].add( f );
            } 
            rProbe.add_mul( FF::dCell.c, -nz );
        } 
        rProbe.add_mul( FF::dCell.b, -ny );
    }
}

1 个答案:

答案 0 :(得分:0)

您应该能够使用std::bind()std::function()的组合来组合这两个功能(请参阅code on coliru):

#include <stdio.h>
#include <functional>

using namespace std::placeholders;


double getLJForceAtoms (int, int, double*, double*, double*)
{
    printf("getLJForceAtoms\n");
    return 0;
}


double getCoulombForceAtoms (int, int, double*, double*)
{
    printf("getCoulombForceAtoms\n");
    return 0;
}


void getFF (int natom, double* Rs_, std::function<double(int, int, double*)> GetForce)
{
    int rProbe = 1;

    double Force = GetForce(rProbe, natom, Rs_);
}


int main ()
{
    double* C6 = nullptr;
    double* C12 = nullptr;
    double *kQQs = nullptr;
    double* Rs_ = nullptr;

    auto getLJForceFunc = std::bind(getLJForceAtoms, _1, _2, _3, C6, C12);
    auto getCoulombForceFunc = std::bind(getCoulombForceAtoms, _1, _2, _3, kQQs);

    getFF(1, Rs_, getLJForceFunc);
    getFF(1, Rs_, getCoulombForceFunc);

    return 0;
}
输出预期的

getLJForceAtoms
getCoulombForceAtoms

更新 - 性能

虽然关注使用std::function vs模板的性能是很自然的,但如果没有首先进行基准测试和分析,我就不会省略可能的解决方案。

我无法直接比较性能,因为我需要完整的源代码和输入数据集来制作准确的基准测试,但我可以做一个非常简单的测试来向您展示它的外观。如果我使用力函数做了一些工作:

double getLJForceAtoms (int x, int y, double* r1, double* r2, double* r3)
{
    return cos(log2(abs(sin(log(pow(x, 2) + pow(y, 2))))));
}

然后有一个非常简单的getFF()函数调用它们1000万次我可以在各种设计方法(VS2013上的测试,发布版本,快速优化标志)之间进行粗略比较:

  • 直接致电 = 1900毫秒
  • 切换 = 1900毫秒
  • 如果(标记) = 1900毫秒
  • 虚拟功能 = 2400毫秒
  • std :: function = 2400 ms

因此std::function方法在这种情况下大约慢25%但是switch和if方法的速度与直接调用大小相同。根据您的实际力量功能的工作量,您可能会得到更糟或更好的结果。目前,编译器优化器和CPU分支预测器足以完成很多可能令人惊讶甚至反直觉的事情,这就是必须进行实际测试的原因。

我会使用您的确切代码和数据集进行类似的基准测试,看看各种设计有什么区别(如果有的话)。如果您的问题中只显示了两个案例,那么&#34; if(flag)&#34;方法可能是一个不错的选择。