views:

167

answers:

4

was the idea of nested functions considered to be useless during the time of developing older c++ standard, because its usage is basically covered by another concept like object-oriented programming; or it wasn't implemented just as a matter of simplification?

+1  A: 

You don't really need them - you can simply use static functions to accomplish the same thing. Even when programming in languages that do support nested functions, like Pascal, I avoid them because (to me at least) they make the code more complex and less readable.

anon
"You don't really need them" They are nice to have when you want to extract some loop into subroutine without introducing additional classes/structs outside of function. In pascal nested functions "see" variables declared in parent funciton, which may be useful. "Unfortunately", in C++ you can declare variable anywhere, it is a bit hard to imagine sane syntax for declaring nested function. Which is why such problems are normally solved by introducing new classes into namespace outside of function. Not a nicest approach, but a reasonable one, when you take additional C++ benefits into account.
SigTerm
@Sig Your comment should probably be an answer.
anon
A: 

You can either use a nested class having the method you need. In C++ the idea is to group methods together with data to get classes and not having loose functions around.

schoetbi
+3  A: 

Nested functions - to be useful - need the stack frame of the containing function as context. Look at this:

class Foo()
{
   void Tripulate()
   {
       int i=0; 

       void Dip()
       {
           // ...
       }

       int x = 12;
       for(i=1; i<=3; ++i)
       {
          int z= 33;
          Dip();
          // ...
       }
   }
}

Which values should Dip() get access to?

None? you have just duplicated the functionality of (anonymous) namespaces, more or less.
Only to i, because it's the only one defined before the function?
Only to i and x, because they are in the sam scope as Dip()? Does the compiler have to make sure the constructor of x did already run, or is that your job?
What about z?

If Dip gets any access to both the local values of tripulate and the stack frame, so the internal prototype would be

   void Dip(Foo * this, __auto_struct_Dip * stackContext)
   {
       // ...
   }

You have basically replicated the functionality of structs / classes and member functions, but on two incompatible and non-exchangable paths. That's a lot of complexity for a questionable gain.

I've wished for local functions a few times, simply because this would better indicate the scope where this is needed. But with all the questions... there are more useful things to throw more complexity onto C++.

peterchen
If nested functions weren't allowed to call themselves directly or indirectly (basically don't allow calling equal or higher scoped functions), then no context would be needed and locals of the enclosing functions would be at statically known stack positions. I've sometimes found myself wanting this form of nested function for some algorithms which have a large number of variables. The only alternatives are to abuse macros or create structures or objects to hold the needed variables, but this has a cost in runtime performance and clarity of code.
ergosys
@ergosys: Actually, no. The idea that local variables have a "position" on the stack certainly ended with the new Static Single Assignment compilers. Variables may have different stack and/or register locations before and after assignment.
MSalters
@MSalters, s/stack position/register or stack location/ but I agree that optimizations could be limited by their usage. OTOH, inlining would be likely and ameliorate that somewhat.
ergosys
what's that "int value" parameter in Dip declaration after all ?
Pooria
@Pooria, that was from an old prototype I forgot to remove in that location. fixed. The last was jsut to demo that you get kind of "two this pointers".
peterchen
+2  A: 

The idea was raised (a number of times) during standardization. Steve Clamage wrote a post in comp.std.c++ in 1996 responding to a question about adding them to C++. He summarized his points as:

In the end, it seems to me that nested functions do not solve any programming problem for which C++ does not already have a solution at least as good.

A proposal to add nested functions to C++ should show an important class of programming problems solved by nested functions which are inconvenient to solve otherwise. (Perhaps there are such problems, and I simply haven't seen them.)

A later (1998) post by Andrew Koenig indirectly states that the committee did discuss it, but nothing seems to have materialized from it.

The obvious way to support nested functions requires hardware support, and still adds a bit of overhead. As pointed out in a post by Fergus Henderson, it's also possible to support them via "trampoline" code, but this method adds some compiler complexity (even if they're never used).

As an aside: all three are members of the C++ standard committee (or at least were at the time). If memory serves, at that time Steve was either convener of the ISO committee or chairperson of the US committee.

Jerry Coffin
Um, what "hardware support"? Presumably Pascal manages to implement them without such support.
anon
@Neil: The obvious way requires to assemble a code pad in a region of memory that is executable and writable. This is not possible with Harward-architecture machines (often microcontroller, DSP's etc.)
Luther Blissett
Pascal *can* do so, but most processors from the timeframe when Pascal was in wide use include the hardware support (e.g., the x86 provides Enter and Leave instructions; the 68K has rough equivalents, though I don't remember their names). Basically it comes down to a way of (semi-)automatically creating a chain of pointers to outer stack frames to give access to local variables in the functions inside of which a particular function is nested. To be more accurate, I should have said that without hardware support, you get quite a bit of overhead, even though it is still possible.
Jerry Coffin
Well, the source code for various Pascal compilers I've seen requires nothing particularly exotic. Remember, Pascal was designed as a language that was easy to write compilers for.
anon
@Neil: what did they target? As noted above, if it was x86 (for an obvious example) they'd just use Enter/Leave to create/destroy stack frames. Unless you looked for it specifically, you probably wouldn't notice a thing.
Jerry Coffin
The code pad (or trampoline) method is perfectly working for all Von-Neumann machines - provided that the OS allows to allocate executable memory (GCC makes the stack executeable for nested functions in C). However, IIRC this was identified as a problem, because it means in order to support nested functions on Harward-machines (or security restrained OS which do *not allow* to write executable memory) a different scheme was required: The fat pointer, which contains a function pointer and the nested invocation frame. But this would break the standard layout of function pointer.
Luther Blissett
JavaScript supports it and GCC supports it in C with a language extension, along with Fortran-90
0A0D
Pascal is available on all architectures that I'm aware of - you guys obviously know of architectures where this isn't so. Perhaps you could list them.
anon
But in Pascal, the situation is different: You can not take the function pointer of a nested Pascal function (IIRC)!. This means that you can invoke the function only from the scope where the function is defined, you can't pass it around. This makes the implementation suddenly very simple: A nested function *always knows* how many stack frames a up-level variable is away. This is reflected in the ENTER opcode which allows to specify how "deep" (0-31) a function is nested.
Luther Blissett
@Neil: Perhaps you need to start with working on reading comprehension. I said only that the **obvious** way of supporting them requires hardware support (later amended to point out "without adding a fair amount of overhead). There are less obvious ways that don't even benefit from hardware support. That said, are you honestly claiming that somebody has written a Pascal compiler for the ENIAC, or are you claiming you never heard of the ENIAC? (or Whirlwind, etc. -- lots of computers were obsolete before Pascal was invented).
Jerry Coffin
@Jerry Forget it.
anon
@Luther C++ could easily bar pointers to nested functions - similar restrictions apply to local classes.
anon
@Neil: Yes, but this would be a very unpopular language feature, hated by users ("Why can't I pass that stupid nested functions to std::sort()") and compiler writers (the nested functions need their upvalues at the same place in the stack on each invocation, this is hard to optimize if the nested function is not inlined).
Luther Blissett
@Luther I don't see it would be any more hated than the restrictions on local classes. But please note, I am not in favour of nested functions, I simply don't see any insurmountable technical reasons against them. But enough is enough, I think.
anon
@Jerry: I believe the first Pascal compiler was written for the CDC 6600 family. That computer did have machine-code support for non-recursive functions only, so everything had to be done explicitly. Back then, recursive functions were a new hot idea, not existing in real languages like FORTRAN or COBOL, confined to ALGOL and Lisp weenies.
David Thornley
@David: quite true -- I don't think Niklaus Wirth had any problem with imposing fairly high runtime overheads (e.g., most Pascals did bounds checking on all array accesses). C++ is governed heavily by the "you don't pay for what you don't use" principle, but that never really affected the design of Pascal (and Wirth *did* use nested functions quite a bit).
Jerry Coffin
There are perfectly obvious zero-overhead ways to implement nested functions.
jalf