Topic: C++ already *has* nested functions SO WHAT'S THE BEEF?


Author: matt@physics2.berkeley.edu (Matt Austern)
Date: 8 Jan 93 10:04:28
Raw View
In article <9300811.3521@mulga.cs.mu.OZ.AU> fjh@munta.cs.mu.OZ.AU (Fergus James HENDERSON) writes:

> The classic
> sort of example where nested functions are useful is

  [Example of passing a function pointer to qsort deleted]

> Because the interface to qsort() is fixed, the alternative is to use a rather
> ugly hack using global variables (well, ok, you can make them file-scope
> static variables rather than real global variables).

Correct; that's just where nested function would be nice.

And note that encapsulating compare() in some class is not a solution:
qsort expects its argument to be a function pointer, not a member
function pointer.
--
Matthew Austern                   Just keep yelling until you attract a
(510) 644-2618                    crowd, then a constituency, a movement, a
austern@lbl.bitnet                faction, an army!  If you don't have any
matt@physics.berkeley.edu         solutions, become a part of the problem!




Author: harvey@opl.com (Harvey Reed)
Date: 8 Jan 93 17:44:24 GMT
Raw View
fjh@munta.cs.mu.OZ.AU (Fergus James HENDERSON) writes:

>harvey@opl.com (Harvey Reed) writes:

>>If programmers want nested functions, it sounds a lot like delegation which
>>C++ can handle quite well, as is. Just delegate to another object, or
>>your self. This leaves matters fairly explicit.

>Two points: firstly, I would not agree that C++ (without overloadable
>operator dot) handles delegation well; secondly, delegation can be used
>as a poor man's substitute for nested functions, but it does *not* have
>the same power/expressiveness that nested functions do. The classic
>sort of example where nested functions are useful is

> somefunc(lots of complex parameters) {
>     lots of complex local variables;

>     int compare(const void *ptr1, const void *ptr2) {
>  const object *x = (object *)ptr1;
>  const object *y = (object *)ptr2;
>  // compare x and y using some complex ordering which is
>  // dependant on the above parameters and local variables
>     }

>     ...
>     qsort(object_array, n, sizeof(object), compare);
>     ...
> }

>Because the interface to qsort() is fixed, the alternative is to use a rather
>ugly hack using global variables (well, ok, you can make them file-scope
>static variables rather than real global variables). Here delegation doesn't
>help.

Good example!

The premise for your arguement is that the service call "quicksort"
itself requires a function ("compare"), and that function you want to
define inside "somefunc". In addition, that compare function relies on
parameters scoped to the outer function.

Using fixed flat C interfaces is frustrating sometimes. Maybe when
true object oriented operating systems become commercial we will
be better off :-)

In the meantime, explore all the ways that you can package info
for the purposes of interfacing with "flat land". One possiblity
could be:

---------------------

class ObjCompare1   // one way to compare an "object"
{
   public:
      static  (lots of complex variables)

      static int compare (const void *, const void *);
};


class ObjCompare2   // another way to compare an "object"
{
   public:
      static  (lots of complex variables)

      static int compare (const void *, const void *);
};


void somefunc(lots of complex parameters)
{
   // now we choose which way to compare the objects,
   // set the (static) data, then use a static function
   // to emulate a flat function.

   ObjCompare1::xxx = ... (set all the static data)

   qsort(object_array, n, sizeof(object), ObjCompare1::compare);
}

----------------------

Advantages: Multiple ways to compare objects, packaged into classes.
We can incrementally add new policies, packaged into classes.

Disadvantages: we must make sure the compare choice matches, for
setting data and calling qsort.

Obviously my opinion is that the advantages outway the disadvantages.
Until operating systems improve, we will have these kind of "warts".
However, a wart packaged into a class is easier to deal with than
a wart flattened out IMO.



--
++harvey
========================================================================
internet:  harvey@opl.com  /  hreed@cs.ulowell.edu  /  h.reed@ieee.org
voice:     617-965-0220




Author: harvey@opl.com (Harvey Reed)
Date: 4 Jan 93 19:31:06 GMT
Raw View
rfg@netcom.com (Ronald F. Guilmette) writes:


