2010-07-15 5 views
4

매우 C 함수 (C 함수를 다시 작성하는 것은 이 아니고이 아닌 옵션)에서 복잡한 C 함수를 사용하는 데 문제가 있습니다. C 함수 :std :: tr1 :: function 및 std :: tr1 :: bind

typedef void (*integrand) (unsigned ndim, const double* x, void* fdata, 
          unsigned fdim, double* fval); 
// This one: 
int adapt_integrate(unsigned fdim, integrand f, void* fdata, 
        unsigned dim, const double* xmin, const double* xmax, 
        unsigned maxEval, double reqAbsError, double reqRelError, 
          double* val, double* err); 

I는 자신의 입력 integrand 공극 기능을 제공해야하고, adapt_integrate는 N 차원의 적분을 계산한다. 코드 calcTripleIntegral (아래)은 func이 독립 실행 형 함수 인 경우 독립 실행 형 함수로 작동합니다. 이 쉽게 등 과부하 될 수 나는 GCC 4.4.5 (시험판)에

class myIntegrator 
{ 
public: 
    double calcTripleIntegral(double x, double Q2, std::tr1::function<integrand> &func) const 
    { 
     //...declare val, err, xMin, xMax and input(x,Q2) ...// 
     adapt_integrate(1, func, input, 
         3, xMin, xMax, 
         0, 0, 1e-4, 
         &val, &err); 
     return val; 
    } 
    double integrandF2(unsigned ndim, const double *x, void *, // no matter what's inside 
       unsigned fdim, double *fval) const;   // this qualifies as an integrand if it were not a class member 
    double getValue(double x, double Q2) const 
    { 
     std::tr1::function<integrand> func(std::tr1::bind(&myIntegrator::integrandF2, *this); 
     return calcTripleIntegral(x,Q2,func); 
    } 
} 

, 이것은 저를 제공합니다 ..., 적분과 같은 (! 비 정적) 클래스 멤버 함수를 전달하려는 :

error: variable 'std::tr1::function func' has initializer but incomplete type

편집 : 내 코드의 오류는 무엇인가요? 나는 GCC 4.4, 4.5 및 4.6으로 컴파일을 시도했다. 모두 동일한 오류가 발생했다. 하나 더 작업이 이루어되지 않은, 또는 나는 매우 많은

감사에게 를 뭔가 잘못 /편집했다! 내가 충분히 명확하지 않으면, 기꺼이 자세히 설명해 드리겠습니다.

추 신 : myIntegrator.cpp 어딘가에 정의 된 함수 포인터를 사용하여 tr1 요소없이이 문제를 해결할 수 있습니까?

최종 업데이트 : 오케이, 나는 TR1이이를 위해 한 두 줄짜리 해결책을 제공했다고 착각했습니다. 파머. 클래스를 네임 스페이스로 "변환"하고 함수 선언을 복사합니다. 하나의 기본 클래스와 인터페이스를 다시 구현 한 하나의 하위 클래스 만 있으면됩니다. C 함수 포인터 + C++ 클래스 = 나에게 나쁜 소식. ! 어쨌든 모든 해답을, 당신이 나에게 C++의 일부 어두운 구석을 표시했습니다 감사합니다, 나는 적분으로 (비 정적) 클래스 멤버 함수를 전달하려는

+0

C++ 0x 기능이 허용됩니까? –

+0

'fdata'와'input' 매개 변수는 무엇입니까? 왜 피한화 유형의'void *'매개 변수에 이름이 없습니까? –

+0

Caspin, 형식 선언에서 함수의 매개 변수 이름을 지정할 필요가 없습니다. 함수에서 함수를 사용할 필요가 없다면 함수의 실제 정의에서 이름을 지정할 필요조차 없습니다. 함수는 다른 코드와 함께 사용하기 위해 특정 서명과 일치해야하는 경우 일반적으로이 코드를 작성하지만 일부 매개 변수는 구현시 사용되지 않습니다. –

답변

3

멤버 함수를 C 스타일 콜백에 전달하려는 경우 std::t1::bind 또는 std::tr1::function을 사용하여 out으로 처리 할 수 ​​있습니다.

class myIntegrator 
{ 
public: 
    // getValue is no longer const. but integrandF2 wasn't changed 
    double getValue(double x, double Q2) 
    { 
     m_x = x; 
     m_Q2 = Q2; 

     // these could be members if they need to change 
     const double xMin[3] = {0.0}; 
     const double xMax[3] = {1.0,1.0,1.0}; 
     const unsigned maxEval = 0; 
     double reqAbsError = 0.0; 
     double reqRelError = 1e-4; 

     double val; 

     adapt_integrate(1, &myIntegrator::fancy_integrand, 
         reinterpret_cast<void*>(this), 
         3, xMin, xMax, 
         maxEval, reqAbsError, reqRelError, 
         &val, &m_err); 

     return val; 
    } 

