2011-01-31 6 views
5

아래 클래스 멤버 함수를 전역 함수에 바인딩하는 개념이 있습니다. 이것의 주된 목적은 C 스타일의 콜백 함수 구현을 위해 C++을 사용하는 것입니다. 더 좋은 방법으로이를 수행 할 수 있습니까 (예 : 최종 매크로 또는 typeof 또는 C++ 0x 기능 사용)?클래스 멤버 함수를 c- 함수로 바인딩

#include <iostream> 

using namespace std; 

template<typename MF> struct mf_wrapper_helper; 

template<typename R, typename T> 
struct mf_wrapper_helper<R (T::*)()> 
{ 
    template <R (T::*F)()> 
    static R wrapper(T *foo) { return (foo->*F)(); } 
}; 

template<typename R, typename T, typename T1> 
struct mf_wrapper_helper<R (T::*)(T1)> 
{ 
    template <R (T::*F)(T1)> 
    static R wrapper(T *foo, T1 arg1) { return (foo->*F)(arg1); } 
}; 

#define BIND_MF(mf) \ 
    mf_wrapper_helper<typeof(mf)>::wrapper<mf> 


struct Foo 
{ 
    void x() { cout << "Foo::x()" << endl; } 
    void x1(int i) { cout << "Foo::x1(" << i << ")" << endl; } 
}; 

int 
main() 
{ 
    typedef void (*f_t)(Foo *); 
    typedef void (*f1_t)(Foo *, int i); 

    Foo foo; 

    f_t f_p = BIND_MF(&Foo::x); 
    (*f_p)(&foo); 

    f1_t f1_p = BIND_MF(&Foo::x1); 
    (*f1_p)(&foo, 314); 

    return 0; 
} 
+0

당신이 사용하는 어떤 컴파일러? C++ 0x를 사용할 수 있다면 캡쳐리스 (capture-less) 인 람다 (lambda)를 사용하면 함수 포인터로 변환 할 수 있습니다. 또한 왜 동적으로'main'에 어떤 것을 할당할까요? – GManNickG

+0

'new'는 무시합니다. - 질문에는 중요하지 않습니다. 나는 GCC 4.5와 ICC 11.1을 사용한다. 'Lambda 함수는 구현 의존형의 함수 객체이기 때문에'lambda가 여기서 어떻게 도움이되는지 잘 모르겠습니다. 사실, 나는 C++ 0x를 잘 모른다. 코드 예제가 인정된다. – klimkin

+0

이 질문이 ** 아마도 ** http://codereview.stackexchange.com/ –

답변

1

완벽하게 작동하는 유일한 기술은 C 콜백 내에서 호출하려는 각 멤버 함수에 대해 C 래퍼 함수를 ​​작성하는 것입니다. 즉 :

extern "C" void Foo_x(Foo *foo) 
{ 
    foo->x(); 
} 

extern "C" void Foo_x1(Foo *foo, int i) 
{ 
    foo->x1(i); 
} 

또한 암시 적으로 동일한 매개 변수를 가진 기능과 폐쇄 형의 함수 호출 연산자로 유형을 반환하는 포인터로 변환 C++ 0X의 람다 표현식을 사용할 수 있습니다. 그러나 함수 유형의 언어 연결은 "C"가 아니라 "C++"입니다.

#include <cstdlib> 
#include <iostream> 

using namespace std; 

struct Foo 
{ 
    void x() { cout << "Foo::x()" << endl; } 
    void x1(int i) { cout << "Foo::x1(" << i << ")" << endl; } 
}; 

int main() 
{ 
    typedef void (*f_t)(Foo*); // default ("C++") language linkage 
    typedef void (*f1_t)(Foo*, int); 

    Foo foo; 

    Foo_x(&foo); 
    Foo_x1(&foo, -10); 

    f_t fn = [] (Foo *foo) { 
     foo->x(); 
    }; 
    fn(&foo); 

    f1_t fn1 = [] (Foo *foo, int i) { 
     foo->x1(i); 
    }; 
    fn1(&foo, 314); 

    return EXIT_SUCCESS; 
} 

