在共享库中的类中调用GSL函数

时间:2013-10-18 12:53:35

标签: c++ class shared-libraries gsl

我正在尝试用c ++实现一个共享库,为Fermi气体实现工具。我正在使用GSL库以数字方式解决函数,并且我的代码在没有作为脚本运行时运行没有问题,但是当我尝试将其转换为共享库和类时遇到问题。

我见过类似的问题: Q1 Q2 Q3

我对c ++很新 - 编程似乎无法适应我的问题的不同答案。可能因为我不太明白答案。

我的代码是:

/* Define structure for the GSL-function: chempot_integrand */
struct chempot_integrand_params { double mu; double T; };

double
ChemicalPotential::chempot_integrand (double x, void * params){
    /* Computes the integrand for the integral used to obtain the chemical potential.
     *
     * This is a GSL-function, which are integrated using gsl_integration_qag.
     */

    // Get input parameters.
    struct chempot_integrand_params * p = (struct chempot_integrand_params *) params;
    double mu = p->mu;
    double T = p->T;

    // Initiate output parameters for GSL-function.
    gsl_sf_result_e10 result;
    int status = gsl_sf_exp_e10_e( ( gsl_pow_2(x) - mu ) / T , &result );

    if (status != GSL_SUCCESS){
        printf ("Fault in calculating exponential function.");
    }

    // Return (double) integrand.
    return (gsl_pow_2(x) / ( 1 + result.val * gsl_sf_pow_int(10,result.e10) ));
}

/* Define structure for the GSL-function: chempot_integration */
struct chempot_integral_params { double T; };

double
ChemicalPotential::chempot_integration (double mu, double T){
    /* Computes the integral used to obtain the chemical potential using the integrand: chempot_integrand.
    */

    // Set input parameters for the integrand: chempot_integrand.
    struct chempot_integrand_params params_integrand = { mu, T };

    // Initiate the numerical integration.
    gsl_integration_workspace * w = gsl_integration_workspace_alloc (1000); // Allocate memory for the numerical integration. Can be made larger if neccessary, REMEMBER to change it in the function call: gsl_integration_qag as well.
    double result, error;
    gsl_function F;
    F.function = &ChemicalPotential::chempot_integrand;
    F.params = &params_integrand;

    // Upper limit for integration
    double TOL = 1e-9;
    double upp_lim = - T * gsl_sf_log(TOL) + 10;

    gsl_integration_qag (&F, 0, upp_lim, 1e-12, 1e-12, 1000, 6, w, &result, &error);

    // Free memory used for the integration.
    gsl_integration_workspace_free (w);

    return result;
}

编译时我得到错误

error: cannot convert ‘double (Fermi_Gas::ChemicalPotential::*)(double, void*)’ to ‘double (*)(double, void*)’ 

在行

F.function = &ChemicalPotential::chempot_integrand;

2 个答案:

答案 0 :(得分:3)

人们一遍又一遍地问这个确实很有意思。一个原因可能是提出的解决方案不容易理解。我一个人在理解和实施它们时遇到了问题。 (对于我来说,解决方案没有开箱即用,正如您所料。)

tlamadon的帮助下,我刚刚找到了一个可能对此有帮助的解决方案。让我们看看你们的想法。

所以回顾一下,问题是你有一个包含成员函数的类,你想用GSL库中的东西来操作它。如果GSL接口需要

,我们的示例很有用
gsl_function F;

see here for a definition.

所以这是示例类:

class MyClass {

    private:
        gsl_f_pars *p;  // not necessary to have as member

    public: 
        double obj(double x, void * pars);  // objective fun
        double GetSolution( void );  
        void setPars( gsl_f_pars * xp ) { p = xp; };
        double getC( void )  ;  // helper fun

};

本练习的目的是能够

  1. 发起MyClass test
  2. 为它提供参数结构(或编写相应的构造函数)和
  3. 在其上调用test.GetSolution(),它应该返回用于GSL函数的任何内容(obj的最小值,根,积分或其他)
  4. 现在的诀窍是在参数struct gsl_f_pars中添加一个元素,其中是指向MyClass 的指针。这是结构:

    struct gsl_f_pars {
        double a;
        double b;
        double c;
        MyClass * pt_MyClass;
    };
    

    最后一部分是提供一个将在MyClass::GetSolution()内调用的包装器(包装器是成员函数MyClass::obj的替代品,我们不能只指向&obj在课堂内)。这个包装器将采用参数struct,dereference pt_MyClass并评估pt_MyClass的成员obj

    // Wrapper that points to member function
    // Trick: MyClass is an element of the gsl_f_pars struct
    // so we can tease the value of the objective function out
    // of there.
    double gslClassWrapper(double x, void * pp) {
        gsl_f_pars *p = (gsl_f_pars *)pp;
        return p->pt_MyClass->obj(x,p);
    }
    

    完整的例子有点太长了,不能发布在这里,所以我提出了一个要点。这是一个header file和一个cpp file,它应该在你拥有GSL的任何地方工作。编译并运行

    g++ MyClass.cpp -lgsl -o test
    ./test
    

答案 1 :(得分:0)

这是一个重复的问题。例如,请参阅Q1Q2。您的问题如下:您无法将指向成员函数的指针转换为释放函数指针。要解决您的问题,有两种选择。您可以将您的成员函数定义为static(在90%的情况下这是不好的,因为成员函数不会附加到您的类的任何实例化,这就是为什么您可以将它转换为自由函数)或者您可以使用包装器you linked将使用引擎盖下的静态成员函数使您的代码与gsl兼容,而无需将您的特定成员函数声明为静态。

编辑@Florian Oswald。基本上你的整个解决方案可以使用std :: bind我之前引用的包装器在两行中实现

gsl_function_pp Fp( std::bind(&Class::member_function, &(*this),  std::placeholders::_1) );
gsl_function *F = static_cast<gsl_function*>(&Fp);

实际上,这只是纯C代码中的一个额外行!

正如我在评论中所述,使用额外的全局结构和额外的全局函数包装要集成的每个成员函数都很麻烦,并且使用许多额外的函数/结构来污染您的代码,而这些函数/结构完全没有必要。如果我们拒绝使用使C ++强大且有用的功能(与C相比),为什么要使用c ++?

另一个经典示例:如果要传递大量参数,请使用lambda函数(没有额外的struct或全局函数)!

更确切地说:想象一下你有2个参数(双打)。

 //Declare them (locally) here
   double a1  = ...;
   double a2  = ...;
 // Declare a lambda function that capture all of them by value or reference
 // no need to write another struct with these 2 parameters + class pointer
   auto ptr = [&](double x)->double {/.../};
 // Cast to GSL in 3 lines using the wrapper 
 std::function<double(double)> F1(ptr);
 gsl_function_pp F2(F1);
 gsl_function *F = static_cast<gsl_function*>(&F2); 

没有全局函数的额外全局结构,也没有额外的包装器(解决集成成员函数问题的相同包装器也解决了集成lambda表达式的问题)。当然这最终是一个风格问题,但由于没有这些很好的功能允许使用C库而没有代码膨胀,我永远不会离开C。