2009-10-19 9 views
3

현재 Don Clugston의 fastdelegates를 사용하는 타이머/콜백 시스템을 구현 중입니다.함수 포인터 간 변환

여기 시작 코드 (http://www.codeproject.com/KB/cpp/FastDelegate.aspx 참조)

struct TimerContext 
{ 
}; 

void free_func(TimerContext*) 
{ 
} 

struct Foo 
{ 
    void member_func(TimerContext*) 
    { 
    } 
}; 

Foo f; 
MulticastDelegate< void (TimerContext*) > delegate; 

delegate += free_func; 
delegate += bind(&Foo::member_func, &f); 

좋아,하지만 지금, 나는 사용자가 저장하고 콜백에 자신의 구조를 보내 TimerContext를 서브 클래 싱 할 수 있도록 바랍니다. 여기 목적은 당신이 짐작 이름으로 TimerContext 자신

struct TimerContext 
{ 
}; 

struct MyTimerContext : TimerContext 
{ 
    int user_value; 
}; 

void free_func(TimerContext*) 
{ 
} 

void free_func2(MyTimerContext*) 
{ 
} 

struct Foo 
{ 
    void member_func(TimerContext*) 
    { 
    } 

    void member_func2(MyTimerContext*) 
    { 
    } 
}; 

Foo f; 
MulticastDelegate< void (TimerContext*) > delegate; 

delegate += free_func; 
delegate += free_func2; 
delegate += bind(&Foo::member_func, &f); 
delegate += bind(&Foo::member_func2, &f); 

를 다운 캐스트 할 필요에서 사용자를 방지하는 것입니다, GCC는 나를

error: invalid conversion from `void (*)(MyTimerContext*)' to `void (*)(TimerContext*)' 
error: initializing argument 1 of `delegate::Delegate<R()(Param1)>::Delegate(R (*)(Param1)) [with R = void, Param1 = TimerContext*]' 

그래서 지금은 내 질문은 : 그렇게 못하게 : reinterpret_cast을 사용하여 강제로 캐스트하면 제대로 작동하지만 안전할까요?

PS :이 시간에 중요한 콜백은 무거운 가상 중심의 솔루션은 불가능한 것으로 간주됩니다입니다 :/

답변

5

C++ 표준은 말합니다 13.4/7이 :

표준 변환 (4 절은 없다)를 다른 포인터로 함수 유형으로 변환합니다. 특히, 경우에도 BD의 공개베이스, 우리가 가지고있다

D* f(); 
B* (*p1)() = &f; // error 
void g(D*); 
void (*p2)(B*) = &g; // error 

그래도 당신은 하나 개의 인수, boost::function 같은과 기능에 대한 포인터를 저장하는 기능 어댑터를 사용할 수있을 수 있습니다,하지만 난 아니에요 지금 당장 당신의 문제를 해결할 수 있는지 여부.

+0

Howw는 괜찮 :(는 참조 주셔서 감사합니다! TimerContext * 암시 MyTimerContext으로 변환하지 않기 때문에 – NewbiZ

+0

심지어 부스트 : : 함수가 자신의 경우에 작동하지 않습니다 *. 나는이 유형의 안전 문제를 언급 내 Answer 그러나 어떤 이유로 든 downvoted되었습니다. – sellibitze

0

물론 캐스팅 함수 포인터는 일반적으로 나쁜 생각입니다.

void(*)(Derived*)에서 void(*)(Base*)까지 함수 포인터를 캐스팅하거나 작동하지 않을 수 있습니다. Derived * 및 Base * 포인터의 내부 표현이 변환시 조정될 필요가있는 경우 확실히 작동하지 않습니다. 그러나 단일 상속 관계인 경우에는 그렇지 않습니다. 그래도 클래스 레이아웃과 포인터 조정은 구현에 따라 정의되므로이를 사용해서는 안됩니다. 위험을 무릅 쓰고 싶다면 다음과 같이하십시오.

포인터를 조정할 필요가 없다고 가정하면 void(*)(Derived1*)에서 void(*)(Base*)으로의 변환은 Derived1 *이 Derived2 *와 함께 호출 될 것으로 예상하는 함수를 허용하기 때문에 여전히 좋은 방법이 아닙니다 Derived1과 Derived2는 상속 계층에서 형제입니다.

+1

구현 정의되지 않았습니다. 정의되지 않은 동작 (다소 다른 의미가 있습니다) –

+0

아니요, 클래스 레이아웃과 가능한 포인터 조정은 구현에 따라 정의됩니다. – sellibitze

0

reinterpret_cast은 개체의 '보낸 사람'과 '받는 사람'이 일치하는 경우에만 안전합니다. 따라서 발신자와 수신자가 같은 코드로 구현되는 경우 꽤 당분간 안전 할 수 있습니다. 당신이 대리인, 당신이 그들을 다른 유형을 갖고 싶어에 콜백을 추가하려는 경우처럼

, 당신은이 개 시나리오의 : 당신은 모든 참가자가 선행 알고

  • ,에 컴파일 시간 => 유형리스트에 포장 할 수 있습니다.

  • 대리인은 런타임에 구성 할 수 있습니다.> 런타임 바인딩, 즉 가상 함수 (exec 메소드 등)를 사용해야합니다.