6

だから私はc++11ラムダで使用される統合関数を書こうとしています。コードは次のようになります。

double Integrate(std::function<double(double,void*)> func, double a,double b,std::vector<double> & params)
{
  gsl_integration_workspace * w = gsl_integration_workspace_alloc (1000);
  gsl_function F;
  F.function =func;
  F.params = (void*)&params;
  double error,result;
  gsl_integration_qag (&F, a, b, 0, 1e-7, 1000,GSL_INTEG_GAUSS61,w, &result, &error);
  gsl_integration_workspace_free (w);
  return result;
}

void Another_function()
{
 //...
Integrate([](double a,void* param)
   {
   return ((vector<double> *)params)->at(0)*a+((vector<double> *)params)->at(1);
   }
   ,0,3,{2,3});
}

これをコンパイルしようとすると、コンパイラは次のように言います。

error: cannot convert ‘std::function<double(double, void*)>’ to ‘double (*)(double, void*)’ in assignment

ラインについて

F.function =func;

しかし、私が書く場合:

F.function =[](double a,void* param)
   {
   return ((std::vector<double> *)param)->at(0)*a+((std::vector<double> *)param)->at(1);
   };

コンパイルして正常に動作します。これをどのように解決すればよいですか?

4

5 に答える 5

4

void *を使用することは、関数にいくつかの「状態」を渡すためのCコールバックインターフェースの典型です。ただし、std :: functionは「ステートフル関数」をサポートしているため、std::functionはこれを必要としません。したがって、次のようなことができます。

double Integrate(
          std::function<double(double)> func,
          double a, double b)
{
    typedef std::function<double(double)> fun_type;
    :::
    F.function = [](double x, void* p){
        return (*static_cast<fun_type*>(p))(x);
    };
    F.params = &func;
    :::
}

std :: functionオブジェクトにカプセル化されるファンクターの一部としてパラメーターベクトルへの参照を格納するか、次のようにします。

void Another_function()
{
    double m = 2;
    double b = 3;
    auto func = [&](double x){return m*x+b};
    auto r1 = Integrate(func,0,3);
    :::
}

ただし、このソリューションではかなり多くの間接化が使用されます。GSLはラムダを呼び出します。ラムダはstd::function <> :: operator()を呼び出します。これにより、型消去に使用されるある種の仮想関数が呼び出され、実際の計算が呼び出されます。

したがって、パフォーマンスを気にする場合は、そこにあるいくつかのレイヤー、具体的にはstd::functionを取り除くことができます。関数テンプレートを使用した別のアプローチは次のとおりです。

template<class Func>
double Integrate(
          Func func,
          double a, double b)
{
    :::
    F.function = [](double x, void* p)->double{
        return (*static_cast<Func*>(p))(x);
    };
    F.params = &func;
    :::
}

std::functionソリューションよりもこれを好むと思います。

于 2012-11-08T13:02:49.980 に答える
3

gslライブラリには関数ポインタが必要なようです。キャプチャしないラムダは、関数ポインタに変換できます。任意のラムダをに変換できますstd::function。ただし、std::functionaを関数ポインタに変換することはできません。

あなたは試すことができます:

struct functor_and_params {
  std::function<double(double, void*)> f;
  void* params;
  static double invoke(double x, void* ptr) {
      functor_and_params& f_and_p = *reinterpret_cast<functor_and_params*>(ptr);
      return f_and_p.f(x, f_and_p.params);
  }
};

double Integrate(std::function<double(double,void*)> func,
                 double a,double b,std::vector<double> & params) {
    functor_and_params f_and_p{ func, &params };
    gsl_function F;
    F.function = &functor_and_params::invoke;
    F.params = &f_and_p;
    //...
 }
于 2012-11-08T13:00:18.830 に答える
3

Astd::function<>を関数ポインタに変換することはできません。std::function<>は、通常の関数がステートレスであるのに対し、潜在的に状態を保持できる関数オブジェクトです(一種、static変数を持つ可能性がありますが、それは別のことです)。

一方、ステートレスラムダは関数ポインターに変換できるため、関数のシグネチャを変更して関数ポインターを直接取得すると、ラムダが変換される可能性があります。

double Integrate(double(*func)(double,void*), double a, double b, 
                 std::vector<double> & params) // !!!

std::vector<double> p{2,3};
Integrate([](double a,void* param)
   {
      std::vector<double> *p = static_cast<std::vector<double>*>param;
      return p->at(0)*a+p->at(1);
   }
   ,0,3,p);

右辺値を非定数参照にバインドすることは違法であるため{2,3}、最後の引数として合法的に渡すことはできませんIntegrate(Visual Studioで許可されている場合でも)。名前付き変数を作成する必要があります。

于 2012-11-08T13:04:18.933 に答える
3

void *ラッパー関数内に変換をカプセル化するのが最善です。

double Integrate(std::function<double(double)> func, double a, double b)
{
  gsl_integration_workspace * w = gsl_integration_workspace_alloc (1000);
  gsl_function F;
  F.function = [](double a, void *param) {
    return (*static_cast<std::function<double(double)> *>(param))(a); };
  F.params = (void*)&func;
  double error,result;
  gsl_integration_qag (&F, a, b, 0, 1e-7, 1000,GSL_INTEG_GAUSS61,w, &result, &error);
  gsl_integration_workspace_free (w);
  return result;
}

void Another_function()
{
  //...
  std::vector<double> params = {2, 3};
  Integrate([params](double a) { return (params[0]*a+params[1]; }, 0, 3);
}

ここ(から)にはある程度の過剰な間接参照std::functionがありますが、間接参照は常に同じラムダに対して行われるため、CPUの分岐予測はうまく機能します。

于 2012-11-08T13:24:19.890 に答える
3

ラムダ関数をキャプチャと統合する必要がある場合(この場合、rawポインターへの変換はありません)、およびstd :: functionに関連付けられたパフォーマンスペナルティを持たせたくない場合(sellibitzeで指摘されているように-stdを参照):: function vs template)、次のラッパーを使用できます

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

これがその使用方法を示すテストコードです

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

本当にstd::functionを使用したい場合は、このバージョンのラッパーを使用できます

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

この場合のテストコードはさらに単純です

double a = 1;
gsl_function_pp Fp([=](double x)->double{return a*x;}); 
gsl_function *F = static_cast<gsl_function*>(&Fp);  

これらのラッパーの良いところは、クラスメンバー関数を統合するためにも使用できることです。

于 2013-08-23T23:14:32.987 に答える