2009-02-05 3 views
3

이 코드 조각은 demote-boostfunction-to-a-plain-function-pointer에서 논의 된 것처럼 합법이 아니라는 점을 제외하면 메서드를 함수 콜백에 바인딩해야합니다.바운드 메서드에서 원시 함수 포인터

이 동작을 얻는 가장 간단한 방법은 무엇입니까?

struct C { 
void m(int x) { 
    (void) x; 
    _asm int 3; 
}}; 

typedef void (*cb_t)(int); 
int main() { 
    C c; 
    boost::function<void (int x)> cb = boost::bind(&C::m, &c, _1); 
    cb_t raw_cb = *cb.target<cb_t>(); //null dereference 
    raw_cb(1); 
    return 0; 
} 

답변

2

자신 만의 클래스를 만들어서 부스트 바인딩 함수와 동일한 작업을 수행 할 수 있습니다. 모든 클래스는 함수 유형과 함수를 포함하는 객체에 대한 포인터를 허용해야합니다.

template<typename owner> 
class VoidDelegate : public IDelegate 
{ 
public: 
    VoidDelegate(void (owner::*aFunc)(void), owner* aOwner) 
    { 
     mFunction = aFunc; 
     mOwner = aOwner; 
    } 
    ~VoidDelegate(void) 
    {} 
    void Invoke(void) 
    { 
     if(mFunction != 0) 
     { 
     (mOwner->*mFunction)(); 
     } 
    } 

private: 
    void (owner::*mFunction)(void); 
    owner* mOwner; 
}; 

사용법 : 예를 들어, 다음은 무효 반환 및 무효 PARAM 위임이다 VoidDelegate<C>는 타입이기 때문에

class C 
{ 
    void CallMe(void) 
    { 
     std::cout << "called"; 
    } 
}; 
int main(int aArgc, char** aArgv) 
{ 
    C c; 
    VoidDelegate<C> delegate(&C::CallMe, &c); 
    delegate.Invoke(); 
} 

이제, 이들의 컬렉션을 가진 것은 실용적이지 않을 수는, 만약에 있기 때문에 목록에는 클래스 B의 함수도 포함되어 있었습니까? 그것은 할 수 없었다.

여기에는 다형성이 작용합니다. 당신은 함수 호출이있는 인터페이스 IDelegate를 만들 수 있습니다 :

class IDelegate 
{ 
    virtual ~IDelegate(void) { } 
    virtual void Invoke(void) = 0; 
} 

VoidDelegate<T> 만약 구현하면 IDelegates의 컬렉션이 때문에 다른 클래스 유형에 방법에 콜백을 가질 수 IDelegate.

+0

는 유망 해 보입니다. 사용을 게시 할 수 있습니까? –

+0

아쉽게도 멤버 함수 호출을 다른 것으로 대체하는 것입니다. – Eclipse

+0

수정하십시오. 이를 외부 다형성 (external polymorphism)이라고합니다.기본적으로 멤버 함수를 객체에 전달하고이 객체를 전달한 다음 나중에 설정 한 클래스에서 호출 할 수 있습니다. 이벤트에 아주 좋습니다! 이것은 C#의 델리게이트와 같습니다. – Chap

2

어느 당신은 글로벌 변수에 바운드 매개 변수를 밀어하고 값을 선택할 수 있습니다 정적 함수를 만들고 그 위에 함수를 호출하거나에 당 인스턴스 기능을 생성해야 할거야 수 있습니다 플라이 - 이것은 당신이 원하는 값으로 설정된 정적 로컬 변수를 가진 힙에 스텁 함수를 생성하기위한 일종의 플라이 코드 gen을 포함 할 것입니다. 그리고 나서 그 함수를 호출합니다.

첫 번째 방법은 간단하고 이해하기 쉽지만 스레드로부터 안전하지 않거나 재진입이 불가능합니다. 두 번째 버전은 지저분하고 어렵지만 thread-safe 및 reentrant가 올바르게 수행됩니다.

편집 : 방금 ATL이 코드 생성 기술을 사용하여 정확히 이것을 수행한다는 사실을 알게되었습니다. 즉석에서 멍청한 신호를 생성하여 this 포인터 및 기타 데이터를 설정 한 다음 콜백 함수로 바로 이동합니다. Here's a CodeProject article이 어떻게 작동하는지 설명하고 직접 할 수있는 방법을 알려줍니다. 특히 마지막 샘플 (프로그램 77)을보십시오.

DEP가 작성되었으므로 VirtualAllocPAGE_EXECUTE_READWRITE과 함께 사용해야 만 썽크를 할당하고 실행할 수있는 메모리 덩어리를 얻을 수 있습니다.

+0

on-the-fly 코드 gen은 분명히 너무 복잡합니다. 가능한 한 간단하게 찾고 있습니다. –

+0

첫 번째 방법은 내가 한 일입니다. 내 대답을보십시오. 그게 당신이 말하는거야? –

+0

나는 그것이라고 상상했다. 그러나 나는 어떻게해서든지 그것을 거기에 던질 것이다라고 생각했다. – Eclipse

0

나는 C를 싱글 톤으로 변환하여 C :: m을 C :: m_Impl로 만들고, C :: m (int)을 선언하여 싱글 톤 인스턴스로 전달합니다. 해킹에 대해 이야기하십시오.

+0

한 번에 하나의 콜백 만 사용한다면이 방법이 유용 할 것입니다. – Eclipse

1
#include <iostream> 
typedef void(*callback_t)(int); 

template< typename Class, void (Class::*Method_Pointer)(void) > 
void wrapper(int class_pointer) 
{ 
    Class * const self = (Class*)(void*)class_pointer; 
    (self->*Method_Pointer)(); 
} 

class A 
{ 
public: 
    int m_i; 
    void callback() 
    { std::cout << "callback: " << m_i << std::endl; } 
}; 

int main() 
{ 
    A a = { 10 }; 
    callback_t cb = &wrapper<A,&A::callback>; 
    cb((int)(void*)&a); 
}