    double get_error() 
    { return m_error; } 

private: 
    // use m_x and m_Q2 internally 
    // I removed the unused void* parameter 
    double integrandF2(unsigned ndim, const double *x, 
         unsigned fdim, double *fval) const; 

    static double fancy_integrand(unsigned ndim, const double* x, void* this_ptr, 
            unsigned fdim, double* fval) 
    { 
     myIntegrator& self = reinterpret_cast<myIntegrator*>(this_ptr); 
     self.integrateF2(ndim,x,fdim,fval); 
    } 

    double m_x 
    double m_Q2; 
    double m_err; 
}; 
+0

은 정적 함수에서 비 정적 멤버에 액세스 할 때 컴파일러를 속이는 것이 아닙니다. – rubenvb

+0

@rubenvb : 아니요, 이것은 완벽하고 안전하며 이식성이 뛰어나고 C++ 스타일입니다. 정적 함수 만 사용하여 myIntegrator 내부에 액세스 할 수 있습니다. 'fancy_integrand'가 스탠드 일 수 있기를 바란다면 "myIntegrator"의 모든 멤버를 공개 할 수 있습니다. 정적 메서드 접근 방식이 더 캡슐화되어 있기 때문에 마음에 듭니다. –

+0

제목이 바인드 및/또는 함수없이 응답에 적합하지 않을 수도 있지만 손에 문제가 있습니다. 고맙습니다. :) – rubenvb

2

) ...

당신은 할 수 없습니다. 그래서 멤버 함수를 콜백으로 사용하기 위해 SO를 검색하면 어쨌든 직접 접근 방식을 시도하는 등의 유용한 정보를 찾을 수밖에 없습니다.

편집 : BTW, 코드에서 문제 중 하나 (수행하려는 작업이 단순히 불가능하므로 더 이상 할 수 없음)는 함수 포인터 유형을 함수 <>에 전달한 것입니다. 기대는 서명입니다. 함수 템플릿과 같이 뭔가를 구현됩니다

template < typename Signature > 
struct function; 

// for each possible number of arguments: 
template < typename R, typename Arg1, typename Arg2 > 
struct function<R(Arg1,Arg2)> 
{ 
    ... body ... 
}; 

단순히 컴파일러에 의해 이해하지 않을 이런 종류의 일에 함수 포인터를 전달 볼 수 있듯이. 그것은 앞으로 선언을 인스턴스화하고 아무데도하려고합니다. 이것은 물론 당신이 의미하는 컴파일러 오류이지만, 근본적인 문제를 해결하지 못합니다. 즉, 당신이하고있는 일은 결코 작동하지 않을 것입니다.

완전히 C++ 0x 컴파일러에서 이것은 다르게 수행 될 수 있지만 boost :: function과 MSVC는 이와 같아야합니다. 또한 C++ 0x 버전은 현재 직면 한 문제와 동일한 문제가 발생합니다.

+0

그게 바로 내가'function'과'bind'를 사용하여이 문제를 해결하는 이유입니다. http://stackoverflow.com/questions/2374847/passing-member-function-pointer-to-member-object-in -c (두 번째 대답은 내가 영감을 얻은 것임) – rubenvb

+0

@rub : 두 번째 대답은 C 함수를 통해/일반 함수 포인터로 전달할 필요가 없습니다. 문제가 발생한 곳입니다. –

+0

