Re: MFC and c++ problems
- From: Joseph M. Newcomer <newcomer@xxxxxxxxxxxx>
- Date: Sat, 04 Feb 2006 21:03:35 -0500
See below...
On Sat, 04 Feb 2006 14:13:38 GMT, Daniel James <wastebasket@xxxxxxxxxxxxxxxx> wrote:
In article news:<fa63u1pin4p62hk7umrn7bvrjvhlm3tk29@xxxxxxx>, Joseph M.****
Newcomer wrote:
So a control subclass becomes a completely self-contained class in my
world.... It cannot look into any other class that might contain it,
including CWinApp, CView, or CDialog classes that might contain it.
GetParent()->SendMessage is the interface of choice, and this can
usually be concealed with some reasonable programming techniques.
I agree with most of what you wrote in that long post, joe, but I have to
play Devil's Advocate with you here for a moment ...
You say that a control must make no assumptions about its parent.
But, if a control sends a message to its parent, is it not making the
assumption that its parent can handle that message? How is this any better
than calling a method defined by the parent?
That is not "making an assumption", that is "defining an interface". The control says
"here is something. Deal with it". It doesn't care if the parent deals with it or not.
This is quite different than knowing the parent has a CString variable that is going to
reflect something important.
****
****
Semantically, of course, the parent is free not to handle any message that
it doesn't understand. Your program containing a control that sends
messages to its parent will compile and run ... but may not WORK, because
the control may require the parent to handle the messages it sends, and
make some response.
Sure. And the point is? Key here is that it doesn't care HOW the parent handles it, and
if the parent fails to handle it, that is a problem with the parent. If the consequence
of this is that the program doesn't work, that isn't the problem of the control. It has
discharged its entire responsibility when it sends the message to the parent. And that's
a key idea in thinking about how to design controls.
*****
****
I agree wholeheartedly with you that it is a great strength of OO
programming that there is no coupling between unrelated program components
.. but here we're dealing with components which do have some logical
(i.e. real-world) coupling. We can't hide from that, and we should be
prepared to allow it to be manifest in our expressions of the program.
Actually, you do hide it. You don't care, when you embed a control, exactly how it
decides to send a message; as the container, you only care that a message has a specific
meaning. not how it is derived. As the control, you only care that you must send a
notification to your parent when something arises. Any coupling that involves the parent
reacting is independent of the control.
*****
****
If MyControl calls methods in its parent directly there is a visible
connection between control and parent that documents the real-world
connection between their behaviours. If you place that control in a parent
that doesn't provide the methods that the control needs to call your
program will fail to compile ... which is rather better, in my book, than
having it compile and run but not work.
But how can it? To do this, it has to know the name of the parent, the methods in the
parent, etc. This makes it completely non-reusable. If you change the nature of the
parent, even within your own project, you can make such controls no longer compilable. Key
to thinking about controls is that there is no change in the parent that you can make that
would require recompiling a control. Assume the control was compiled a month ago by
somebody who never heard of your container, and you only have the binary. The control's
behavior is well-specified, and is independent of any other component in its environment.
Thus it can be easily reused in any other context, in any other container. This is good
practice. Otherwise, you have a control that can be used in precisely one context, and
that seems pointless.
****
****
One could write an interface class that supported the method(s) needed by
the control, and make sure that any parent class using that control
inherited from the interface. One would pass the control's constructor a
pointer/reference to the parent *as an interface object* through which the
calls could be made ... that way the control does not need to know any
more about the parent class than is necessary, and the compiler can still
catch the error if an attempt is made to place the control in an
inappropriate parent.
Except for all the other problems of multiple inheritance, it might work, as long as the
interface has no knowledge of the parent (the implementation of the interface is where
such dependencies would be introduced).
*****
****
The trouble with using SendMessage is firstly that it *cannot* be made
typesafe in this way, and secondly that it imposes a (small) runtime
overhead.
I agree that SendMessage has type safety problems. This is a real downside of using it.
But Ifind the type safety issues easier to deal with than the non-reusability incurred by
including the parent class. I have a rich set of controls I use (many of which are on my
Web site) and I could not manage that if they were non-reusable. My key "value added" is
that I have such a rich set of controls in my stock, which I can quickly add into an app.
****
*****
Having made those points ... I must say that for most scenarios I can
think of I would actually use the SendMessage technique; for the usual
commonplace control-notifies-parent-of-user-action situations it is what
is expected. Windows uses messages for notifications of this kind all the
time, and allows messages to be spied upon, hooked, redirected, etc.; and
supports subclassing of the message sender/receiver without breaking the
overall model.
In a more formal interface mechanism, you could create ON_WHATEVER macros that linked to
functions of the appropriate prototype, thus hiding a lot of the type-safety issues in the
interface. On the whole, this is more work than is needed for most cases. because the
interfaces are clearly defined.
Note that I consder messages to be interfaces, that is, a typical message is declared as
/*******************************************************************
* UWM_WHATEVER
* Inputs:
* WPARAM: (WPARAM)(CString *) CString that contains whatever text
* LPARAM: (LPARAM)(UINT)flags
* flags is zero or more of
* FLAG_DOTHIS: explanation here
* FLAG_DOTHAT: explanation here
* FLAG_OTHER: explanation here
* Result:: LRESULT
* (LRESULT)(BOOL) TRUE if something happened successful, FALSE otherwise
* Effect:
* Notifies the parent that whatever is required should be done
*******************************************************************/
#define UWM_WHATEVER_MSG _T("UWM_WHATEVER-<guid here>")
Obviously, real messages have more meaningful descriptions than the toy example here, and
I'd put in a real GUID in the message type.
The other option, for LRESULT, by the way, is "Logically void, 0, always)
There is nothing more apalling when getting a piece of Windows code than having to figure
out, by reading the code, what the arguments to a message mean. In my code, EVERY
function, and EVERY message, gets a header like the one shown above. Sometimes they are
more elaborate. Some functions have longer headers than code, and the header may include
decision tables and other useful features as a "Notes" section.
joe
*****
Joseph M. Newcomer [MVP]
It's not a completely cut-and-dry issue, though ...
Cheers,
Daniel.
email: newcomer@xxxxxxxxxxxx
Web: http://www.flounder.com
MVP Tips: http://www.flounder.com/mvp_tips.htm
.
- Follow-Ups:
- Re: MFC and c++ problems
- From: Daniel James
- Re: MFC and c++ problems
- References:
- MFC and c++ problems
- From: Benry
- Re: MFC and c++ problems
- From: Joseph M . Newcomer
- Re: MFC and c++ problems
- From: Ajay Kalra
- Re: MFC and c++ problems
- From: Joseph M . Newcomer
- Re: MFC and c++ problems
- From: Daniel James
- MFC and c++ problems
- Prev by Date: Re: AddString to Combobox
- Next by Date: Re: MFC and c++ problems
- Previous by thread: Re: MFC and c++ problems
- Next by thread: Re: MFC and c++ problems
- Index(es):
Relevant Pages
|