2010-01-14 2 views
5

는 인스턴스 함수에 대한 올바른 주소를 얻을이 정말있는 유일한 방법입니다 :올바른 방법 과부하를 선택하는 더 좋은 방법은 무엇입니까?

typedef CBitmap * (CDC::* SelectObjectBitmap)(CBitmap*); 
SelectObjectBitmap pmf = (SelectObjectBitmap)&CDC::SelectObject; 

첫째, 하나는 타입 정의를 만들 수 있으며, 다음 중 하나에 과부하가 올바른을 선택하도록 컴파일러에 강제로 그것을 사용하는 방법을 그 주소를 취할 때? 내 코드에서 자주 ScopeGuard를 사용

SelecdtObjectBitmap pmf = &CDC::SelectObject(CBitmap*); 

:

이 같은 자연 및 독립적 인 아니 구문 없습니다. 한 가지 확실한 용도는 임시 CDC 개체가 지정된 DC로 먼저 선택된 다음 범위 종료에서 제거되어 예외적 인 상황에서도 코드가 누출되지 않도록하고 동시에 작성된 코드를 정리하는 것입니다 (바보 같은 다중 종료 경로 및 시도/catch 등을 사용하여 주어진 CDC에서 선택한 객체를 제거하려고 할 수 있습니다. 그것은 항상 난을하고자하는 나는 모든 오버로드 기능을 형식 정의를 할 필요가 지옥처럼 바보 같은 내게 쳤다

// get our client rect 
CRect rcClient; 
GetClientRect(rcClient); 

// get the real DC we're drawing on 
PAINTSTRUCT ps; 
CDC * pDrawContext = BeginPaint(&ps); 

// create a drawing buffer 
CBitmap canvas; 
canvas.CreateCompatibleBitmap(pDrawContext, rcClient.Width(), rcClient.Height()); 

CDC memdc; 
memdc.CreateCompatibleDC(pDrawContext); 

//*** HERE'S THE LINE THAT REALLY USES THE TYPEDEF WHICH i WISH TO ELIMINATE ***// 
ScopeGuard guard_canvas = MakeObjGuard(memdc, (SelectObjectBitmap)&CDC::SelectObject, memdc.SelectObject(&canvas)); 

// copy the image to screen 
pDrawContext->BitBlt(rcClient.left, rcClient.top, rcClient.Width(), rcClient.Height(), &memdc, rcClient.left, rcClient.top, SRCCOPY); 

// display updated 
EndPaint(&ps); 

: 같은

그래서 나는 현재하도록 강요하고 무엇보다 완전한 예를 본다 의 주소.

그래서 ... 더 좋은 방법이 있습니까?!

편집 : 나를 위해 올바른 SelectObject 매크로 재정의 추론 MakeGuard에 대한 더 자연스러운 구문을 가지고 예 :

template <class GDIObj> 
ObjScopeGuardImpl1<CDC, GDIObj*(CDC::*)(GDIObj*), GDIObj*> MakeSelectObjectGuard(CDC & dc, GDIObj * pGDIObj) 
{ 
    return ObjScopeGuardImpl1<CDC, GDIObj*(CDC::*)(GDIObj*), GDIObj*>::MakeObjGuard(dc, (GDIObj*(CDC::*)(GDIObj*))&CDC::SelectObject, dc.SelectObject(pGDIObj)); 
} 
사람들이 제공 한 답변을 바탕으로, 나는 나의 기본적인 필요에 대한 해결책을 믿습니다 내 위의 코드 변경하는

:

ScopeGuard guard_canvas = MakeSelectObjectGuard(memdc, &canvas); 

///////////////////////////////// ///////////////////////

같은 일의 비 MFC 버전 여기를 보일 수 있습니다 사람들을 위해 0

:

////////////////////////////////////////////////////////////////////////// 
// 
// AutoSelectGDIObject 
// selects a given HGDIOBJ into a given HDC, 
// and automatically reverses the operation at scope exit 
// 
// AKA: 
// "Tired of tripping over the same stupid code year after year" 
// 
// Example 1: 
// CFont f; 
// f.CreateIndirect(&lf); 
// AutoSelectGDIObject select_font(*pDC, f); 
// 
// Example 2: 
// HFONT hf = ::CreateFontIndirect(&lf); 
// AutoSelectGDIObject select_font(hdc, hf); 
// 
// NOTE: 
// Do NOT use this with an HREGION. Those don't need to be swapped with what's in the DC. 
////////////////////////////////////////////////////////////////////////// 

class AutoSelectGDIObject 
{ 
public: 
    AutoSelectGDIObject(HDC hdc, HGDIOBJ gdiobj) 
     : m_hdc(hdc) 
     , m_gdiobj(gdiobj) 
     , m_oldobj(::SelectObject(m_hdc, gdiobj)) 
    { 
     ASSERT(m_oldobj != m_gdiobj); 
    } 

    ~AutoSelectGDIObject() 
    { 
     VERIFY(m_gdiobj == ::SelectObject(m_hdc, m_oldobj)); 
    } 

private: 
    const HDC  m_hdc; 
    const HGDIOBJ m_gdiobj; 
    const HGDIOBJ m_oldobj; 
}; 

//////////////////////// ///////////////////////

감사합니다. &이 (가) 댓글을 달았습니다. : D

+0

당신은 할 수는 있지만, 확실히는 좋지 않을 것입니다 - 기본적으로 typedef 이름을 사용하는 typedef의 본문을 삽입하게 될 것입니다. – James

+0

희망이 CBitmap이 아닙니다 : http://thedailywtf.com/Articles/The-cbitmap.aspx –

답변

2

당신이 묻는 것은 이전 질문과 유사합니다. 그리고 제가 준 대답은 여기서도 관련이 있습니다.

인수없이 오버 함수명의 용도가 해결/1 ("오버로드 함수의 주소는"[over.over]) 13.4 부에서

특정 상황에서 함수에 대한 포인터 또는 오버로드 집합의 특정 함수에 대한 멤버 함수에 대한 포인터. 함수 템플리트 이름은 이러한 컨텍스트에서 오버로드 된 함수 세트를 명명하는 것으로 간주됩니다. 선택한 함수는 해당 유형이 컨텍스트에서 필요한 목표 유형과 일치하는 함수입니다. 타겟은,

  • 개체 또는 참조 (8.5, 8.5.3)으로 초기화되고있을 수
  • 할당 (5.17)의 좌측 함수의
  • 파라미터 (5.2.2) 사용자 정의 연산자 (135)의
  • 파라미터,
  • 함수 연산자 기능 또는 변환 (6.6.3)의 반환 값 또는
  • 명시 형식 변환 (5.2.3 5.2.9, 5.4).

오버로드 함수 이름 앞에는 & 연산자를 사용할 수 있습니다. 과부하 된 함수 이름은 나열된 것 이외의 문맥에서 인수없이 사용되어서는 안된다. [참고 : 오버로드 된 함수 이름을 둘러싼 괄호 세트는 무시됩니다 (5.1). ] 귀하의 경우

은 상기리스트에서 타겟는 세 번째, 네 MakeObjGuard 함수의 파라미터이다. 그러나, 나는 그것이 함수 템플리트라고 의심하고, 템플리트의 유형 매개 변수 중 하나는 함수 포인터의 유형입니다. 컴파일러에는 Catch-22가 있습니다. 어떤 과부하가 선택되었는지 알지 못하면 템플릿 매개 변수 유형을 추론 할 수 없으며 매개 변수 유형을 알지 못하면 어떤 과부하를 자동으로 선택할 수 없습니다.

따라서 도움이 필요합니다. 지금하고있는 것처럼 메소드 포인터를 타입 캐스트 할 수도 있고 함수를 호출 할 때 명시 적으로 템플릿 인수 유형을 지정할 수도 있습니다 : MakeObjGuard<SelectObjectBitmap>(...). 어느 쪽이든, 당신은 그 유형을 알아야합니다. 함수 유형에 대한 typedef 이름을 엄격하게 지정할 필요는 없지만 가독성을 높이는 데 도움이됩니다.

+0

MakeObjGuard는 MakePair와 유사합니다. MakePair가 std :: pair 을 생성하는 것처럼 함수가 인수 유형을 추론하고 주어진 인수에 대해 올바른 템플릿 객체를 생성하도록합니다. 따라서 MakeXXX 함수를 제공해야하는 "도움말"은 수동으로 결과 템플릿을 선택하는 대신 인수 목록에 있어야합니다 (이 작업은 수행 할 수 있지만 그 반대입니다). – Mordachai

+0

당신의 문제는 고맙겠지 만'MakeObjGuard'를 호출 할 때 오버로드 된 함수 포인터로'make_pair'를 호출하는 것과 같은 문제가있었습니다. 컴파일러는 인수 유형을 모르는 경우 템플릿 유형을 추론 할 수 없습니다. –

+0

필자는 적어도 현재 귀하와 다른 사람들로부터 파생 된 코드를 사용하여 만족하고 있습니다. 가장 완전한 답을 얻으려면 공식 답변을 얻으십시오. 다시 한번 감사드립니다 :) – Mordachai