> ...
>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
> ...

I share your consternation over "block programming" and your concern
of over "featurism" (your words).

If programmers want nested functions, it sounds a lot like delegation which
C++ can handle quite well, as is. Just delegate to another object, or
your self. This leaves matters fairly explicit.

I think a formal extension to add nested functions to C++ is not useful.
The example you gave however was interesting!

(wringing my hands also),

--
++harvey
========================================================================
internet:  harvey@opl.com  /  hreed@cs.ulowell.edu  /  h.reed@ieee.org
voice:     617-965-0220




Author: fjh@munta.cs.mu.OZ.AU (Fergus James HENDERSON)
Date: Fri, 8 Jan 1993 00:39:33 GMT
Raw View
harvey@opl.com (Harvey Reed) writes:

>If programmers want nested functions, it sounds a lot like delegation which
>C++ can handle quite well, as is. Just delegate to another object, or
>your self. This leaves matters fairly explicit.

Two points: firstly, I would not agree that C++ (without overloadable
operator dot) handles delegation well; secondly, delegation can be used
as a poor man's substitute for nested functions, but it does *not* have
the same power/expressiveness that nested functions do. The classic
sort of example where nested functions are useful is

 somefunc(lots of complex parameters) {
     lots of complex local variables;

     int compare(const void *ptr1, const void *ptr2) {
  const object *x = (object *)ptr1;
  const object *y = (object *)ptr2;
  // compare x and y using some complex ordering which is
  // dependant on the above parameters and local variables
     }

     ...
     qsort(object_array, n, sizeof(object), compare);
     ...
 }

Because the interface to qsort() is fixed, the alternative is to use a rather
ugly hack using global variables (well, ok, you can make them file-scope
static variables rather than real global variables). Here delegation doesn't
help.

--
Fergus Henderson             fjh@munta.cs.mu.OZ.AU
This .signature virus is a self-referential statement that is true - but
you will only be able to consistently believe it if you copy it to your own
.signature file!




Author: kriss@roll.ibp.fr (Christophe GROSJEAN)
Date: Thu, 24 Dec 1992 11:38:25 GMT
Raw View
The following code shows another way of simulating nested functions.
That's what I use right now, waiting for the removing of this restriction.

I've read that GNU support nested functions, the compiler I use right know
(G++ 2.3.2) does not seem to do it. Which version does it ?

Sample (should say simple) code follow-up, with context. Use of templates is not
relevant to the nested function problem.

The syntax could be *much* simplified using macros. But stantard macro preprocessors
are not powerful enough.

First, anybody thought of removing the comma-as-separator restriction
in the preprocessor in order you can choose the right one ?
I had much trouble with that using macros with multi-arguments templated classes. 'cause of that.

Second, overloading should be extended to macros in C++, seems homogeneous, and
usefull.

Third, in my case, what I would need is context vars, stacking vars in a first macro then
using them in a second one, kind of parenthesing macros.

--------- code follows -------------

#include <iostream.h>
#define TRUE 1
#define FALSE 0

template<class R, class T>
class Func1
{
 public:
 virtual R call(T o1) = 0;
};

void * ONULL = NULL;

