как избежать статической функции-члена при использовании gsl с С++

Я хотел бы использовать GSL в классе С++ без объявления функций-членов как static. Причина этого в том, что я не слишком хорошо их знаю и не уверен в безопасности потоков. Из того, что я читал, std::function может быть решением, но я не уверен, как его использовать.

Мой вопрос сводится к тому, как удалить static в объявлении 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";
}

Обновление (1):

Я пытался изменить gsl_monte_function G = { &g, 3, 0 }; на gsl_monte_function G = { bind(&A::g, this,_1,_2,_3), 3, 0 };, но это не сработало.

Обновление (2): я попытался использовать назначение std::function в функцию-член, но это тоже не сработало.

Обновление (3) в конце концов я написал функцию, не являющуюся членом:

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

Это сработало, но это беспорядочное решение, потому что мне нужно было написать вспомогательную функцию. С лямбда-выражениями, функциями и привязками должен быть способ, чтобы все было логично внутри класса.


person kirill_igum    schedule 25.10.2012    source источник
comment
Я знаю, что мой ответ пришел довольно поздно, но я надеюсь, что этот урок поможет вам в будущем. Эта обертка очень удобна, потому что она также позволяет вам интегрировать лямбда-функции или связывать функции с более чем одним параметром (например, если вы хотите интегрировать f(x, a) = a x, где a — параметр).   -  person Vivian Miranda    schedule 31.08.2013


Ответы (4)


Вы можете легко обернуть функции-члены, используя следующий код (это хорошо известное решение)

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

Затем вы можете использовать std::bind для переноса функции-члена в std::function. Пример:

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

Однако вы должны знать о снижении производительности std::function, прежде чем включать функции-члены в программу интеграции gsl. См. раздел template и std::function . Чтобы избежать этого падения производительности (которое может быть или не быть критическим для вас), вы должны использовать шаблоны, как показано ниже.

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

В этом случае для вызова функции-члена вам потребуется следующее

 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: ссылка template vs std::function объясняет, что компилятору обычно легче оптимизация шаблонов, чем std::function (что критично для производительности, если ваш код выполняет тяжелые числовые вычисления). Таким образом, даже сложный обходной путь во втором примере кажется более громоздким, я бы предпочел шаблоны, а не std::function.

person Vivian Miranda    schedule 12.08.2013
comment
Это хорошее решение, но оно по-прежнему использует статические функции-члены. в любом случае, я отмечаю это как ответ, чтобы закрыть вопрос. я отошел от gsl и теперь использую odeint, поэтому я не проверял его. - person kirill_igum; 01.09.2013

GSL использует функции C-типа “int (*)(char,float)”, а не C++-типа “int (Fred::*)(char,float)”. Чтобы преобразовать функцию-член в функцию C-типа, вам нужно добавить static.

см. Является ли тип функции "указатель на член" " отличается от "указатель на функцию"?

person kirill_igum    schedule 18.06.2014

Почему вы беспокоитесь о статической функции в этом случае? Переменные и/или объекты, объявленные в статической функции, не распределяются между разными потоками, если только они сами не являются статическими (что в вашем случае не является таковым).

Ваш код что-то не делает?

person krolth    schedule 29.10.2012
comment
не сейчас, но это часть более крупного кода, который я буду продолжать расширять. Сейчас я использую статическое, но я бы предпочел нестатическое решение. еще одним преимуществом было бы понимание bind и function - person kirill_igum; 29.10.2012

Извините, но то, что вы пытаетесь сделать, не имеет никакого смысла. Какие бы проблемы безопасности нитей вас не беспокоили, они не будут решены добавлением или удалением ключевого слова static.

Единственная причина, по которой вы должны сделать g нестатическим, заключается в том, что экземпляр A каким-то образом требуется для работы g. И текущая реализация g не требует такого экземпляра.

Обратите внимание, что вы также можете сделать g глобальной функцией без ключевого слова static. Видимой разницы в вашем случае не будет. Однако в вашем случае лучше иметь g в классе, который использует его как статическую функцию.

Кроме того, здесь есть некоторый связанный материал об указателях на (static/ нестатические) функции-члены.

person Danra    schedule 29.10.2012
comment
в моем полном коде функция g зависит от члена std::function. Я получаю сообщение об ошибке, если делаю g статическим invalid use of member ... in static member function. тогда я должен сделать это std::function статическим, но тогда у меня возникает проблема с назначением ему функции из другого класса. - person kirill_igum; 30.10.2012