이 답변은 정확합니다. 나는 void * 인자를 보았습니다. 이것은 userdata 스타일의 인자입니까? 많은 C 콜백은 필요한 컨텍스트에 상관없이 무효 *를 제공합니다. C 함수 포인터에 예비 void *가 없으면이 작업을 수행 할 수 없습니다. – Puppy

0

관련된 오류 유형 중 하나에 대해 포함되지 않은 오류 메시지가 표시됩니다. 적어도 integrandtr1에 두 번 확인 하시겠습니까?

+0

을 포함했는데 그 외에 무엇을 포함 할 수 있습니까? 'integrand'는 Cubature.h에 정의되어 있습니다. 독립 실행 형 클래스 레스 폼의이 코드는 꼭 필요한대로 작동하므로, 'integrand'는 문제가 아닙니다. – rubenvb

0

bind 내가 생각하는 것보다 약간 다른 방식으로 작동합니다. 값을 제공하거나 모든 인수에 대해 자리 표시자를 제공해야합니다. 귀하의 예를 들어

이,

std::tr1::function<integrand> func(std::tr1::bind(&myIntegrator::integrandF2, *this, _1, _2, _3, _4, _5)); 
당신이 멤버 함수를 결합하고 있기 때문에

, 당신은 당신이 멤버 함수를 호출 객체 즉, 추가 (암시 적) 인수를 얻었다 (자리에) 아래로 비등 그래서 당신은 6 명이 있습니다.

처음에는 this 개체를 바인딩하고 다른 인수는 단순히 자리 표시자를 전달합니다.

함수 선언이 void를 반환하는 반면 멤버 함수는 double을 반환합니다.

는 (기록을 위해, 난 아직도 작은 TR1 지원 이전 컴파일러를 사용하고, 그래서 난 단지 bind와 부스트를 사용 function 경험을 가지고, 어쩌면 일이 ... (TR1)에 대한 약간의 변경)

3

당신 세 가지 문제를 가지고 ... 우선은 std::tr1::function<R (Args..)>를 원하지만 당신은 std::tr1::function<R (*)(Args...)> 아래로 비등 - 그래서 당신은이 구조체에는 필요

typedef void (integrand) (unsigned ndim, const double *x, void *, 
         unsigned fdim, double *fval); 
typedef integrand* integrand_ptr; 

을 ... 그래서 첫 번째는 당신에게 컴파일 가능한 function<integrand> 수 있습니다. 그것은해야한다,

int adapt_integrate(unsigned fdim, integrand_ptr f, ...); 

다음 귀하의 bind 구문이 꺼져 :

std::tr1::bind(&myIntegrator::integrandF2, *this, _1, _2, _3, _4, _5); 

나머지 문제는 것, 그래서 tr1::function<T>는 함수 포인터로 변환되지 않는 것입니다 adapt_integrate 그에 따라 고정되어야한다 문맥을 전달하기 위해 void* fdata 인수를 사용하여 래퍼 함수를 ​​통과해야합니다. 예 : 뭔가 같이 : 아니 그것은 추한 것인지

extern "C" void integrand_helper (unsigned ndim, const double *x, void* data, 
            unsigned fdim, double *fval) 
{ 
    typedef std::tr1::function<integrand> Functor; 
    Functor& f = *static_cast<Functor*>(data); 
    f(ndim, x, data, fdim, fval); 
} 

// ... 
adapt_integrate(1, &integrand_helper, &func, ...); 

이것은 void* 매개 변수가 함수에 통과한다고 가정 물론이다. 한편

, void* fdata은, 모든 tr1::function 물건이 불필요하고 그냥 트램 폴린 기능을 통해 직접 갈 수있는 상황을 전달할 수 있습니다 경우 - 단지 상황에 맞는 인수를 통해 this을 통과 :

extern "C" void integrand_helper (unsigned ndim, const double *x, void* data, 
            unsigned fdim, double *fval) 
{ 
    static_cast<myIntegrator*>(data)->integrandF2(ndim, ...); 
} 

// ... 
adapt_integrate(1, &integrand_helper, this, ...); 
+0

나는 이것이 "가장 중요한 답변"이라고 생각합니다. – rubenvb

2

