Using GSL functions defined in a structure

2019-07-25 12:41发布

问题:

I want to write a structure containing all the functions (including GSL functions) and parameters for solving an ODE system. From the main function, I only want to call an update function defined in the struct to advance the system by one time-step. When I try this however, I get the error:

Line 27, ERROR:  cannot convert ‘ODE::funcS’ from type ‘int (ODE::)(double, const double*, double*, void*)’ to type ‘int (*)(double, const double*, double*, void*)’ Below is a minimal code. \

Here is a minimal version of my code:

#include <iostream>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_matrix.h>
#include <gsl/gsl_odeiv.h>

struct ODE
{
    void update(double dt)
    {
        // code to advance ODE solution by one time-step dt
    }

    int
    funcS (double t, const double y[], double f[],
          void *params)
    {
        return GSL_SUCCESS;
    }

    double mu = 10;

    gsl_odeiv_system sysS;

    void
    initializeSys()
    {
        sysS.function = funcS; //Line 27
    }
};

int
func (double t, const double y[], double f[],
          void *params)
{
    return GSL_SUCCESS;
}

int main()
{
    // GIVES ERROR
    ODE mySys;
    mySys.update(0.01);


    // WORKS
    double mu = 10;
    gsl_odeiv_system sys;
    sys.function = func;

    return 0;
}

回答1:

You don't need to use static function directly. Instead you can write a very general wrapper.

I believe this is a duplicate question. My answer to the question I just linked is based on the wrapper presented here. However, I generalized it using templates to avoid the performance penalty of std::function due to heap allocation of the functor that std::function holds (the original answer only warns the reader about the penalty that is caused by the multiple indirection involved in std::function implementation, and this is negligible in comparison to the problem caused by heap allocation).

EDIT 1: This issue is also discussed here

EDIT 2 (to answer a question you raised in your first comment to my answer). The first caveat is that you have to make sure that whatever std::function holds is not deleted before GSL finish the calculation. Also, @Managu pointed out that the wrapper itself must not be out of scope while GSL works. This is not hard to enforce if you code carefully. Example of bad code:

 // BAD PROGRAM - EXAMPLE OF WHAT YOU MUST NOT DO. DO NOT COPY THIS CODE
 // HERE THE WRAPPER GETS PREMATURELY OUT OF SCOPE => CRASH
 gsl_function *F 
 auto ptr = [](double x)->double{return 2*x;};
 std::function<double(double)> FF1(std::cref(ptr))

 {      
   gsl_function_pp Fp(FF1);
   F = static_cast<gsl_function*>(&Fp);  
 }
 (...)
 // CALL GSL


标签: c++ gsl