2

실제로 typedef를 사용할 필요는 없습니다. typedef를 사용하여 유형의 이름을 만든 다음 해당 유형을 사용하여 포인터를 정의하고 유형 변환에서 포인터를 초기화하는 것입니다. 정말로 원한다면 두 가지 유형 모두에 직접 유형을 사용할 수 있지만 정의와 유형 변환에 대해 동일한 유형 (종종 긴 유형)을 반복하게됩니다. 다소 단순화, 독립형 예를 들어

struct XXX { 
    int member() { return 0; } 
    int member(int) { return 1; } 
}; 

int main() {  
    int (XXX::*pmfv)(void) = (int (XXX::*)())&XXX::member; 
    int (XXX::*pmfi)(int) = (int (XXX::*)(int))&XXX::member; 
    return 0; 
} 

이 가능하며, 어떤 제대로 작동 컴파일러에 의해 허용해야하지만, 난 정말 좋을 걸 말할 수 없다 -이 생성 및 포인터를 초기화하는 동안 관련된 문장이 긴 이름을 가지고 있다면, 길이가 길어질수록 아마도 두 줄의 코드로 끝날 것입니다.

auto pmf = (int (XXX::*)())&XXX::member; 

이 훨씬 쉽게 형식 정의를 방지 할 수 있도록 (당신이있어 컴파일러에 따라해야합니다

나는 C++의 0X 년대, auto 위 이런 식으로 단축 할 수있는 첫 번째 예제를 허용해야합니다 생각 사용하면 이미 사용 가능할 수 있습니다).