노트는 C++ 표준의 섹션 5.2.2, 함수 호출, 진술한다 :

가 그 기능 유형의 언어 결합 상이한 언어 결합을 갖는 식으로 함수를 호출 호출 된 함수 정의의 함수 유형은 정의되지 않습니다.

extern "C" typedef void (*f_t)(Foo*); 

int main() 
{ 
    Foo foo; 

    f_t fn = [] (Foo *foo) { 
     foo->x(); 
    }; 
    fn(&foo); // `fn` is a pointer to a function that uses "C++" language linkage, 
      // but it is erroneously called through "C" language linkage. 

//... 

편집 : 실험 조금 후에, 나는 주어진 멤버 함수를 호출 람다를 돌려 다음 템플릿 기능을 함께했다 :

그래서 다음과 같은 기술적으로 정의되지 않은 동작을 호출

: Foo 만약
template <typename return_t, class base, typename... arg_types> 
std::function<return_t (base*, arg_types...)> make_lambda_to_call_member_function(return_t (base::*mem_fn)(arg_types...)) 
{ 
    return [mem_fn] (base *o, arg_types... args) -> return_t { 
     (o->*mem_fn)(args...); 
    }; 
} 

template <typename return_t, class base, typename... arg_types> 
std::function<return_t (const base*, arg_types...)> make_lambda_to_call_member_function(return_t (base::*cmem_fn)(arg_types...) const) 
{ 
    return [cmem_fn] (const base *o, arg_types... args) -> return_t { 
     (o->*cmem_fn)(args...); 
    }; 
} 

는 다음과 같이 정의된다 람다 표현식이 람다 캡처를 사용하기 때문에 반환 된 람다 개체를 암시 적으로 함수 포인터로 변환되지 않습니다, 그러나,

auto fn = make_lambda_to_call_member_function(&Foo::x); 
fn(&foo); 

auto fn1 = make_lambda_to_call_member_function(&Foo::x1); 
fn1(&foo, 314); 

auto fn2 = make_lambda_to_call_member_function(&Foo::cx1); 
fn2(&foo, 44.832f); 

참고 :

struct Foo 
{ 
    void x() { cout << "Foo::x()" << endl; } 
    void x1(int i) { cout << "Foo::x1(" << i << ")" << endl; } 
    void cx1(float f) const { cout << "Foo::cx1(" << f << ")" << endl; } 
}; 

617,451,515,는 그런 다음 같은 템플릿 make_lambda_to_call_member_function를 사용합니다.

C++ 0X의 최신 초안, n3225, 상태 :

없는 람다 캡처람다 표현 닫는 방법의 종류는 공개되지 않은 가상이 아닌 명시 적 CONST있다 변환 함수는 동일한 매개 변수를 갖는 함수를 가리키는 포인터 및 클로저 유형의 함수 호출 연산자와 같은 리턴 유형을 가리 킵니다. 이 변환 함수가 반환하는 값은 호출 될 때 클로저 형식의 함수 호출 연산자를 호출하는 것과 동일한 효과를 갖는 함수의 주소 여야합니다.

다음은 불법입니다 :

void (*fn5)(Foo*) = make_lambda_to_call_member_function(&Foo::x); 
+0

연결 유형에 대한 좋은 지적입니다. 나는 람다 사용에 문제가 있음을 알 수있다. 모든 인자 타입은 완전히 나열되어야한다. 템플릿 버전에서는 모든 유형이 컴파일러로 계산됩니다. 멤버 함수를 매개 변수로 사용하는 람다 함수의 템플릿을 만들 수 있습니까? – klimkin

+0

@klimkin : 답변을 더 많은 정보로 업데이트했습니다. 귀하의 질문에 대답을 네, 주어진 멤버 함수를 호출하는 람다 개체를 반환하는 템플릿 함수를 작성할 수 있지만 해당 람다 개체를 더 이상 함수 포인터로 변환 할 수 없습니다. –

관련 문제