2013-08-21 2 views
0

함수 포인터 또는 멤버 함수 포인터를 저장할 클래스를 작성했습니다 (한 번에 하나씩). 멤버 함수 포인터를 저장할 때 개체 포인터도 저장합니다 (리시버). 내가 사전에 객체의 유형이나 함수 서명도 모르는, 그래서 템플릿 유형 이름을 사용 멤버 함수 포인터에서 다른 유형으로 캐스팅하고 뒤로, 엄격한 앨리어싱 문제가 있습니까?

문제

입니다. 인수에 대해서는 가변성 템플릿을 사용합니다.

내가이 유사한 코드가 : 필요 하나, 전역 함수 또는 멤버 함수 때문에

template <typename... Args> 
class A 
{ 
public: 
    template <typename Object, typename Ret> 
    A (Object *receiver, Ret (Object::*mem)(Args...)); // store the member function pointer 

    template <typename Ret> 
    A (Ret (*function)(Args...)); // store the global function pointer 

    void operator()(Args... args) const; // to call the function pointer 
    // ... more public stuff ... 

private: 
    class UndefinedClass; 
    typedef void (Undefined::*MemFunPtrType)(Args...) 

    union 
    { 
     struct 
     { 
      MemFunPtrType memFunPtr; // the stored member function 
      void *receiverPtr; // the receiver object 
      void (*call)(void *, MemFunPtrType, Args...); 
     } member; 

     void (*funPtr)(Args...); // the stored global function 
    }; 

    enum class Type {Function, Member} type; 
}; 

을, 나는 union 안에 모든 것을 넣어. 생성자에서

은 내가 void (Undefined::*)(Args...)에 멤버 함수 mem 캐스팅하고 저장합니다. std :: function 구현에서이 트릭을 가져 왔습니다. 어떤 캡처와 람다를 사용

, 나는, 원래의 형태로 다시 객체와 기능 모두를 캐스팅하고 내가 그들을 호출 내부를 호출 할

typedef Ret (Object::*OriginalMemberType)(Args...); 
// ... 
member.call = [] (void *receiver, MemFunPtrType memFun, Args... args) 
{ 
    (static_cast<Object*>(receiver)->*reinterpret_cast<OriginalMemberType>(memFun))(args...); 
} 

나는 call 함수 포인터에서이 람다 저장을 operator(). if-else 문장을 사용하여 형식 데이터를 비교하고 올바른 포인터를 호출합니다.

나는이 조금 짜증나는 알고 있지만, 그것을 작동합니다. 나는 많은 검사를 받았고 모두 검사를 통과합니다. 그러나 나는 엄격한 앨리어싱 것을 걱정하고 있습니다. 포인터 캐스팅을 많이하고 이것이 정의되지 않은 동작에 해당하는지 확실하지 않습니다.

질문 :Ret (Object::*)(Args...)에서 void (UndefinedClass::*)(Args...)으로 전송할 수 있습니까? (Ret개체Arg은 템플릿 인수입니다. 참고 원래 형식으로 다시 캐스트하지 않고 개체를 호출하지 않습니다. 저장 전용입니다.

질문 :-fno-strict-aliasing으로 컴파일해야합니까? 필자가해야한다면,이 클래스는 템플릿 클래스입니다.이 클래스를 사용하는 모든 프로젝트에 -fno-strict-aliasing을 넣어야합니까? 어쩌면 sizeof(void (UndefinedClass::*)(Args...)) 또는 그 대신에 비슷한 길이의 char 배열을 사용할 수 있습니다.

참고 :

  1. 나는 아치 리눅스에서 GCC 4.8.1를 사용합니다. 나는 C++ 11을 사용한다.
  2. 난 그냥 (내가 필요한 자리의 번호를 모르는) 표준 : 바인드에 사용 된 자리 표시자를 사용하는 것은 여기 표준 : 기능을 사용하지 않았다. 그리고 이것은 결국 좋은 습관과 학습입니다. std :: function을 사용하여이 작업을 수행하는 방법을 알고 있다면 대답은 매우 환영합니다.
  3. 는 사실, 동일한 서명 (신호/슬롯 종류의 프레임 워크)의 많은 "콜백"을 호출하기위한 표준 : : 벡터에서이 클래스를 사용합니다.

는 너무 감사드립니다. 나는이 엉망의 어떤 측면을 명확히 해줄 것이다. 필요하다면 :

답변

1

오랫동안 잘못된 포인터 유형으로 주소에 액세스하지 않았다.당신은 규칙을 어 기지 않습니다. 그래서 이것은 문제가되지 않습니다. 비슷하게 취급되지만 동일하지 않은 다른 유형에 대한 불량 처리 된 포인터에 의해 액세스하는 경우 규칙을 위반하고있는 것입니다. 어쩌면 당신은 allready FreeBSD 소켓을 사용했을 것입니다. 그들은 sockadr_in을 sockadr로 캐스팅했기 때문에 bind() 함수에 던져 넣을 것입니다. 경고는 규칙을 어길 수 있다고 말하지만 구현으로 인해 규칙을 위반할 수 있습니다. 이 질문에 설명 된대로 좋음 : Berkley Sockets, breaking aliasing rules?

당신이 갈 것이라는 것을 확신하지 못한다면 char *로 캐스트 할 수도 있습니다. 왜냐하면 (c99에서와 나는 그것이 C++ 표준에서 같아야한다.) char *는 어떤 것도 앨리어스 할 수 있기 때문이다. 그래서 당신은 C + +에서 당신도 다른 유형에서 캐스팅과 템플릿을 처리 할 수있는 char 및 다시 함수 안에. (좋아, char * cast는 당신의 기능이 오직 사용을위한 것이 아닌 경우 좋은 생각이 아닙니다.)