2010-01-14 4 views
5

Ist das wirklich der einzige Weg, die richtige Adresse für eine Instanz Funktion zu erhalten:gibt es eine bessere Möglichkeit, die richtige Methodenüberladung auszuwählen?

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

Zuerst hat man einen typedef zu erstellen, und dann hat man das verwenden, um die Compiler zu zwingen, die richtigen überlastete zu wählen Methode, wenn man seine Adresse nimmt?

Gibt es keine Syntax, die mehr natürlich ist, und in sich geschlossene, wie zum Beispiel:

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

ich ScopeGuard oft in meinem Code verwenden. Und eine naheliegende Verwendung besteht darin, sicherzustellen, dass alle temporären CDC-Objekte zuerst in einem bestimmten DC ausgewählt und dann beim Exit entfernt werden, so dass mein Code auch unter außergewöhnlichen Umständen frei wird - und gleichzeitig den geschriebenen Code aufräumt (dumme mehrfache Exit-Pfade)/catch und so weiter, um zu versuchen, ausgewählte Objekte von einem bestimmten CDC zu entfernen).

So ein vollständigeres Beispiel dafür, was ich zur Zeit gezwungen bin, sieht zu tun:

// 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); 

Es hat mich immer geschlagen wie doof wie die Hölle, die ich jede überladene Funktion typedef muß, die ich wünschte, das nehmen Adresse von.

Also ... gibt es einen besseren Weg ?!

EDIT: Basierend auf den Antworten Leute geliefert haben, ich glaube, ich eine Lösung für mein zugrunde liegende Bedürfnis haben: dh eine natürlichere Syntax für MakeGuard zu haben, die für mich die richtige AuswählenObjekt Überschreibung folgert:

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)); 
} 

Welche meiner obigen Codeänderung macht:

ScopeGuard guard_canvas = MakeSelectObjectGuard(memdc, &canvas); 

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

Für diejenigen, die hier für eine nicht-MFC-Version der gleichen Sache aussehen könnte:

////////////////////////////////////////////////////////////////////////// 
// 
// 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; 
}; 

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

Dank Jeder, antwortete & kommentiert! : D

+0

Sie können es tun, aber es wird sicherlich nicht hübsch sein - Sie werden im Grunde den Körper des Typedef einfügen, wo Sie derzeit den Typedef-Namen verwenden. – James

+0

Hoffe, es ist nicht diese CBitmap: http://thedailywtf.com/Articles/The-cbitmap.aspx –

Antwort

2

Was Sie fragen ist ähnlich einer früheren Frage, und die Antwort, die ich dort gab, ist auch hier relevant.

Aus dem Bereich 13,4/1 ("Adresse der überladenen Funktion" [over.over]):

Verwendung einer überladenen Funktionsnamen ohne Argumente gelöst ist in bestimmten Kontexten auf eine Funktion, einen Zeiger auf Funktion oder Zeiger auf Elementfunktion für eine bestimmte Funktion aus der Überladungsmenge. Ein Funktionsschablonenname wird dazu verwendet, in solchen Kontexten einen Satz überladener Funktionen zu benennen. Die ausgewählte Funktion ist diejenige, deren Typ dem im Kontext erforderlichen Zieltyp entspricht. Das Ziel kann seine

  • ein Objekt oder eine Referenz initialisiert wird (8.5, 8.5.3),
  • die linke Seite einer Zuweisung (5.17),
  • ein Parameter einer Funktion (5.2.2) ,
  • ein Parameter eines benutzerdefinierten Operator (13.5),
  • der Rückgabewert einer Funktion, bediener~~POS=TRUNC oder Umwandlung (6.6.3) oder
  • eine explizite Typkonvertierung (5.2.3, 5.2.9, 5.4).

Dem Namen der Überladungsfunktion kann der Operator & vorangestellt werden. Ein überladener Funktionsname darf nicht ohne Argumente in anderen als den aufgelisteten Kontexten verwendet werden. [Hinweis: alle redundanten Klammern, die den überladenen Funktionsnamen umgeben, werden ignoriert (5.1).

]

In Ihrem Fall das Ziel aus der obigen Liste ist der dritte, ein Parameter Ihrer MakeObjGuard Funktion. Ich vermute jedoch, dass dies eine Funktionsvorlage ist und einer der Typparameter für die Vorlage der Typ des Funktionszeigers ist. Der Compiler hat eine Catch-22. Es kann den Schabloneparametertyp nicht ableiten, ohne zu wissen, welche Überladung ausgewählt ist, und es kann nicht automatisch auswählen, welche Überladung Sie meinen, ohne den Parametertyp zu kennen.

