簡體   English   中英

C函數指針與C ++ 11 lambdas

[英]C function pointers with C++11 lambdas

所以我正在嘗試編寫一個與c ++ 11 lambdas一起使用的Integration函數。 代碼看起來像這樣:

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

它編譯並正常工作。 我該怎么解決這個問題?

使用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會調用你的lambda。 你的lambda將調用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解決方案。

看起來gsl庫需要一個函數指針。 可以將不捕獲的lambda轉換為函數指針。 任何lambda都可以轉換為std::function 但是std::function無法轉換為函數指針。

你可以嘗試:

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

std::function<>無法轉換為函數指針。 std::function<>是可以保持狀態的函數對象,而常規函數是無狀態的(有點,你可能有static變量,但這是另一回事)。

另一方面, 無狀態 lambda 可以轉換為函數指針,因此您可以更改函數的簽名以直接獲取函數指針,並且將轉換lambda:

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

請注意,將rvalue綁定到非const引用是非法的,因此您不能合法地將{2,3}作為Integrate的最后一個參數傳遞(即使Visual Studio允許),您將需要創建一個命名變量。

最好將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的分支預測器將能夠很好地執行,因為間接將始終是相同的lambda。

如果你需要將lambda函數與capture集成(在這種情況下沒有轉換為原始指針),並且如果你不希望與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);  

這些包裝器的好處是它們也可以用於集成類成員函數。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM