Archive for October 11th, 2007

Stoopid Intarweb

Thursday, October 11th, 2007

Can’t figure out why the middle keeps shifting down on this thing. Blarg.

MFC and Multiple Inheritance

Thursday, October 11th, 2007

A lot of programmers scoff at multiple inheritance, and it is not something I would use very often, but there are times when it makes sense. Recently I did some refactoring to some dialog classes, which are based on MFC. Looking at what we had, it was a perfect case for using multiple inheritance. But then I found out after trying to implement it that apparently the folks who wrote MFC don’t believe in multiple inheritance either. They have this to say in the linked article:

We have found that MI is not required to write a class library, nor is it required for writing serious applications.

Are you kidding me?! That’s a rather condescending remark to say the least, especially from the folks who brought us COM! I would suggest to Microsoft that MFC is not required for writing serious applications either.

The Details…

What I had to deal with were some dialogs that are assembled on the fly. We have control classes that do special things, so naturally there is a class for each one: label, checkbox, edit, button, etc. The previous implementation used a wrappered approach: there was a class for each control and private to that class was an instance of an MFC control. Then each class defined its own methods for all of the usual control functions, like positioning, font, color, etc. with each method operating on its own private control. A lot of redundant code, and compounding it was a lot of application-specific code repeated in each class as well.

So my main goal for this refactor was to eliminate as much redundant code as possible.

First I needed a base class for my controls, to handle the application functions common to all of them. This eliminated a lot of redundancy, but I still had the same SetText, SetColor, etc. defined in each class. Here is where multiple inheritance would be key. I wanted to make my base control class a descendant of CWnd (base class to MFC controls), so that I could handle all of the common control operations in one class. Then I would make each of my control classes descendants of the MFC controls they implement. Only the functions specific to each control, like handling a checkbox would need to go in the descendant classes. 90% of the work can be done in one base class.

The structure is like this:

class CMyControl : virtual public CWnd {…}

class CMyEdit : public CEdit, public CMyControl {…}

With this structure, I can handle all of the basic control operations, like setting text, changing colors, moving, etc. in CMyControl. Each derived control class can deal only with what it needs to.

You might be wondering, aren’t there two copies of CWnd in the v-table? One for CMyControl’s base class, and one for CMyEdit’s instance of CEdit? Normally there would be, and that would be a problem, but that’s why C++ gives you the option to declare a virtual base class. This tells the compiler to keep only one instance of CWnd in the class, so any operations on CMyControl’s CWnd will actually operate on CEdit’s CWnd.

It is a beautiful design, and one that eliminates all redundant code. Except for one small problem: IT WON’T COMPILE!!

It won’t compile because of how MFC is implemented, and given the remark I quoted above, I think some folks at Microsoft need to go back to OOP school. ;-)

The workaround I ended up using, was to remove the virtual base class, and instead use a GetCWnd method so that each descendant class can return a pointer to it’s underlying CWnd. I made GetCWnd a pure virtual method in the base class so that each descendant is required to implement it.

So now, instead of a nice clean call like this:

mycontrol.SetWindowText("This is great!");

I have to write:

mycontrol.GetCWnd()->SetWindowText("This really blows!");

Sure, I could go and write a pass-through method for each and every CWnd method, but who wants to do that?

Even worse, MFC message handlers have to be repeated in each descendant class! Ugh. I’ve got handlers for things like CtlColor and OnSetCursor. It’s the exact same code for each class, but now I have to copy and paste it into each of my control classes. Note: if you find yourself cutting and pasting code into multiple classes, then something is wrong!

All in all, the classes are much better than they were, but they could have been perfect.

Technorati Tags: , ,