template<class DATA>
class List
{
 public:
 List(void) {init();}
 List(DATA o1){ init(); add(o1);}
 List(DATA o1, DATA o2){init(); add(o1); add(o2);}
 List(DATA o1, DATA o2, DATA o3){
  init(); add(o1); add(o2); add(o3);}
 List(DATA o1, DATA o2, DATA o3, DATA o4){
  init(); add(o1); add(o2); add(o3); add(o4);}
 void add(DATA o1){
  Link * tmp = new Link;
  tmp->next = head;
  tmp->data = o1;
  head = tmp;
 }
 void addAll(List<DATA> & c1) {
  class p : public Func1<void,DATA> {
   public:
   p(List<DATA> & c) : c(c) {}
   void call(DATA i) {c.add(i);}
   List<DATA> & c;
  } p(*this);
  c1.all(p);
 }
 void all(Func1<void,DATA> & p){
  Link * tmp;
  for(tmp=head;tmp != NULL;tmp=tmp->next)
   p.call(tmp->data);
 }
 DATA & any(Func1<int,DATA> & t){
  Link * tmp;
  for(tmp=head;tmp != NULL;tmp=tmp->next)
   if (t.call(tmp->data))
    return tmp->data;
  error = 1;
  return (DATA &)ONULL;
 }
 void unlink_any(Func1<int, DATA> & t){
  if (head == NULL){error = 1;return;}
  else{
   Link * tmp = head;
   if (t.call(tmp->data)){
    head = tmp->next;
    delete tmp;
    return;
   }
   else {
    Link * tmp2;
    for (tmp2 = tmp->next; tmp2 != NULL;tmp2=(tmp=tmp2)->next)
     if (t.call(tmp2->data)){
      tmp->next = tmp2->next;
      delete tmp2;
      return;
     }
   }
  }
  error = 1;
  return;
 }
 void unlink_all(Func1<int, DATA> & t){
  Link * tmp;
  Link * tmp2;
  tmp = head;
  head = NULL;
  while(tmp != NULL){
   tmp2 = tmp;
   tmp = tmp->next;
   if (t.call(tmp2->data))
   {
    delete tmp2;
   }
  {
   tmp2->next = head;
   head = tmp2;
  }
  }
  reverse();
 }

 int includes(Func1<int, DATA> & t){
  Link * tmp;
  for(tmp=head;tmp != NULL;tmp=tmp->next)
   if (t.call(tmp->data))
    return TRUE;
  return FALSE;
 }

 int occurencesOf(Func1<int, DATA> & t)
 {
  int nb = 0;
  for(Link * tmp=head;tmp != NULL;tmp=tmp->next)
   if( t.call(tmp->data)) nb++;
  return nb;
 }

 private:
 void init(void) { head = (Link *)NULL;}
 void reverse(){
  Link * tmp;
  Link * tmp2;
  tmp = head;
  head = NULL;
  while(tmp != NULL){
   tmp2 = tmp->next;
   tmp->next = head;
   head = tmp;
   tmp = tmp2;
  }
 }
 struct Link
 {
  Link * next;
  DATA data;
 } * head;
 int error;
 };

int main()
{
 class pint : public Func1<void,int>
 {
  public:
  pint(void) {}
  void call(int d) { cout << d << " "; }
 } pint;
 class pchar : public Func1<void,char>
 {
  public:
  pchar(void) {}
  void call(char d) { cout << d << " "; }
 } pchar;


 class t1 : public Func1<int,int>
 {
  public:
  t1(void) {}
  int call(int d) {return (d>=200);}
 } t1;

 int toto = 120;

// Function reading context variable toto (value a this point)
 class t2 : public Func1<int,int>
 {
  public:
  t2(int toto) : toto(toto) {}
  int call(int d) {return (d > toto);}
  int toto;
 } t2(toto);

 // Function using context variable toto (whatever value)
 class t3 : public Func1<int,int>
 {
  public:
  t3(int & toto) : toto(toto) {}
  int call(int d) {return (d <= toto);}
  int & toto;
 } t3(toto);

 // Function modifying context variable max
 int max;
 class getmax : public Func1<void, int>
 {
  public:
  getmax(int & max) : max(max) {}
  void call(int d) { max = (max > d)?max:d; }
  int & max;
 } getmax(max);

 List<char> L3('A','B','C');
 List<int> L1(100,120,130), L2(200);
 L1.add(110); L2.add(210);
 L1.addAll(L2); L1.add(140); L1.add(150); L1.add(160);
 cout << "List L1 : "; L1.all(pint); cout << "\n";
 cout << "List L2 : "; L2.all(pint); cout << "\n";
 cout << "List L3 : "; L3.all(pchar); cout << "\n";
 toto = 140;
 cout << "First elt of L1 veryfying elt >= 200 : " << L1.any(t1) << "\n";
 cout << "First elt of L1 veryfying elt > toto (120) : " << L1.any(t2) << "\n";
 cout << "First elt of L1 veryfying elt <= toto (now=140) : " << L1.any(t3) << "\n";
 L1.all(getmax);
 cout << "Max elt of L1 : " << max << "\n";

 return 0;
}
-------- end of code --------




