Re: MFC and c++ problems
Common sense is a good reason. You can't "restrict" it in the sense the compiler can
enforce it. But you look at a problem and say "this is wrong", and go off and look for a
solution which decouples dependencies.
The whole point of classes and OO programming is to isolate dependencies. Then someone
discovers that with forward declarations and circular dependencies, you can get the
compiler to generate code that is architecturally nonsensical. The number of times that
circular dependencies make sense is fairly small (although definitely nonzero).
Inheritance, encapsulation, module boundaries, and such are artifices that impose
discipline on what is ultimately a "flat" representation (the CPU doesn't care about any
of this. It just pushes bits around).
So a design in which a control has any syntactic knowledge of any container of itself
makes little sense. This is about as ridiculous as that asinine feature of MFC that keeps
putting the project's .h file (the one that defines the derived CWinApp class) into every
generated subclass. In a well-build system, you might have as many as two .cpp files that
include the project's .h file, and it has no reason to exist anywhere else at all. It
actively works against good design. It is also about as absurd as dropping everything as
"global variables" in the CWinApp class. I learned years ago that as soon as you cast the
AfxGetApp() pointer, or import the CWinApp-derived variable, into a module that you have
made a serious design error.
So a control subclass becomes a completely self-contained class in my world. If what it
needs to know is not defined by its header file, which contains no extern declarations,
then it cannot know it. Period. It cannot look into any other class that might contain
it, including CWinApp, CView, or CDialog classes that might contain it. It knows nothing
that it cannot access independent of its container. Note that its container is free to
access methods in it, because the container knows it contains an instance of the object.
But the object can only use mechanisms visible to itself and itself alone. It contains no
static variables, no global variables, and no header files of anything that might contain
it. GetParent()->SendMessage is the interface of choice, and this can usually be
concealed with some reasonable programming techniques.
What far too many people think is "if I can get it to compile, it must be acceptable
style". I spent a nontrivial part of my life teaching people that this attitude didn't
even work in assembly code, COBOL, and FORTRAN in the 1950s, and there is no reason it
should be applied to programming today. The two serious indicators of problems in our
educational system are (a) people who ask how to call methods or access variables of the
container class and (b) people explain how to do it with forward references and extern
declarations and similar techniques. No programmer should ask the question in the first
place. Any answer that is expressed in pure syntactic terms (excluding interfaces,
assuming they are designed correctly to be completely reusable no matter what the
container is) is wrong. By "syntactic terms" I mean, approximately, anything that
involves doing a #include of the container class, anything that involves global variables,
anything that involves global functions, and anything that involves extern declarations,
as means of allowing the contained control to talk to its container. Note that ActiveX
makes it impossible to express any of these concepts syntactically, since the binary
control may have been compiled on a machine unknown to the ultimate user of the control.
It is unfortunate that C++ represents some of the best late-1960s through mid-1970s
thought on abstraction and interface design, instead of, say, 1990s thought (remember, C++
is based on Simula-67, a language which was at least a decade ahead of its time; when
Stroupstrup invented C++, he was largely emulating Simula-67 ideas, which he found he
could not live without. Many of the ideas came from languages that followed Simula-67.
Most of the key ideas had been well-established by 1975 in a variety of languages. While
some of the ideas about using C as the basis were based on efficiency considerations,
mostly these came about because compiler technology of the era was really rather pitiful
compared to modern compiler technology. I've used languages with powerful compilers,
where I characterized it as "you can write seven levels of abstraction to generate half an
instruction", which I did on a regular basis in the early 1980s. So the arguments about
efficiency or closeness to the hardware don't really hold up given the maturity of
language design and compiler design that evolved in the 1990s; consider, for example, the
Java Hotspot technology (although some of the key ideas of the hotspot approach were
already being the subject of PhD dissertations as early as 1970--I know two of them came
from CMU, and at one of them became a research director at Sun, and was responsible for
funding the Java project). So it would be possible today to design languages far more
restrictive about interfaces than we have seen in the past, and yet could perform as well
as the best optimizing C compiler. C# and the whole .NET CLR are evolutions consistent
with these ideas.
ActiveX, in spite of my opposition to it as a Web methodology, is actually (at least in
the abstract) the best example of pure modular interfaces that the world has ever seen in
a commercial product environment. Unfortunately, it is also surrounded by a massively
complex infrastructure that seems to have very little coherent explanation (such as the
several convoluted Registry keys indexed by GUIDs). Aesthetically, however, it is one of
the cleanest interface methodologies around, because it simply makes it impossible to
either ask or answer the questions about accessing the environment using syntactic means.
And unlike the SendMessage solution, the interfaces it provides can be compile-time
type-checked.
Back in the mid-1980s, when I was at the Software Engineering Institute, we wrote
requirements documents which would have been satisfied by technologies like ActiveX; the
problem was the proprietary operating system Unix had too many competing stakeholders who
wanted to lock customers into a particular vendor's hardware base, so there was clearly
going to be no cooperation. Microsoft created a market for commodity hardware, thus
wiping hardware platform competition out; Intel created commodity processors, rendering
most arguments about hardware architecture irrelevant (when you can buy a complete system,
2GHz hyperthreaded with a half-gig of RAM, including keyboard and display, for $600 from a
major vendor, with support [such as Dell], compared to $20,000 in 1974 dollars for
something that made an 8088 look like progress [e.g., a 32K PDP-11/20], nobody really
cares about the "hardware" at all. The RISC/CISC debate is largely dead. Microsoft
created a market for commodity software, in particular, commodity reusable software
components, so ActiveX, in spite of its gratuitous complexity, became a standard everyone
had to meet. Even if you want to avoid its horrible details, its philosophical base is
sound and should be used as a model for component programming. This means that forward
references to support circular dependencies, circular #include dependencies, and global
variables are simply dead as methodologies and can be ignored.
By the way, does anybody remember when the operating system was free but the hardware cost
$1.2M? I signed off on a mainframe at that price point. Our 4MB of RAM needed an
extra-heavy-duty forklift to remove from the truck. I'd rather pay a few hundred for the
OS; our mainframe, with about the power of the 80286, but supporting sixty timeshared
users, consumed more dollars of electricity per month than it costs to buy a high-end
(dual processor, hyperthreaded, 3.2GHz 2GB server) today. Arguments about program size,
on the whole, program performance, and issues of program-line efficiency largely no longer
make sense. This means issues about making the interfaces between modules, particularly
GUI-based components, "efficient" are empty and pointless discussions. Making the
interfaces clean, elegant, and maintainable saves the REAL costs, which are development,
debugging, and maintenance costs. So SendMessage to a parent is more than adequate as a
methodology and it is clean and simple compared to the syntactic FORTRAN-based solutions
so many seek.
I have found interface classes hard to explain and hard to use in practice. Of course, I
may be just having a blind spot here, but my idea of an "interface" is that there is no
concept of protected/private variables or methods; an interface is all public, and there
is no implementation. Another set of declarations couples the abstract interface
specification to a particular implementation. C++ doesn't seem to do this nearly as well
as other languages (Java has many of the same problems, and its interface mechanism more
carefully designed than those of C++). Languages like Mesa and a few others, including
some I've worked on, got this right in the 1970s, and nothing as good has appeared since.
joe
On 1 Feb 2006 13:15:46 -0800, "Ajay Kalra" <ajaykalra@xxxxxxxxx> wrote:
>> why it would ever make sense to have the subclass B ever call any method of any class,
>
>Why is that though? I dont see how you can restrict what class B can or
>cannot to other classes(ignore the fact that this is a CDialog-child
>control relationship). I presume one of the reasons for constructor to
>exist is to allow that. Perhaps you meant something else.
>
>-----
>Ajay Kalra
>ajaykalra@xxxxxxxxx
Joseph M. Newcomer [MVP]
email: newcomer@xxxxxxxxxxxx
Web: http://www.flounder.com
MVP Tips: http://www.flounder.com/mvp_tips.htm
.
Relevant Pages
- Re: Win32 API and gfortran
... write out a module containing only interface blocks and compile the ... that the /iface switches and ATTRIBUTES directives change the compiler ... arguments are passed in the normal Fortran way ... (comp.lang.fortran) - Re: why still use C?
... OOP in general tends to be slower. ... As if the compiler didn't know... ... > their associated method functions from a design perspective. ... as a language design I feel it has failed. ... (comp.lang.c) - Re: Using early-bound interface on a late-bound object
... > the compiler determines which interfaces to use, ... > supports the declared interface. ... >> help if someone can point me to some authoritative document or reference ... within customer shops when they mirrored this 'division' to keep their ... (microsoft.public.vb.general.discussion) - Re: Some OO design principles WAS Re: Differences in data description in programming languages
... Late binding means it is resolved at run time. ... Conformance is a quality that applies to interfaces. ... * An interface can also be a collection of method prototypes, ... the Compiler provides conformance checking. ... (comp.lang.cobol) - Re: object system...
... in a statically compiled app, all of the type information is known at ... compiler starts integrating itself with the outside world, ... produces the result, the type or interface. ... optimizations the ... (comp.object) |
|