From: rfg@netcom.com (Ronald F. Guilmette)
Subject: C++ already *has* nested functions SO WHAT'S THE BEEF?
Keywords: nested functions, dumb ideas
Organization: Netcom - Online Communication Services  (408 241-9760 guest) 
Date: Mon, 21 Dec 1992 08:09:52 GMT


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.