2010-01-14 1 views
5

È questo davvero l'unico modo per ottenere l'indirizzo corretto per una funzione di esempio:c'è un modo migliore per selezionare il sovraccarico del metodo corretto?

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

In primo luogo, si deve creare un typedef, e quindi si deve usare quello per forzare il compilatore per selezionare la corretta sovraccarico metodo quando si prende il suo indirizzo?

non c'è una sintassi che è più naturale e autosufficiente, come ad esempio:

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

Io uso ScopeGuard spesso nel mio codice. E un ovvio uso è assicurare che qualsiasi oggetto CDC temporaneo venga prima selezionato in un dato DC, poi rimosso all'uscita dallo scope, rendendo il mio codice privo di perdite anche in circostanze eccezionali - e contemporaneamente ripulendo il codice scritto (stupidi percorsi di uscita multipli e provare/catch e così via per cercare di gestire la rimozione di qualsiasi oggetto selezionato da un dato CDC).

Così un esempio più completo di quello che attualmente sono costretto a fare assomiglia:

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

E 'sempre mi ha colpito come goffo come l'inferno che ho bisogno di typedef ogni funzione sovraccaricata che desidero prendere la indirizzo di.

Quindi ... c'è un modo migliore ?!

EDIT: Sulla base delle risposte persone hanno in dotazione, credo di avere una soluzione al mio bisogno di fondo: cioè di avere una sintassi più naturale per MakeGuard che deduce la corretta sostituzione SelezionaOggetto per me:

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

che rende la mia sopra modifica del codice a:

ScopeGuard guard_canvas = MakeSelectObjectGuard(memdc, &canvas); 

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

Per coloro che potrebbero cercare qui per una versione non MFC della stessa cosa:

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

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

Grazie a tutti coloro che hanno risposto & commentato! : D

+0

Puoi farlo, ma sicuramente non sarà bello: praticamente finirai per inserire il corpo del typedef dove attualmente usi il nome typedef. – James

+0

Spero che non sia questo CBitmap: http://thedailywtf.com/Articles/The-cbitmap.aspx –

risposta

2

Quello che chiedi è simile a una domanda precedente, e la risposta che ho dato qui è pertinente anche qui.

Dalla sezione 13.4/1 ("Indirizzo di funzione in overload," [over.over]):

Un uso di un nome di funzione sovraccaricata senza argomenti si risolve in determinati contesti di una funzione, un puntatore alla funzione o puntatore alla funzione membro per una funzione specifica dal set di sovraccarico. Un nome di modello di funzione è considerato per denominare un insieme di funzioni sovraccaricate in tali contesti. La funzione selezionata è quella il cui tipo corrisponde al tipo di destinazione richiesto nel contesto. L'obiettivo può essere

  • un oggetto o di riferimento essendo inizializzato (8.5, 8.5.3),
  • lato sinistro di un'assegnazione (5,17),
  • un parametro di una funzione (5.2.2) ,
  • un parametro di un operatore definito dall'utente (13.5),
  • il valore di ritorno di una funzione, funzione operatore, o conversione (6.6.3), o
  • una conversione esplicita (5.2.3, 5.2.9, 5.4).

Il nome della funzione di sovraccarico può essere preceduto dall'operatore &. Un nome funzione sovraccarico non deve essere utilizzato senza argomenti in contesti diversi da quelli elencati. [Nota: qualsiasi insieme ridondante di parentesi che circonda il nome della funzione sovraccarica viene ignorato (5.1). ]

Nel tuo caso, il bersaglio dalla lista qui sopra è il terzo, un parametro della funzione MakeObjGuard. Tuttavia, ho il sospetto che sia un modello di funzione e uno dei parametri di tipo per il modello è il tipo di puntatore a funzione. Il compilatore ha un Catch-22. Non è possibile dedurre il tipo di parametro template senza sapere quale sovraccarico è selezionato, e non può selezionare automaticamente quale sovraccarico si intende senza conoscere il tipo di parametro.

