특정 객체 인스턴스에서 C++ 함수 포인터 호출
문제
다음과 같이 정의된 함수 포인터가 있습니다.
typedef void (*EventFunction)(int nEvent);
C++ 개체의 특정 인스턴스로 해당 함수를 처리할 수 있는 방법이 있습니까?
class A
{
private:
EventFunction handler;
public:
void SetEvent(EventFunction func) { handler = func; }
void EventOne() { handler(1); }
};
class B
{
private:
A a;
public:
B() { a.SetEvent(EventFromA); } // What do I do here?
void EventFromA(int nEvent) { // do stuff }
};
편집하다: Orion은 Boost가 제공하는 다음과 같은 옵션을 지적했습니다.
boost::function<int (int)> f;
X x;
f = std::bind1st(
std::mem_fun(&X::foo), &x);
f(5); // Call x.foo(5)
불행히도 Boost는 나에게 옵션이 아닙니다.멤버 함수에 대한 포인터를 일반 함수 포인터로 래핑하는 일종의 "커링" 함수가 C++로 작성될 수 있습니까?
해결책
Don Clugston의 우수한 FastDelegate 라이브러리를 적극 권장합니다. 그것은 당신이 실제 대의원에 대해 기대할 모든 것을 제공하고 대부분의 경우 몇 가지 ASM 지침으로 컴파일합니다. 함께 제공되는 기사는 회원 기능 포인터에 대한 좋은 읽기입니다.
다른 팁
기능 포인터를 사용하여 주어진 객체 인스턴스의 vtable에 색인 할 수 있습니다. 이것을 a라고합니다 멤버 기능 포인터. ".*"및 "& ::"연산자를 사용하려면 구문을 변경해야합니다.
class A;
class B;
typedef void (B::*EventFunction)(int nEvent)
그리고:
class A
{
private:
EventFunction handler;
public:
void SetEvent(EventFunction func) { handler = func; }
void EventOne(B* delegate) { ((*delegate).*handler)(1); } // note: ".*"
};
class B
{
private:
A a;
public:
B() { a.SetEvent(&B::EventFromA); } // note: "&::"
void EventFromA(int nEvent) { /* do stuff */ }
};
RAW C ++ 기능 포인터에서 멀어지고 사용합니다. std::function
대신에.
당신이 사용할 수있는 boost::function
C ++ 11을 지원하지 않는 Visual Studio 2008과 같은 오래된 컴파일러를 사용하는 경우.
boost:function
그리고 std::function
같은 것입니다 - 그들은 C ++ 11을 위해 STD 라이브러리로 약간의 부스트 물건을 뽑았습니다.
참고 : 이해하기 쉽기 때문에 Microsoft 대신 Boost 기능 문서를 읽을 수 있습니다.
당신은 찾을 수 있습니다 Marshall Cline의 C ++ FAQ 성취하려는 것에 도움이됩니다.
에 대해 읽다 회원들에게 포인터. 파생 클래스에서 메소드를 호출하려면이 메소드는 기본 클래스에서 가상으로 선언되고 기본 클래스에서 재정의되어야하며 포인터는 기본 클래스 메소드를 가리켜 야합니다. 더 많은 것에 대해 가상 멤버에게 포인터.
C 라이브러리와 인터페이스하는 경우와 같은 것을 사용하지 않고 클래스 멤버 기능을 사용할 수 없습니다. boost::bind
. 콜백 함수를 취하는 대부분의 C 라이브러리는 일반적으로 선택한 추가 인수 (보통 유형의 추가 인수를 전달할 수 있습니다. void*
), 수업을 부트 스트랩하는 데 사용할 수 있습니다.
class C
{
public:
int Method1(void) { return 3; }
int Method2(void) { return x; }
int x;
};
// This structure will hold a thunk to
struct CCallback
{
C *obj; // Instance to callback on
int (C::*callback)(void); // Class callback method, taking no arguments and returning int
};
int CBootstrapper(CCallback *pThunk)
{
// Call the thunk
return ((pThunk->obj) ->* (pThunk->callback))( /* args go here */ );
}
void DoIt(C *obj, int (C::*callback)(void))
{
// foobar() is some C library function that takes a function which takes no arguments and returns int, and it also takes a void*, and we can't change it
struct CCallback thunk = {obj, callback};
foobar(&CBootstrapper, &thunk);
}
int main(void)
{
C c;
DoIt(&c, &C::Method1); // Essentially calls foobar() with a callback of C::Method1 on c
DoIt(&c, &C::Method2); // Ditto for C::Method2
}
불행히도, 이벤트 기능 유형은 올바른 유형이 아니기 때문에 B의 함수를 가리킬 수 없습니다. 당신은 그것을 올바른 유형으로 만들 수 있지만 아마도 당신이 원하는 솔루션은 아닙니다.
typedef void (*B::EventFunction)(int nEvent);
... 그리고 나서 모든 것이 B의 홀로 콜백을 호출하면 모든 것이 작동합니다. 그러나 다른 일을하는 다른 클래스에서 B 이외의 기능을 호출 할 수 있기를 원할 것입니다. 그것은 일종의 콜백의 요점입니다. 그러나 이제이 유형은 B에서 확실히 무언가를 가리 킵니다. 더 매력적인 솔루션은 다음과 같습니다.
- B를 기본 클래스로 만든 다음 호출 될 수있는 서로 클래스의 가상 함수를 대체하십시오. 그런 다음 A는 함수 포인터 대신 B에 포인터를 저장합니다. 훨씬 깨끗합니다.
- 기능을 특정 클래스 유형, 심지어 기본 클래스 (그리고 나는 당신을 비난하지 않을 것입니다)에 바인딩하고 싶지 않다면 정적 함수라고 불리는 함수를 만드는 것이 좋습니다. "
static void EventFrom A(int nEvent);
". 그러면 B의 대상없이 직접 호출 할 수 있지만 B의 특정 인스턴스를 호출하기를 원할 것입니다 (B가 싱글 톤이 아니라면). - 따라서 B의 특정 인스턴스를 호출 할 수 있지만 비 B를 호출 할 수 있으려면 콜백 함수가 올바른 개체를 호출 할 수 있도록 다른 것을 콜백 함수로 전달해야합니다. 위와 같이 함수를 정적으로 만들고 B를 포인터로 만들 수있는 void* 매개 변수를 추가하십시오.
실제로이 문제에 대한 두 가지 솔루션이 있습니다 : 공허*와 이벤트를 통과하는 임시 시스템 및 Windowing Systems와 같은 기본 클래스에서 가상 기능을 갖춘 계층 구조
부스트는 옵션이 아니라고 말씀하셨는데 TR1을 사용할 수 있나요?
TR1은 부스트 라이브러리를 기반으로 하는 함수, 바인드 및 mem_fn 개체를 제공하며 이미 컴파일러와 함께 번들로 제공되어 있을 수 있습니다.아직 표준은 아니지만 최근에 사용한 컴파일러 중 적어도 두 개에는 표준이 있었습니다.
http://en.wikipedia.org/wiki/Technical_Report_1
http://msdn.microsoft.com/en-us/library/bb982702.aspx
여기서 성취하려고하는 것이 다소 불분명합니다. 분명한 것은 기능 포인터가 방법이 아니라는 것입니다.
아마도 당신이 찾고있는 것은 방법에 대한 포인터 일 것입니다.
C ++ 프레임 워크에서 사용하는이 정확한 것을위한 클래스 세트가 있습니다.
http://code.google.com/p/kgui/source/browse/trunk/kgui.h
처리 방법 콜백으로 사용할 수있는 각 클래스 함수는 객체 유형을 바인딩하는 정적 함수가 필요합니다. 자동으로 수행하는 매크로 세트가 있습니다. "CB_"접두사와 클래스 객체 포인터 인 추가 첫 번째 매개 변수를 제외하고 동일한 이름의 정적 기능을 만듭니다.
다른 매개 변수 조합을 처리하기 위해 클래스 유형 KGUICALLBACK 및 다양한 템플릿 버전을 확인하십시오.
#define CALLBACKGLUE(classname , func) static void CB_ ## func(void *obj) {static_cast< classname *>(obj)->func();}
#define CALLBACKGLUEPTR(classname , func, type) static void CB_ ## func(void *obj,type *name) {static_cast< classname *>(obj)->func(name);}
#define CALLBACKGLUEPTRPTR(classname , func, type,type2) static void CB_ ## func(void *obj,type *name,type2 *name2) {static_cast< classname *>(obj)->func(name,name2);}
#define CALLBACKGLUEPTRPTRPTR(classname , func, type,type2,type3) static void CB_ ## func(void *obj,type *name,type2 *name2,type3 *name3) {static_cast< classname *>(obj)->func(name,name2,name3);}
#define CALLBACKGLUEVAL(classname , func, type) static void CB_ ## func(void *obj,type val) {static_cast< classname *>(obj)->func(val);}