Author: rfg@netcom.com (Ronald F. Guilmette)
Date: Mon, 21 Dec 1992 08:09:52 GMT
Raw View
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.




Author: kriss@rock.ibp.fr (Christophe GROSJEAN)
Date: Mon, 21 Dec 1992 17:21:01 GMT
Raw View
In article <1992Dec21.080952.15309@netcom.com> rfg@netcom.com (Ronald F. Guilmette) writes:

>   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.

Indeed C++ provide several ways of simulating nested functions,
the tricks to make it works has basically the 3 problems you
pointed out.


>    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.


But, there is more to say about them :

>   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).

Well, i merely agree with you on this, the effect would be to make a
nested function declaration simpler.

>   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.

Modula-2, as I saw it (I didn't tried *all* compilers) has no reason to envy C,
and particularly not the efficiency. However, scope rules are *perfectly*
consistent. So much for the idea of C success strongly related to its efficiency.
I use (and like) C and C++, and that's not a point. But it still seems strange
to me that you speak about *explicit indirection* when C++ obviously go
the other way (first step : passing vars by reference with &, second step :
virtual functions). I got the feeling that C++ is often confusing about data storage
and concepts.

>   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).

About the first and second problem, it seems easy enough to change,
but I think it could make the difference beetween a used feature and
an unused one (as I see it now !).

I don't think the third restriction you pointed out is a minor one.
For one reason : local static vars already have another meaning !
Those vars are initialized only *once* when you first execute the
containing function ! If you modify them the new value will stuck.

Doesn't matter in most cases. But it's a powerfull source of bugs
whenever you use recursive functions. What I say is that the problem
with static functions is not that they are statically allocated :
no problem if I have space enough, but that they are initialized only
once and won't be preserved in recursive call.

I can do what I want copying values beetween automatic and static vars,
but a *user* of a library expecting a function as an argument should'nt
bother about that, not saying that king of things is very error prone.

I would be perfectly happy with a *local* keyword meaning the value
is stored in a static var, but is saved before calls and initialised
every time my function is called : does it seem a difficult thing to do ?

I need it, I'm no maniac !

Best, regards





Author: chased@rbbb.Eng.Sun.COM (David Chase)
Date: 22 Dec 1992 00:49:22 GMT
Raw View
rfg@netcom.com (Ronald F. Guilmette) writes:
>So what is it that people are complaining about?

> o [fake] nested functions ... cannot access `auto' and
>  `register' *objects* declared within a containing local
>  scope.

>I believe that it is this last item that concerns people the most.

This is true.  The rest is all largely syntatic fluff.  Fluff is nice,
but that is not what people are asking for here.

> ... when I hear about
>all of these complicated schemes for supporting dynamic "up-level addressing"

It was easy.  We did it for Modula-3, two different ways.

>and also such things as "special" kinds of function pointers for holding
>pointers to nested function

This is not necessary at all.  See previous post for details on how to
avoid special function pointer representations for nested functions.

>(and a special new syntax for declaring such things)

Not necessary.

>Haven't the implementors already got enough to do??

Yes, we do.  On the other hand, if you look at in terms of costs and
benefits, especially in comparision to the rest of the language, this
is a relative win.  If you look at it in terms of percentage of total
work, this is also not a big deal.

>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?

You're not getting the picture here.  If I declare (to choose a common
and perhaps over-simple example) a bag-object with an iterator
interface that takes a function-to-be-applied-to-each as a parameter,
then it just takes a function, period.  It doesn't matter if it needs
0, 1, 2, or many pointers to auto variables passed to it.  This
simplifies the interface, and has the advantage of not adding clutter
(e.g., explicit state variables to be passed in and out of the
function) where it is not needed.

>I mean c'mon folks... This endless "featurism" has got to stop somewhere!

I agree, but I think we mean different things.  C++ has many features
that I still haven't figured out how to use, or even if they are in
fact useful.  It has many of other features that I put in the "plenty
of rope" category (be careful not to wrap it round your neck).  Nested
functions, I know how to use, and I know how they can be used to
simplify interfaces, and they are not especially "dangerous".

David Chase
Sun




Author: maxtal@extro.ucc.su.OZ.AU (John MAX Skaller)
Date: Wed, 23 Dec 1992 16:57:19 GMT
Raw View
In article <1992Dec21.080952.15309@netcom.com> rfg@netcom.com (Ronald F. Guilmette) writes:
>
>The example below clearly shows that this is an entirely viable way to
>achieve nested functions with C++ (as now defined).

>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.

 The principal purpose of nested functions is to
access local (auto) data in the containing scope, and thats one of the
exceptions you list.

 Futhermore, I want to nest functions inside MEMBERS,
so these functions get access to the 'this' pointer and the
objects other members (with the same privileges as the containing
member), which implies the function cant be static.

>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.

 I'd live with that.
>
> 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.

 Irrelevant, dont care, as long as I can still take its address.

>
> 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.

 This the whole purpose of nested functions. It is not a minor
and mildly annoying exception.

>
>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.

 Indeed this is the problem that concerns me most with your
technique. No dramatic language change is required, its basically
the removal of a restriction resulting in a *simplification*
of the language syntactically.

 Its been done by GNU.
>
>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.

 Why are you scared of one of the few things in computer
science that is totally and comletely understood, and that is
routinely taught to second year CS students? It is so established
that both the 386 and 68000 have hardware to support this
mechanism.

>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?

 If you keep adding new variables, and have several
pretend nested functions with parameters,  you have in effect
to duplicate the entire set for each function. Apart from
the horrid maintenance problem, its also extremely
inefficient.
>
>So why do people insist on attacking (and defeating) the third minor
>problem listed above (regardless of costs)?

 Because accessing auto variables is exactly the reason
why we want nested functions. I hadn't even *thought* of
accessing anything else, never occured to me that I could
already access a local class name, because I dont care about
that if I cant access the local variables.

>
>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.

 In my case, yes. I liked that ability very much.
Every block structured language should support it. It can
be overused, like anything else.

>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.

 I dont agree that the language should force the user to
type letters in proportion to the execution time of the program
to remind them how slow its going to be.

 And you are completely wrong about the expense of accessing
variables, on the 386 for example the outer scopes at ANY level
can be accessed with exactly one extra indirection.
The technique is called a 'display'. The algorithm for maintaining
the display is trivial and implemented on the 386 and by
hardware. It can be done almost as easily with software.
What is horribly inefficient is passing lots of parameters
needlessly.

>
>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).

 Why is eliminating the restriction against nested functions
a hack? You can nest everything else, why not functions?
>
>I mean c'mon folks... This endless "featurism" has got to stop somewhere!

 Agreed. Lets get rid of the feature that specifically
prohibits nested functions. We already have nested blocks,
and a nested function is just a named nested block
with parameters.

 A language that supports stuff as complicated as pointers
to members, multiple inheriance with virtual and nonvirtual bases,
virtual functions, nested classes etc etc should have no
problems supporting something as simple as nested functions.
Compared to the problems of the type system, nested functions
just aren't that hard.

It has been done by GNU,
it can be done easily,
it can be highly efficient,
it requires almost no new syntax,
it is quite useful,
it simplifies the language by removing a restriction,
it is within the spirit of C to provide facilities that
  match the underlying hardware,
  and most CISC machines have this hardware,
it can be implemented without hardware support on other machines,
it breaks no existing code,
it doesnt cost you anything if you dont use it.

If this were a MAJOR extension to the language (like exceptions)
I would agree it wouldn't be useful enough to add, but that
isnt the case.

Small conveniences are just the things programmers are usually
most grateful for. (Anyone for switch .. case 'a','d'..'f':  :=)
Remember the language is for programmers to use, the vendors
and committee's convenience is not so important as the standard
for the world most popular language, which we're going to get stuck
with for the next 20 years :-).

--
;----------------------------------------------------------------------
        JOHN (MAX) SKALLER,         maxtal@extro.ucc.su.oz.au
 Maxtal Pty Ltd, 6 MacKay St ASHFIELD, NSW 2131, AUSTRALIA
;--------------- SCIENTIFIC AND ENGINEERING SOFTWARE ------------------