Pertanto, è necessario dare una mano. È possibile digitare il cast del puntatore del metodo, come si fa ora, oppure è possibile specificare esplicitamente il tipo di argomento del modello quando si chiama la funzione: MakeObjGuard<SelectObjectBitmap>(...). Ad ogni modo, è necessario conoscere il tipo. Non è strettamente necessario avere un nome typedef per il tipo di funzione, ma sicuramente aiuta la leggibilità.

+0

Il MakeObjGuard è simile a MakePair - Voglio che la funzione deduca i tipi di argomento e produca l'oggetto modello corretto per gli argomenti specificati (proprio come MakePair produce una coppia std :: coppia ). Quindi ogni "aiuto" che devo dare alla funzione MakeXXX deve essere nella sua lista degli argomenti, piuttosto che selezionare manualmente il modello risultante (anche se questo potrebbe essere fatto, è antitetico). – Mordachai

+0

Apprezzo il tuo problema, ma avresti lo stesso problema nel chiamare 'make_pair' con un puntatore di funzione sovraccarico mentre stai chiamando' MakeObjGuard'. Il compilatore non può dedurre tipi di template se non conosce i tipi di argomento. –

+0

Sono soddisfatto, almeno per il momento, con il codice che ho ricavato dal tuo e altri di aiuto sull'argomento. Ottieni la risposta ufficiale per la risposta più completa. Grazie ancora :) – Mordachai

2

Non è proprio necessario utilizzare typedef. Stai semplicemente utilizzando typedef per creare un nome per un tipo, quindi utilizzare quel tipo per definire il puntatore e in un typecast per inizializzare il puntatore. Se lo si desidera, è possibile utilizzare il tipo direttamente per entrambi, ma si finisce per ripetere lo stesso (spesso lungo) tipo sia per la definizione che per il typecast. Per un po 'semplificato, ad esempio standalone: ​​

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

Mentre questo è possibile, e dovrebbe essere accettato da qualsiasi compilatore correttamente funzionante, non posso dire che mi piacerebbe davvero consigliare - mentre crea e inizializza il puntatore in una singola istruzione, se il tipo in questione ha un nome lungo, probabilmente finirà comunque con un paio di righe di codice, solo a causa della lunghezza.

credo C++ 0x di, auto dovrebbero permettere il primo esempio di cui sopra per essere ridotto a qualcosa di simile:

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

Questo dovrebbe rendere molto più facile per evitare il typedef (ea seconda del compilatore sei usando, potresti già averlo disponibile).

+0

Bene, ancora una volta, non c'è bisogno di un cast in questo caso particolare, indipendentemente dal fatto che usi un typedef o meno. Il compilatore è necessario per selezionare il sovraccarico corretto anche senza cast. – AnT

1

si può evitare di utilizzare il typedef, ma in realtà non è abbastanza:

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

meglio attenersi al typedef.

0

Sembra che tu abbia frainteso la radice del problema. Finché il tipo concreto sul lato ricevente della cessione/inizializzazione è noto per il compilatore, non si dovrebbe fare alcuno sforzo per "selezionare" a tutti, Se il typedef è definito come

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

poi l'inizializzazione

SelectObjectBitmap pmf = &CDC::SelectObject; 

è necessaria per invocare la risoluzione di sovraccarico e selezionare la versione corretta di overload della funzione senza la necessità di un cast esplicito. Questo è in realtà solo un posto in C++ quando la risoluzione di sovraccarico sul lato destro dell'espressione/inizializzazione dipende dal lato sinistro.

Naturalmente, lo stesso dovrebbe funzionare senza un typedef.


OK, visto che quello che si sta effettivamente facendo sta passando l'indirizzo del metodo per una funzione template come un tipo di argomento dipendente: naturalmente, in questo caso bisogna o 1) selezionare il corretto sovraccarichi da utilizzando un cast, o 2) correggere il tipo di parametro della funzione specificando esplicitamente l'argomento del template.

Naturalmente, si può solo pre-selezionare la corretta sovraccarico facendo

SelectObjectBitmap pmf = &CDC::SelectObject; 

e quindi passare pmf alla funzione del modello.