Boost logo

Boost :

From: Eugene Lazutkin (eugene_lazutkin_at_[hidden])
Date: 2003-07-19 23:11:56


Inline

"David Abrahams" <dave_at_[hidden]> wrote in message
news:usmp21ijr.fsf_at_boost-consulting.com...
> >
> > Example for #1. Windows GDI has hundreds (if not thousands) API
functions.
> > New versions of Windows introduce new API functions. It doesn't make any
> > sense to cover it with functional wrapper. MFC tried it and failed.
>
> How do you measure failure? Certainly MFC is primitive, but tens of

It is a Boolean function --- pass/fail. :-) Take a look at wrapped handles
in, say, afxwin. You will find a lot of marvels of object-oriented code like
this:

...
_AFXWIN_INLINE COLORREF CDC::GetBkColor() const
 { ASSERT(m_hAttribDC != NULL); return ::GetBkColor(m_hAttribDC); }
_AFXWIN_INLINE int CDC::GetBkMode() const
 { ASSERT(m_hAttribDC != NULL); return ::GetBkMode(m_hAttribDC); }
_AFXWIN_INLINE int CDC::GetPolyFillMode() const
 { ASSERT(m_hAttribDC != NULL); return ::GetPolyFillMode(m_hAttribDC); }
_AFXWIN_INLINE int CDC::GetROP2() const
 { ASSERT(m_hAttribDC != NULL); return ::GetROP2(m_hAttribDC); }
_AFXWIN_INLINE int CDC::GetStretchBltMode() const
 { ASSERT(m_hAttribDC != NULL); return ::GetStretchBltMode(m_hAttribDC); }
_AFXWIN_INLINE COLORREF CDC::GetTextColor() const
 { ASSERT(m_hAttribDC != NULL); return ::GetTextColor(m_hAttribDC); }
_AFXWIN_INLINE int CDC::GetMapMode() const
 { ASSERT(m_hAttribDC != NULL); return ::GetMapMode(m_hAttribDC); }
...

Majority of methods are like that. I call this style of OOP "failure" in
large part because it doesn't make much sense to me. What is the point of
replacing XXX(a) with a.XXX()? It is all done so you can program your
graphics in "cool" way (pseudo code):

CDC dc;
...
{
CPen pen( PS_SOLID, 0, RGB(0,0,0) );
CBrush brush( RGB(255,255,255) );
CPen* old_pen = dc.SelectObject(&pen);
CBrush* old_brush = dc.SelectObject(&brush);
dc.Rectangle( 0, 0, 100, 100 );
dc.SelectObject(old_brush);
dc.SelectObject(old_pen);
}
...

Compare it with "boring old" code:

HDC dc = NULL;
...
HPEN pen = CreatePen( PS_SOLID, 0, RGB(0,0,0) );
HBRUSH brush = CreateSolidBrush( RGB(255,255,255) );
HGDIOBJ old_pen = SelectObject( dc, pen );
HGDIOBJ old_brush = SelectObject( dc, brush );
Rectangle( dc, 0, 0, 100, 100 );
SelectObject( dc, old_brush );
SelectObject( dc, old_pen );
...

Added value of MFC methods (functionally) is close to nothing. Almost the
same amount of characters to type. Of course, CDC methods have asserts in
them and can help you to debug a code. Another notable difference is handle
destruction. In my example I didn't do it (two extra lines of code):

DeleteObject(brush);
DeleteObject(pen);

This difference is the only reason for me why MFC wrappers are useful.

Now imaging that we use simple smart handles for HDC, HPEN, and HBRUSH.
Let's call them Dc, Pen, and Brush respectively. These smart handles
provides housekeeping by calling appropriate functions in destructor and
(just like MFC) operator Handle(). All of them would be about ten lines of
code (pseudo code to give an idea):

struct Pen
{
 explicit Pen( HPEN h ) : hp(h) {}
 ~Pen() { DeleteObject(hp); }

 operator HPEN () const { return hp; }
 operator HGDIOBJ () const { return hp; }

 HPEN hp;
}

Additionally let's introduce SafeSelectObject (pseudo code):

struct SafeSelectObject
{
 SafeSelectObject( HDC h, HGDIOBJ obj ) :
    hDC(h), old_handle( ( h && obj ) ? SelectObject( h, obj ) : NULL ) {}
 ~SafeSelectObject() { if( hDC && old_handle ) SelectObject( hDC,
old_handle ); }

 HDC hDC;
 HGDIOBJ old_handle;
};

Now our example looks like this:

Dc dc;
...
{
Pen pen( CreatePen( PS_SOLID, 0, RGB(0,0,0) ) );
Brush brush( CreateSolidBrush( RGB(255,255,255) ) );
SafeSelectObject old_pen( dc, pen );
SafeSelectObject old_pen( dc, brush );
Rectangle( dc, 0, 0, 100, 100 );
}
...

Hey, it looks like MFC! Almost. The difference? I didn't write _huge_
classes like CDC --- I tend to think that class with hundred methods is "bad
idea". These handles support all GDI functions including new ones like
exotic AlphaBlend() or GradientFill(). If you want to do asserts on your
handles like MFC does, you can do it in conversion operator.

Now I think you understand my point.

> thousands of developers use it every day. There are better

Count me in. :-)

> alternatives - Microsoft tells me they have been shipping an
> "unsupported" library which is "what MFC would have been if we'd
> started with templates".

WTL? ;-)

Please understand me correctly. I am not trying to prove that MFC is evil.
:-) Given its historical background and necessity to support legacy
versions, probably it is as good as it can be. Of course, we should not
forget that MFC is _much_ more than collection of simple handle wrappers.
But I don't want our smart handles to be done in MFC way.

Please note that in exercise above I didn't use any "new" C++ features,
which were not available back in 1992. I didn't even use templates! But
provided functionality is pretty much similar. :-))

Thanks,

Eugene


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk