home *** CD-ROM | disk | FTP | other *** search
- Newsgroups: comp.std.c++
- Path: sparky!uunet!spool.mu.edu!umn.edu!csus.edu!netcom.com!rfg
- From: rfg@netcom.com (Ronald F. Guilmette)
- Subject: C++ already *has* nested functions SO WHAT'S THE BEEF?
- Message-ID: <1992Dec21.080952.15309@netcom.com>
- Keywords: nested functions, dumb ideas
- Organization: Netcom - Online Communication Services (408 241-9760 guest)
- Date: Mon, 21 Dec 1992 08:09:52 GMT
- Lines: 237
-
-
- I see a lot of nonsense go buy in this newsgroup (and the in the others
- I read) and I rarely comment on it all simply because I do not have the
- time, but some of the hand-wringing I've seen here recently regarding
- the alledged lack of a "nested function" feature in C++ causes me to
- to feel a need to point out the basic fallacy on which come of these
- comments have been based.
-
- You see, C++ already provides nested functions. I believe that even the
- people who are bemoaning the lack of nested function in C++ are probably
- aware of this fact (or would be if they thought about it for a moment)
- and that in fact what we have here is a failure to communicate clearly.
-
- As illustrated by the lengthy example (of legal C++ code) given at the
- end of this message, all that is necessary in order to nest one function
- within another is to declared the inner function as a static member of
- some class type (invented expressly for this purpose) which is itself
- nested within the desired containing function.
-
- The example below clearly shows that this is an entirely viable way to
- achieve nested functions with C++ (as now defined). In fact the example
- goes to great lengths to demonstrate that references to named entities
- declared locally within the function containing the "nested" function
- are possible (and legal) with a few minor exceptions, as long as the
- references in question obey the normal name scoping rules.
-
- So what is it that people are complaining about?
-
- Well, obviously there are several small problems with using this sort of
- technique for defining nested functions, but let's be clear about what
- we are talking about. We *can* nest functions, so that isn't really
- the issue.
-
- The problems with this technique are basically as follows:
-
- o We have to "invent" an otherwise useless and unnecessary
- containing struct, union, or class type in order to be
- able to nest functions.
-
- o Due to the (silly?) prohibition against defining a function
- member of a block-local struct, union, or class type outside
- of (and after) its "parent" type, and due to the (silly?)
- rule that says that all functions defined *within* a class
- are implicitly `inline', our nested functions are always
- necessarily `inline' (when using this technique for nesting)
- even if we do not want them to be.
-
- o Last but not least, although nested functions can legally
- reference *most* names of *most* kinds of things declared
- in a containing scope (including one local to a containing
- function) there is one small and mildly annoying exception
- to this general rule... i.e. we cannot access `auto' and
- `register' *objects* declared within a containing local
- scope. Note that we *can* however reference the tags of
- enum, struct, union, and class, types, names declared in
- typedef statements, names of enumerators, function names,
- and names of static and extern objects which are declared
- in the containing (block) scope.
-
- I believe that it is this last item that concerns people the most. In
- fact, I get the general impression that some people view these minor
- exceptions (to the general scoping rules) as being horrendous problems
- which are so utterly insurmountable that dramatic language changes are
- required in order to eliminate these exceptions.
-
- It would not concern me if the kinds of changes being suggested were
- (in general) trivial for implementors to implement, but when I hear about
- all of these complicated schemes for supporting dynamic "up-level addressing"
- and also such things as "special" kinds of function pointers for holding
- pointers to nested function (and a special new syntax for declaring such
- things) I shrink back in horror. Haven't the implementors already got
- enough to do?? Can't people figure out how to simply pass any needed
- `auto' variables which are local to the containing function as additional
- actual argument to the nested (called) function on those very rare occasions
- when this is actually necessary?? I mean what the devil is the problem here?
-
- For my part, I would be satisfied if the ANSI/ISO C++ committee(s) would
- add "nested functions" to C++ in such a way that the first two problems
- on my list above could be eliminated. But note that this could be done
- with only a single minor syntax change (to allow function *definitions*
- as well as function *declarations* within a local block) and one minor
- semantic change, i.e. disallowing the mention of any name of a locally
- declared function (except those declared `extern') in any context other
- than a call to the given function. These two minor changes would solve
- the problems I'm concernd with, and they would be relatively easy and
- painless for implementors to implement. Also, just as importantly, they
- would not add materially to the already horrendous complexity of the
- language (which is already a terrible burden for all those who would try
- to learn this language).
-
- So why do people insist on attacking (and defeating) the third minor
- problem listed above (regardless of costs)?
-
- Well, it appears that some people have this mental model of "nested
- functions" left over from the days when they programmed in Pascal, where
- *anything* in a containing scope could be accessed directly. Sadly, these
- folks don't seem to "get" the fundamental idea which made C into the big
- success that it is today... i.e. that it is *good* when potentially
- expensive run-time actions (e.g. indirection through some number of
- "static link" pointers) are made *explicit* to the programmer so that he
- or she will be more aware of the potential effects these things may have
- upon run-time performance.
-
- Obviously, I have a strong disagreement with the folks who would (for the
- sake of eliminating a very minor restriction which only makes a difference
- in rare cases anyway) "hack" the language mercilessly (and thereby cause
- yet another headache for implementors).
-
- I mean c'mon folks... This endless "featurism" has got to stop somewhere!
-
- // Ron ("Loose Cannon") Guilmette uucp: ...uunet!lupine!segfault!rfg
- //
- // "On the one hand I knew that programs could have a compelling
- // and deep logical beauty, on the other hand I was forced to
- // admit that most programs are presented in a way fit for
- // mechanical execution, but even if of any beauty at all,
- // totally unfit for human appreciation."
- // -- Edsger W. Dijkstra
-
-
- ===========================================================================
- Here's the example code showning how to "nest" functions. This is perfectly
- valid C++ code. Please try it on your favorite compiler and let me (and
- your vendor) know if the implementation you have doesn't compile this
- cleanly. (Many don't, but then I've found that C++ implementations are,
- almost without exception, horrendously buggy.)
-
- /////////////////////////////////////////////////////////////////////////
- void outer (int outer_arg)
- {
- // Declare some types whose scope is just this block.
-
- enum E { red, green, blue }; // The enumerator names are also local!
- class C { public: int Cmember; };
- struct S { int Smember; void S_func_member () { } };
- union U { int Umember; };
- typedef int T;
-
- // Declare some (static-storage) objects of the above types.
-
- static enum E outer_E_var;
- static class C outer_C_var;
- static struct S outer_S_var;
- static union U outer_U_var;
- static T outer_T_var;
-
- // Declare some plain objects whose scope is just this block.
-
- auto int auto_object;
- static int static_object;
- register int register_object;
- extern int extern_object;
- int object; // Really is an `auto'.
-
- // Declare some anon-union objects whose scope is just this block.
-
- auto union { int auto_anon_Umember; };
- static union { int static_anon_Umember; };
- register union { int register_anon_Umember; };
- //extern union { int extern_anon_Umember; }; // dubious (see 7.1.1)
- union { int anon_Umember; }; // Really is an `auto'.
-
- // Declare some functions whose scope is just this block.
-
- extern int extern_func ();
- int func (); // Really is an `extern'
-
- // Now declare a nested function. **************************************
-
- struct INNER {
- static void inner ()
- {
- // Use the types declared in the containing scope (to declare objects).
-
- enum E inner_E_var; // OK!
- class C inner_C_var; // OK!
- struct S inner_S_var; // OK!
- union U inner_U_var; // OK!
- T inner_T_var; // OK!
-
- // Use the enumerators declared in the containing scope.
-
- inner_E_var = green; // OK!
-
- // Refer to members of types declared within the containing scope.
-
- inner_C_var.Cmember = 99; // OK!
- inner_S_var.Smember = 99; // OK!
- inner_S_var.S_func_member (); // OK!
- inner_U_var.Umember = 99; // OK!
-
- outer_C_var.Cmember = 99; // OK!
- outer_S_var.Smember = 99; // OK!
- outer_S_var.S_func_member (); // OK!
- outer_U_var.Umember = 99; // OK!
-
- // Refer to some plain objects declared in the containing scope.
-
- //outer_arg = 99; // NO GOOD!
-
- //auto_object = 99; // NO GOOD!
- static_object = 99; // OK! NO PROBLEM!
- //register_object = 99; // NO GOOD!
- extern_object = 99; // OK!
- //object = 99; // NO GOOD!
-
- // Refer to some anon-union objects declared in the containing scope.
-
- //auto_anon_Umember = 99; // NO GOOD!
- static_anon_Umember = 99; // OK! NO PROBLEM!
- //register_anon_Umember = 99; // NO GOOD!
- //extern_anon_umember = 99; // dubious (see above)
- //anon_Umember = 99; // NO GOOD!
-
- // Refer to some functions declared in the containing scope.
-
- extern_func (); // OK!
- func (); // OK!
-
- }
- };
-
- // Establish a shorthand alias for the nested function.
-
- #define inner INNER::inner
-
- // Call the local nested function.
-
- inner ();
- }
- --
-
- // Ron ("Loose Cannon") Guilmette
- // uucp: ...uunet!lupine!segfault!rfg
- // New new motto: Quality control is a state of mind.
- // misc.forsale.computers ad, circa 2007:
- // Used Cray wrist watch for sale; 25 bucks or best offer.
-