이후 std::tr1::bind 및 C 스타일의 함수 포인터가 일치하지 않으면 대신이 코드를 시도하십시오. myIntegrator::getValue은 더 이상 스레드로부터 안전하지 않습니다. calcTripleIntegral이 인터페이스에서 제거 된 경우 이는 더욱 단순 해지고 std::tr1::bind 또는 std::tr1::function을 사용할 필요가 없습니다.은 C-API가 통과 허용한다는 가정을 만들기

class myIntegrator 
{ 
public: 
    double getValue(double x, double Q2) const 
    { 
     return calcTripleIntegral(x,Q2,std::tr1::bind(&Integrator::integrandF2,this)); 
    } 

    double calcTripleIntegral(double x, double Q2, const std::tr1::function<integrand>& func) const 
    { 
     assert(s_integrator == NULL); 
     s_integrator = this; 
     m_integrand = func; 

     //...declare val, err, xMin, xMax and input(x,Q2) ...// 
     adapt_integrate(1, &myIntegrator::fancy_integrand, input, 
         3, xMin, xMax, 
         0, 0, 1e-4, 
         &val, &err); 

     assert(s_integrator == this); 
     s_integrator = NULL; 

     return val; 
    } 
private: 
    double integrandF2(unsigned ndim, const double *x, void *, 
       unsigned fdim, double *fval) const; 

    static double fancy_integrand(unsigned ndim, const double* x, void* input, 
            unsigned fdim, double* fval) 
    { 
     s_integrator->integrateF2(ndim,x,input,fdim,fval); 
    } 

    std::tr1::function<integrand> m_integrand; 
    static const myIntegrator* s_integrator; 
}; 
+0

내가 생각하고있는 것처럼 보이는데, 그 'void *'매개 변수가 전달되지 않으면 놀랄 것입니다 :) –

+0

calcTripleIntegral (valT, err, input을 정의/선언하는 것)과 같은 편리한 함수를 제거하면 어떻게됩니까? 및 xMin 및 xMax 매개 변수) std :: tr1 :: bind 및 함수 불필요하게? 나는 그것을 제거 할 수는 있지만 그것을 단순화하는 방법을 모른다.나는이 tr1 기능을 사용한 적이 한번도 알지 못했다. ( – rubenvb

+0

내가 이것을 시도하면 불완전한 유형의 m_integrand에 대한 오류가 발생한다. ( – rubenvb

1

형식에 얽매합니다 (C-API 함수 유형을 알고 있지만 그것을 필요로하는지 알고 콜백 함수에 의존하지 않는다는 의미에서) 컨텍스트 매개 변수 (일반적으로이 함수는 콜백 함수의 경우이며,이 경우 fdata 매개 변수가이 줄에있는 것으로 의심됩니다.) 함수 개체를이 컨텍스트 매개 변수의 일부로 전달합니다.

그것은 다음과 같이 보일 것이다 : call_callback는 C-API 함수입니다

#include <iostream> 
#include <tr1/functional> 

typedef void (*callback_function_t)(void *input, int arg); 

struct data_type { 
    int x; 
}; 

struct context_type { 
    std::tr1::function<void(data_type const &, int)> func; 
    data_type data; 
}; 

void callback(data_type const&data, int x) { 
    std::cout << data.x << ", " << x << std::endl; 
} 

void callback_relay(void *context, int x) { 
    context_type const *ctxt = reinterpret_cast<context_type const*>(context); 
    ctxt->func(ctxt->data, x); 
} 

void call_callback(callback_function_t func, void *context, int x) { 
    func(context, x); 
} 

int main() { 
    context_type ctxt = { callback, { 1 } }; 

    call_callback(callback_relay, &ctxt, 2); 
} 

합니다. 이렇게하면 std :: tr1 :: bind 식을 포함하여 함수 호출 구문을 지원하는 원하는 것을 context_type :: func에 할당 할 수 있습니다. 또한 (필자도 도덕적으로 이것을 언급 할 의무가 있음에도 불구하고) C 및 C++ 함수에 대한 호출 규칙이 동일하다는 것을 엄격히 말하면 표준에서 정의하지는 않지만 실제로는 context_type을 클래스 템플릿으로, callback_relay를 함수 템플릿으로 만들 수 있습니다 context_type :: data를보다 융통성있게 만들고 이런 방식으로 원하는 것을 전달하십시오.