2012-11-08 1 views
6

그래서 나는 C++ 11 lambdas 함께 사용할 수있는 통합 함수를 작성하려고합니다.C++ 11 람다와 C 함수 포인터

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

그것을 컴파일하고 잘 작동합니다. 어떻게 해결해야합니까?

+6

'std :: function'의 다 기능성이 필요합니까? 'Integerate'의 첫 번째 매개 변수를 함수 포인터로 바꿀 수 있습니까? 왜냐하면 당신이 아주 못생긴 전역 변수 비지니스에 들어 가지 않는 한,'std :: function'을 함수 포인터로 사용할 수있는 방법이 없기 때문입니다. 람다는 포착하지 않는 한 함수 포인터에 저장할 수 있습니다. 예를 들어 보여준 예에서는 포착 할 수 없습니다. –

+0

첫 번째 의견의 마지막 문장에 답변이 있습니다. –

+0

사실, C 인터페이스는 전달 될'void *'매개 변수를 포함하기 때문에 전역 변수가 필요 없습니다. – aschepler

답변

4

무효 * 함수에 약간의 "상태"를 전달하는 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; 
    ::: 
} 

을하고, 표준 : : 함수 객체에 캡슐화 될 펑터의 일환으로 매개 변수 벡터에 대한 참조를 저장하거나 같은 것을 할 : 그래서, 당신은 다음과 같이 뭔가를 할 수

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 솔루션보다 더 선호합니다.

+0

나는 정말 마지막으로 하나, 아주 좋은 솔루션을 좋아해요! –

+0

':::'는 무엇입니까? 장소 보유자? – pyCthon

+1

@pyCthon : 네, 여기에 "물건이 많습니다"장소 홀더가 있어야합니다. C++은 이미 다른 의미로 "..."을 오버로드했습니다. 줄임표 및 팩 확장 연산자입니다. ;) – sellibitze

3

gsl 라이브러리에 함수 포인터가 필요합니다. 캡쳐되지 않은 람다는 함수 포인터로 변환 될 수 있습니다. 모든 람다는 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; 
    //... 
} 
3

std::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); 
당신이 이름을 만들어야합니다 불법이라고

참고는 const가 아닌 참조에 를 rvalue를 결합하는, 그래서 당신은 법적으로 (비주얼 스튜디오는 당신을 허용하더라도) Integrate 마지막 인수로 {2,3}을 전달할 수 없습니다 변하기 쉬운.

3

그것은 당신의 래퍼 함수 내부의 void * 변환을 캡슐화하는 것이 가장 좋습니다 : 간접으로 잘 수행 할 수 있습니다 여기 (std::function을 통해)를 초과하는 간접의 일정 금액이 있습니다

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

하지만 CPU의 분기 예측 항상 같은 람다가 될 것입니다.

3

캡쳐와 람다 함수를 통합해야하는 경우 (이 경우 원시 포인터로의 변환이 없음), 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); 

을 사용하는 방법을 보여줍니다 테스트 코드는 다음 래퍼를 사용할 수 있습니다 이 버전의 래퍼를 사용할 수 있습니다

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

도 간단하다.