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

标签 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 );
    }
}

最佳答案

您应该能够使用 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 与模板的性能是很自然的,但我不会在没有首先进行基准测试和分析的情况下省略可能的解决方案。

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

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 毫秒

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

我会用您的确切代码和数据集做一个类似的基准测试,看看各种设计有什么区别(如果有的话)。如果您真的只有问题中显示的两种情况,那么“if (flag)”方法可能是一个不错的选择。

关于c++ - 由具有不同数量参数的其他函数参数化的函数模板,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30780522/

相关文章:

c++ - 如何使用 mingw 在 Windows 中静态链接 qt 应用程序

c++ - 将数组传递给函数而不更改其值?

c++ - 如何使用 extern 在 C++ 中的源文件之间共享 globe 变量?

c++ - 如何根据目标文件中缺少的符号来实例化模板?

c++ - 模板中内联函数的好处

c++ - 模板类中特殊函数的声明

c++ - 错误使用指针或错误?

python - 如何在Python中委托(delegate) `__iter__`方法

c++ - 获取此对象的原始类型

c++ - 捕获数组模板参数的大小