Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

is there a better way to select correct method overload?

Tags:

Is this really the only way to get the correct address for an instance function:

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

First, one has to create a typedef, and then one has to use that to force the compiler to select the correct overloaded method when taking its address?

Is there no syntax that is more natural, and self-contained, such as:

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

I use ScopeGuard often in my code. And one obvious use is to ensure that any temporary CDC objects are first selected into a given DC, then removed at scope exit, making my code leak free even under exceptional circumstances - and simultaneously cleaning up the code written (stupid multiple exit paths and try/catch and so on to try to handle removing any selected objects from a given CDC).

So a more complete example of what I currently am forced to do looks like:

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

It has always struck me as goofy as hell that I need to typedef every overloaded function which I wish to take the address of.

So... is there a better way?!

EDIT: Based on the answers folks have supplied, I believe I have a solution to my underlying need: i.e. to have a more natural syntax for MakeGuard which deduces the correct SelectObject override for 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));
}

Which makes my above code change to:

ScopeGuard guard_canvas = MakeSelectObjectGuard(memdc, &canvas);

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

For those who might look here for a non-MFC version of the same thing:

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

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

Thanks Everyone who replied & commented! :D

like image 418
Mordachai Avatar asked Jan 14 '10 15:01

Mordachai


1 Answers

What you're asking is similar to an earlier question, and the answer I gave there is relevant here as well.

  • Conditional operator can’t resolve overloaded member function pointers

From section 13.4/1 ("Address of overloaded function," [over.over]):

A use of an overloaded function name without arguments is resolved in certain contexts to a function, a pointer to function or pointer to member function for a specific function from the overload set. A function template name is considered to name a set of overloaded functions in such contexts. The function selected is the one whose type matches the target type required in the context. The target can be

  • an object or reference being initialized (8.5, 8.5.3),
  • the left side of an assignment (5.17),
  • a parameter of a function (5.2.2),
  • a parameter of a user-defined operator (13.5),
  • the return value of a function, operator function, or conversion (6.6.3), or
  • an explicit type conversion (5.2.3, 5.2.9, 5.4).

The overload function name can be preceded by the & operator. An overloaded function name shall not be used without arguments in contexts other than those listed. [Note: any redundant set of parentheses surrounding the overloaded function name is ignored (5.1). ]

In your case, the target from the above list is the third one, a parameter of your MakeObjGuard function. However, I suspect that's a function template, and one of the type parameters for the template is the type of the function pointer. The compiler has a Catch-22. It can't deduce the template parameter type without knowing which overload is selected, and it can't automatically select which overload you mean without knowing the parameter type.

Therefore, you need to help it out. You can either type-cast the method pointer, as you're doing now, or you can specify the template argument type explicitly when you call the function: MakeObjGuard<SelectObjectBitmap>(...). Either way, you need to know the type. You don't strictly need to have a typedef name for the function type, but it sure helps readability.

like image 75
Rob Kennedy Avatar answered Oct 12 '22 09:10

Rob Kennedy