3

我正在尝试在 C++ 中为费米气体实现工具创建一个共享库。我正在使用 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;
4

2 回答 2

3

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

在tlamadon的帮助下,我刚刚想出了一个在这里也可能有用的解决方案。让我们看看你们是怎么想的。

回顾一下,问题在于您有一个包含成员函数的类,您希望在该类上使用 GSL 库中的某些内容进行操作。如果 GSL 接口需要一个

gsl_function F;

请参阅此处了解定义。

所以这里是示例类:

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、根、积分或其​​他)

现在的诀窍是在参数中放置一个元素,struct gsl_f_pars是指向 MyClass 的指针。这是结构:

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

最后一点是提供一个将在内部调用MyClass::GetSolution()的包装器(包装器是成员函数的替代品MyClass::obj,我们不能只&obj在类内部指向它)。这个包装器将采用参数结构、取消引用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);
}

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

g++ MyClass.cpp -lgsl -o test
./test
于 2013-10-22T16:30:03.420 回答
1

这是一个重复的问题。例如,参见Q1Q2。您的问题如下:您不能将指向成员函数的指针转换为自由函数指针。要解决您的问题,有两种选择。您可以将您的成员函数定义为静态(这在 90% 的情况下是不好的,因为该成员函数不会附加到您的类的任何实例化,这就是您可以将其转换为自由函数的原因),或者您可以使用您链接的包装器将在后台使用静态成员函数来使您的代码与 gsl 兼容,而无需声明您的特定成员函数为静态的。

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

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

在实践中,这只是纯 C 代码中的 1 行!

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

另一个经典例子:如果你想传递很多参数,使用 lambda 函数(没有额外的结构或全局函数)!!!

更准确地说:假设您有 2 个参数 (doubles) 。

 //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。

于 2013-10-18T18:00:43.227 回答