Calling GSL function inside a class in a shared library

前端 未结 2 888
时光说笑
时光说笑 2020-12-18 13:15

I\'m trying make a shared library in c++ implementing tools for Fermi gases. I\'m using the GSL library to solve a function numerically and my code runs without a problem wi

相关标签:
2条回答
  • 2020-12-18 13:41

    This is a duplicate question. See Q1 or Q2 for example. Your problem is the following: you cannot convert pointers to member functions to free function pointers. To solve your problem, there are two options. You can define your member function as static (which is bad in 90% of the case because the member function will not be attached to any instantiation of your class and that is why you can convert it to a free function) or you can use the wrapper you linked that will use a static member functions under the hood to make your code compatible with gsl without the need of declaring your particular member function static.

    EDIT @Florian Oswald. Basically your entire solution can be implemented in 2 lines using std::bind the wrapper I cited before

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

    In practice is this is just 1 extra line from a pure C code!

    As I stated in a comment, wrapping every member function that you want to integrate using an extra global struct and an extra global function is cumbersome and pollute your code with a lot of extra functions/struct that are completely unnecessary. Why use c++ if we refuse to use the features that make C++ powerful and useful (in comparison to C)?

    Another classical Example: if you want to pass a LOT of parameters, use lambda functions (no extra struct or global functions) !!!

    To be more precise: Imagine you have 2 parameters (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); 
    

    No extra global struct of global functions and no extra wrapper (the same wrapper that solved the problem of integrating member function also solved the problem of integrating a lambda expression). Of course this is a matter of style in the end, but in the absence of these nice features that allow the use of C libraries without code bloat, I would never leave C.

    0 讨论(0)
  • 2020-12-18 13:57

    It is indeed interesting that people ask this over and over again. One reason may be that the proposed solutions are not easy to understand. I for one had problems understanding and implementing them. (the solutions did not work out of the box for me, as you might expect.)

    With the help of tlamadon I just figured out a solution that may be helpful here as well. Let's see what you guys think.

    So just to recap, the problem is that you have a class that contains a member function on which you want to operate with something from the GSL library. Our example is useful if the GSL interface requires a

    gsl_function F;
    

    see here for a definition.

    So here is the example class:

    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
    
    };
    

    The objective of this exercise is to be able to

    1. initiate MyClass test,
    2. supply it with a paramter struct (or write a corresponding constructor), and
    3. call test.GetSolution() on it, which should return whatever the GSL function was used for (the minimum of obj, a root, the integral or whatever)

    The trick is now to put have an element in the parameter struct gsl_f_pars which is a pointer to MyClass. Here's the struct:

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

    The final piece is to provide a wrapper that will be called inside MyClass::GetSolution() (the wrapper is a stand in for the member function MyClass::obj, which we cannot just point to with &obj inside the class). This wrapper will take the parameter struct, dereference pt_MyClass and evaluate pt_MyClass's member 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);
    }
    

    The full example is a bit too long to post here, so I put up a gist. It's a header file and a cpp file, it should be working wherever you have GSL. Compile and run with

    g++ MyClass.cpp -lgsl -o test
    ./test
    
    0 讨论(0)
提交回复
热议问题