Search code examples
c++c++11callbackgslstd-function

how to avoid static member function when using gsl with c++


I would like to use GSL within a c++ class without declaring member functions as static. The reason for this is because I don't know them too well and I'm not sure about thread safety. From what I read, std::function might be a solution but I'm not sure how to use it.

My question comes down to how can I remove static in declaration of g?

#include<iostream>
#include <functional>
#include <stdlib.h>
#include <gsl/gsl_math.h>
#include <gsl/gsl_monte.h>
#include <gsl/gsl_monte_plain.h>
#include <gsl/gsl_monte_miser.h>
#include <gsl/gsl_monte_vegas.h>


using namespace std;

class A {
public:
  static double g (double *k, size_t dim, void *params)
  {
    double A = 1.0 / (M_PI * M_PI * M_PI);
    return A / (1.0 - cos (k[0]) * cos (k[1]) * cos (k[2]));
  }
  double result() {
    double res, err;

    double xl[3] = { 0, 0, 0 };
    double xu[3] = { M_PI, M_PI, M_PI };

    const gsl_rng_type *T;
    gsl_rng *r;

    ////// the following 3 lines didn't work ///////
    //function<double(A,double*, size_t, void*)> fg;
    //fg = &A::g;
    //gsl_monte_function G = { &fg, 3, 0 };
    gsl_monte_function G = { &g, 3, 0 };

    size_t calls = 500000;

    gsl_rng_env_setup ();

    T = gsl_rng_default;
    r = gsl_rng_alloc (T);

    {
      gsl_monte_plain_state *s = gsl_monte_plain_alloc (3);
      gsl_monte_plain_integrate (&G, xl, xu, 3, calls, r, s, &res, &err);
      gsl_monte_plain_free (s);
    }

    gsl_rng_free (r);
    return res;
  }
};

main() {
  A a;
  cout <<"gsl mc result is " << a.result() <<"\n";
}

Update (1):

I tried changing gsl_monte_function G = { &g, 3, 0 }; to gsl_monte_function G = { bind(&A::g, this,_1,_2,_3), 3, 0 }; but it didn't work

Update (2): I tried using assigning std::function to a member function but it didn't work either.

Update (3) in the end I wrote a non-member function:

double gmf (double *k, size_t dim, void *params) {
  auto *mf = static_cast<A*>(params);
  return abs(mf->g(k,dim,params));
  //return 1.0;
};

It worked but it's a messy solution because I needed to write a helper function. With lambdas,function and bind, there should be a way to have everything logical within the class.


Solution

  • You can easily wrap member functions using the following code (which is a well known solution)

     class gsl_function_pp : public gsl_function
     {
        public:
        gsl_function_pp(std::function<double(double)> const& func) : _func(func){
          function=&gsl_function_pp::invoke;
          params=this;
        }    
        private:
        std::function<double(double)> _func;
        static double invoke(double x, void *params) {
         return static_cast<gsl_function_pp*>(params)->_func(x);
       }
    };
    

    Then you can use std::bind to wrap the member function in a std::function. Example:

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

    However, you should be aware about the performance penalties of std::function before wrapping member functions inside gsl integration routine. See template vs std::function . To avoid this performance hit (which may or may not be critical for you), you should use templates as shown below

    template< typename F >
      class gsl_function_pp : public gsl_function {
      public:
      gsl_function_pp(const F& func) : _func(func) {
        function = &gsl_function_pp::invoke;
        params=this;
      }
      private:
      const F& _func;
      static double invoke(double x, void *params) {
        return static_cast<gsl_function_pp*>(params)->_func(x);
      }
    };
    

    In this case, to call a member function you need the following

     Class* ptr2 = this;
     auto ptr = [=](double x)->double{return ptr2->foo(x);};
     gsl_function_pp<decltype(ptr)> Fp(ptr);     
     gsl_function *F = static_cast<gsl_function*>(&Fp);   
    

    PS: the link template vs std::function explains that compiler usually has an easier time optimizing templates than std::function (which is critical for performance if your code is doing heavy numerical calculation). So even tough the workaround in the second example seems more cumbersome, I would prefer templates than std::function.