+0

typedef를 사용하는지 여부에 관계없이이 특별한 경우에 캐스팅을 할 필요가 없습니다. 컴파일러는 캐스트 없이도 적절한 과부하를 선택해야합니다. – AnT

1

당신은 타입 정의를 사용하지 않도록 할 수 있지만, 정말 꽤되지 않습니다 :

void foo(int){ 
    std::cout << "int" << std::endl; 
} 

void foo(float){ 
    std::cout << "float" << std::endl; 
} 

int main() 
{ 
    typedef void (*ffp)(float); 

    ffp fp = (void (*)(float)) foo; // cast to a function pointer without a typedef 

    ((ffp)fp)(0); 
} 

더 나은는 타입 정의에 충실.

0

문제의 근본 원인을 오해하는 것 같습니다. 만큼 할당/초기화의 수신 측의 구체적인 유형이 컴파일러에 알려진대로, 당신은 "선택"어떤 노력을 안하는 것이 전혀, 당신의 형식 정의는

typedef CBitmap * (CDC::* SelectObjectBitmap)(CBitmap*); 

으로 정의 된 경우 다음 초기화

SelectObjectBitmap pmf = &CDC::SelectObject; 

은 오버로드 확인을 호출하고 명시 적 캐스트가 필요없이 함수의 적절한 오버로드 버전을 선택해야합니다. 이것은 실제로 표현식/초기화의 오른쪽에있는 과부하 해결이 왼쪽에 의존 할 때 C++에서 단 하나의 장소입니다.

물론 typedef 없이도 작동합니다.


확인,보고 그 무엇을 당신이 실제로 종속 인수 형식으로 템플릿 함수에 방법의 주소를 통과하고있다 : 물론,이 경우에 당신도 1)에 의해 적절한 과부하를 선택해야 캐스트를 사용하거나 2) 명시 적으로 템플릿 인수를 지정하여 함수 매개 변수 유형을 수정하십시오.

물론, 당신은 단지

SelectObjectBitmap pmf = &CDC::SelectObject; 

을 수행하여 적절한 과부하를 미리 선택하고 다음 템플릿 함수에 pmf를 전달합니다.

관련 문제