Daher müssen Sie es aushelfen. Sie können den Methodenzeiger entweder wie in der aktuellen Situation eingeben oder den Vorlagenargumenttyp explizit angeben, wenn Sie die Funktion aufrufen: MakeObjGuard<SelectObjectBitmap>(...). In jedem Fall müssen Sie den Typ kennen. Sie müssen nicht unbedingt einen typedef-Namen für den Funktionstyp haben, aber es hilft Ihnen, die Lesbarkeit zu verbessern.

+0

Der MakeObjGuard ähnelt MakePair - ich möchte, dass die Funktion die Argumenttypen ableitet und das korrekte Template-Objekt für die gegebenen Argumente erzeugt (so wie MakePair ein std :: pair erzeugt). Also muss jede "Hilfe", die ich der MakeXXX-Funktion geben muss, in der Argumentliste enthalten sein, anstatt manuell die resultierende Vorlage auszuwählen (obwohl dies möglich wäre, es ist antithetisch). – Mordachai

+0

Ich schätze Ihr Problem, aber Sie würden die gleichen Probleme haben, 'make_pair' mit einem überladenen Funktionszeiger aufzurufen, wenn Sie' MakeObjGuard' aufrufen. Der Compiler kann keine Vorlagentypen ableiten, wenn er die Argumenttypen nicht kennt. –

+0

Ich bin zumindest für den Moment zufrieden mit dem Code, den ich von dir und anderen helfe. Sie erhalten die offizielle Antwort für die vollständigste Antwort. Danke noch einmal :) – Mordachai

2

Sie müssen nicht wirklich die Typedef verwenden. Sie verwenden nur typedef, um einen Namen für einen Typ zu erstellen, verwenden diesen Typ dann zum Definieren Ihres Zeigers und in einer Typumwandlung zum Initialisieren des Zeigers. Wenn Sie wirklich wollen, können Sie den Typ direkt für beide verwenden, aber Sie wiederholen den gleichen (oft langen) Typ für die Definition und die Typumwandlung. Für eine etwas vereinfachte, eigenständiges Beispiel:

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; 
} 

Während dies möglich ist, und sollte von jedem gut funktionierenden Compiler akzeptiert werden, kann ich nicht sagen, dass ich es wirklich raten würde - während es erstellt und initialisiert den Zeiger in einer einzigen Anweisung, wenn der Typ einen langen Namen hat, wird es wahrscheinlich nur ein paar Zeilen Code, nur wegen der Länge.

Ich glaube, C++ 0x ist, auto über das erste Beispiel erlauben sollte so etwas verkürzt werden:

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

Dies sollte es viel einfacher macht den typedef (und abhängig von den Compiler vermeiden Sie sind verwenden, haben Sie möglicherweise bereits verfügbar).

+0

Nun, wieder, es gibt keine Notwendigkeit für eine Besetzung in diesem speziellen Fall, unabhängig davon, ob es eine Typedef verwendet oder nicht. Der Compiler muss die richtige Überladung auch ohne Besetzung auswählen. – AnT

1

können Sie vermeiden, dass die typedef verwenden, aber es ist wirklich nicht schön:

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); 
} 

besser auf die typedef zu bleiben.

0

Sie scheinen die Ursache des Problems falsch zu verstehen. Solange die konkrete Art auf der Empfangsseite der Zuordnung/Initialisierung an den Compiler bekannt ist, werden Sie keine Mühe machen, soll es zu „wählen“ überhaupt, Wenn Ihr typedef als

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

dann die Initialisierung

SelectObjectBitmap pmf = &CDC::SelectObject; 

ist erforderlich, Überladungsauflösung und wählen Sie die richtige überladene Version der Funktion, ohne die Notwendigkeit für eine explizite Umwandlung aufzurufen. Dies ist eigentlich nur ein Ort in C++, wenn die Überladungsauflösung auf der rechten Seite des Ausdrucks/der Initialisierung von der linken Seite abhängt.

Natürlich sollte das gleiche ohne eine typedef funktionieren.


OK Sehen Sie das, was Sie tatsächlich vergehen die Adresse des Verfahrens auf eine Template-Funktion als abhängigen Argumenttyp zu tun: natürlich, in diesem Fall müssen Sie entweder 1) die richtige Überlastung auswählen, indem Sie Verwenden Sie eine Umwandlung, oder 2) beheben Sie den Parameter-Funktionstyp, indem Sie das Template-Argument explizit angeben.

Natürlich können Sie nur eine Vorauswahl die richtige Überlastung durch

tun
SelectObjectBitmap pmf = &CDC::SelectObject; 

und dann pmf auf Ihre Template-Funktion übergeben.

Verwandte Themen