Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

nested functions

6 views
Skip to first unread message

Loďc Trégan

unread,
Apr 19, 1998, 3:00:00 AM4/19/98
to

Are nested functions a good thing ? (of course, i have my opinion...)

void f()
{
void InternalA() {}
void InternalB() {}

InternalA();
InternalB();
}

if YES, why they are nt in the draft ?
if NO, why are they allowed in C and not C++ ?

thanks.
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std...@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html ]

Mathew Hendry

unread,
Apr 19, 1998, 3:00:00 AM4/19/98
to

Loïc Trégan <loic....@isdnet.net> wrote:
: Are nested functions a good thing ?

I think they would have a rather limited place in C++.

A very simple way of emulating them is to provide a unique namespace
for your function; this is roughly what would happen under the surface
if they were provided explicitly.

Or perhaps you are talking about local functions, rather than nested
functions. That is, where an inner function has access to the outer
function's variables and functions. (Forgive me if I'm using the wrong
terminology here.)

Local functions are rather more tricky to implement, but the functor
style can be used, with the variables to be used in the inner function
provided as members of the functor class; if the functor class is
nested, you can provide multiple levels of function nesting, too. A
functor can be wrapped up to appear as a normal function, so this sort
of implementation can made entirely invisible to users:

int func( int i ) {
class func_functor {
public:
func_functor() { /* ... */ }
int operator()( int i ) { /* ... */ }
private:
// ...
};
return func_functor()( i );
};

In fact, this is a cleaner way to implement nested functions, too, as
the encapsulation is more clearly visible, configurable, and
enforcible. (If you use the simpler namespace method, there's nothing
to prevent another function from using the same namespace, and not all
that much to indicate that it shouldn't.)

: (of course, i have my opinion...)

Well, what is it? :)

: if YES, why they are nt in the draft ?

Many people feel that the language is complicated enough, if not too
complicated, already. There are already reasonably simple ways to
achieve this sort of thing, and in a much more flexible manner.

: if NO, why are they allowed in C and not C++ ?

They're not allowed in C either. Perhaps you're thinking of Pascal.

--
Mathew Hendry (actually at dial.pipex.com, not dev.null)

Manfred Knemeyer

unread,
Apr 20, 1998, 3:00:00 AM4/20/98
to

Nested function definitions are not allowed in C either.

Loďc Trégan wrote in message <6hac23$8lb$1...@news2.isdnet.net>...


>Are nested functions a good thing ? (of course, i have my opinion...)
>
>void f()
>{
> void InternalA() {}
> void InternalB() {}
>
> InternalA();
> InternalB();
>}
>

>if YES, why they are nt in the draft ?

>if NO, why are they allowed in C and not C++ ?
>

>thanks.
---

Oleg Zabluda

unread,
Apr 20, 1998, 3:00:00 AM4/20/98
to

Loïc Trégan <loic....@isdnet.net> wrote:
: Are nested functions a good thing ? (of course, i have my opinion...)

If they inherit the context from the enclosing function, then
probably not.

: void f()
: {
: void InternalA() {}
: void InternalB() {}

: InternalA();
: InternalB();
: }

You can achieve the same effect with local classes:

void f()
{
struct InternalA { InternalA() {} }
struct InternalB { InternalB() {} }

InternalA();
InternalB();

// A little bit of syntactic sugar never hurt anyone.

}

: if YES, why they are nt in the draft ?


: if NO, why are they allowed in C and not C++ ?

They are not allowed in C either.

Oleg.
--
Life is a sexually transmitted, 100% lethal disease.

Chuck McCorvey

unread,
Apr 20, 1998, 3:00:00 AM4/20/98
to

Loďc Trégan wrote in message <6hac23$8lb$1...@news2.isdnet.net>...
>Are nested functions a good thing ? (of course, i have my opinion...)
>
>void f()
>{
> void InternalA() {}
> void InternalB() {}
>
> InternalA();
> InternalB();
>}
>
>if YES, why they are nt in the draft ?
>if NO, why are they allowed in C and not C++ ?
>


They can be useful to provide further scoping. However, they are probably
(?) less valuable in the presence of classes as in C++.

They are NOT allowed for in C or C++. The above code snippet should have
caused compiler errors of the form (compiled under VC++ 5.0):

test.cpp(3) : error C2601: 'InternalA' : local function definitions are
illegal
test.cpp(4) : error C2601: 'InternalB' : local function definitions are
illegal

--
Chuck McCorvey
Creative SolutionWare, Inc.

Bradd W. Szonye

unread,
Apr 20, 1998, 3:00:00 AM4/20/98
to

Loïc Trégan wrote:
>
> Are nested functions a good thing ? (of course, i have my opinion...)
>
> void f()
> {
> void InternalA() {}
> void InternalB() {}
>
> InternalA();
> InternalB();
> }
>
> if YES, why they are nt in the draft ?
> if NO, why are they allowed in C and not C++ ?
>
> thanks.

They are not allowed in C. Nested functions are allowed by some C
compilers as an extension to the language, but they are not a C feature.

Nesting functions can be a useful scoping construct, but they add little
new utility or expressiveness to a language, so their absence is
generally not seen as a major failing of C or C++, except by advocates
of languages that do support function nesting.
--
Bradd W. Szonye
bra...@concentric.net
http://www.concentric.net/~Bradds

My reply address is correct as-is. The courtesy of providing a correct
reply address is more important to me than time spent deleting spam.

Tony Cook

unread,
Apr 20, 1998, 3:00:00 AM4/20/98
to

Loďc Trégan (loic....@isdnet.net) wrote:
: Are nested functions a good thing ? (of course, i have my opinion...)

: void f()
: {
: void InternalA() {}
: void InternalB() {}

: InternalA();
: InternalB();
: }

Nested functions have some increased runtime costs, though I suspect
they pass the normal 'you only pay for it if you use it' rule for C++.

: if YES, why they are nt in the draft ?


: if NO, why are they allowed in C and not C++ ?

They aren't allowed in C, though your compiler may implement them as
an extension.

--
Tony Cook - to...@online.tmx.com.au

J. Kanze

unread,
Apr 20, 1998, 3:00:00 AM4/20/98
to

"Loďc Trégan" <loic....@isdnet.net> writes:

|> Are nested functions a good thing ? (of course, i have my opinion...)
|>
|> void f()
|> {
|> void InternalA() {}
|> void InternalB() {}
|>
|> InternalA();
|> InternalB();
|> }
|>

|> if YES, why they are nt in the draft ?
|> if NO, why are they allowed in C and not C++ ?

Whether they are a good thing or not is a subject of discussion -- I
happen to like them, although I think that there are even better
solutions in the context of C++.

Good thing or not, they are not allowed in either C or C++. During the
recent revision of the C standard, I think that they were actually
considered and voted down. Of course, this might be partially because
of difficulties in introducing them into the language as it exists
today, without breaking anything else, and without imposing additional
cost on people who don't use them.

If your compiler does not issue a diagnostic in the above program, it is
either seriously broken, or it is not a C or C++ compiler. (Gcc
requires special compiler options, for example, to compile C; be
default, it compiles a language which is only somewhat similar to C.
G++ is somewhat better with regards to C++, but you still need special
options to get C++.)

--
James Kanze +33 (0)1 39 23 84 71 mailto: ka...@gabi-soft.fr
GABI Software, 22 rue Jacques-Lemercier, 78000 Versailles, France
Conseils en informatique orientée objet --
-- Beratung in objektorientierter Datenverarbeitung

Didier Trosset-Moreau

unread,
Apr 20, 1998, 3:00:00 AM4/20/98
to

Hello,

they aren't in the draft and will probably never be.

Bjarne Stroustrup himself has explained that allowing them would generate
lots of overhead in calls (if you think a little about the call mechanism,
you'll reach the same problem very early). And decided to accept no overhead
when designing C++.

That's the main reason, even if adding some notions of locality inside
functions could be a good thing.

He also say that would encourage people to write large functions, and that
it was not a good thing, at all, for their OO designs !

Didier Trosset-Moreau
PAM Development
t...@iprolink.fr


Loďc Trégan a écrit dans le message <6hac23$8lb$1...@news2.isdnet.net>...


>Are nested functions a good thing ? (of course, i have my opinion...)
>
>void f()
>{
> void InternalA() {}
> void InternalB() {}
>
> InternalA();
> InternalB();
>}
>
>if YES, why they are nt in the draft ?
>if NO, why are they allowed in C and not C++ ?

Christopher Eltschka

unread,
Apr 21, 1998, 3:00:00 AM4/21/98
to

Oleg Zabluda wrote:
>
> Loïc Trégan <loic....@isdnet.net> wrote:
> : Are nested functions a good thing ? (of course, i have my opinion...)
>
> If they inherit the context from the enclosing function, then
> probably not.
>
> : void f()

> : {
> : void InternalA() {}
> : void InternalB() {}
>
> : InternalA();
> : InternalB();
> : }
>
> You can achieve the same effect with local classes:
>
> void f()
> {
> struct InternalA { InternalA() {} }
> struct InternalB { InternalB() {} }
>
> InternalA();
> InternalB();
>
> // A little bit of syntactic sugar never hurt anyone.
>
> }

That's not really the same:

void f(int i)
{
int j=1;
void internalA() { j+=i; }
void internalB() { j*=i; }

internalA();
internalB();

assert( j == i*(i+1) );
}

With your replacement, you would need to give all needed variables
as parameters. If the functions are recursive, this may be a big
overhead (no inlining possible, so parameters *must* be pushed
on the stack!) or otherwise require ugly constructs (to reduce the
parameters to one pointer to the actual data; thus making the
function (nearly) as efficient as with local functions).
---

Loic Tregan

unread,
Apr 22, 1998, 3:00:00 AM4/22/98
to

Manfred Knemeyer wrote:
>
> Nested function definitions are not allowed in C either.
>

maybe, but in gcc yes :

main()
{
int f() { return 5; }

printf( "%d", f());

}


test.c : compile
test.cc : does not compile

Andreas Rossberg

unread,
Apr 23, 1998, 3:00:00 AM4/23/98
to

Bradd W. Szonye wrote:
>
> Nesting functions can be a useful scoping construct, but they add little
> new utility or expressiveness to a language, so their absence is
> generally not seen as a major failing of C or C++, except by advocates
> of languages that do support function nesting.

True nested functions (ie. closures) are a _very_ powerful feature that
is absolutely essential if you're seriously using functionals (functions
taking function(pointer)s as argument, also called higher-order
functions). For example, would C++ have closures then the whole tedious
business with iterators in the STL would be almost redundant, because
you can factor out the most common kinds of iterations over arbitrary
containers into a small set of simple library template functions like
iterate, fold, map, etc. that do the looping for you (possibly in an
optimized way) while you just supply the 'body' -- as a simple local
function given directly.

That's why programs in functional languages tend to be shorter by a
factor of 10 than equivalent C/C++ code. A language with good support
for higher-order functions and appropriate libraries highly increases
productivity and modularity. So your statement is plainly false.

--
Andreas Rossberg, rossberg...@ips.cs.tu-bs.de
Please remove the `_antispam' when replying via email.

fa...@email.address.net

unread,
Apr 23, 1998, 3:00:00 AM4/23/98
to

"Didier Trosset-Moreau" <t...@iprolink.fr> writes:

> Bjarne Stroustrup himself has explained that allowing them would generate
> lots of overhead in calls (if you think a little about the call mechanism,
> you'll reach the same problem very early). And decided to accept no overhead
> when designing C++.

Nested functions produce no overhead if you don't use them (just look
at the gcc implementation). As such, they obey Bjarne's zero-overhead
rule, just like exceptions (which are in fact harder to implement in a
way that obeys this rule).

Regards,
Bas de Bakker
basde dot bakker at pica dot nl

Jens Kilian

unread,
Apr 23, 1998, 3:00:00 AM4/23/98
to

"Didier Trosset-Moreau" <t...@iprolink.fr> writes:
> Bjarne Stroustrup himself has explained that allowing them would generate
> lots of overhead in calls (if you think a little about the call mechanism,
> you'll reach the same problem very early). And decided to accept no overhead
> when designing C++.

To properly implement nested functions, you must have true closures.
AFAIK, even GCC supports only downward closures. If nested functions
were supported the way they are (and have been for decades) in Lisp,
the following would be legal:

int (*)(void) make_counter(void)
{
int counter = 0;
int count(void) { return counter++; }

return count;
}

void
some_function(void)
{
int (*counter1)(void) = make_counter();
int (*counter2)(void) = make_counter();

// This would output the sequence 0, 0, 1, 1.
cout << counter1() << counter2() << counter1() << counter2();
}

I think it's safe to say that C/C++ will *never* support such constructs.
Bye,
Jens.
--
mailto:j...@acm.org phone:+49-7031-14-7698 (HP TELNET 778-7698)
http://www.bawue.de/~jjk/ fax:+49-7031-14-7351
PGP: 06 04 1C 35 7B DC 1F 26 As the air to a bird, or the sea to a fish,
0x555DA8B5 BB A2 F0 66 77 75 E1 08 so is contempt to the contemptible. [Blake]

Paul D. DeRocco

unread,
Apr 24, 1998, 3:00:00 AM4/24/98
to

J. Kanze wrote:
>
> Good thing or not, they are not allowed in either C or C++. During the
> recent revision of the C standard, I think that they were actually
> considered and voted down. Of course, this might be partially because
> of difficulties in introducing them into the language as it exists
> today, without breaking anything else, and without imposing additional
> cost on people who don't use them.

As far as I can figure, it would be trivial to allow nested functions,
as long as taking the address of them was prohibited, and as long as
recursion was prohibited. The latter could be prohibited by banning
self-calling, and by banning declarations without definitions, thus
banning the forward calling necessary for mutual recursion.

However, allowing recursion is only a small increase in complexity. The
mechanism for remembering the context of the original call (i.e., the
address of the enclosing stack frame) is well-understood, and is even
built into the x86 ENTER instruction.

To allow pointers to nested functions would be more complex, since the
pointer would have to contain the address of the function and the
address of the enclosing context. But a doubly-nested function would
need two context addresses, and so on. Thus, the type of a pointer to a
nested function would have to be "pointer to function taking blah-blah
parameters and returning blah-blah, and nested n deep". In other words,
you wouldn't be able to use a pointer to a singly-nested function where
a pointer to a non-nested function was expected, and so on.

The only completely general solution would be to require that all
pointers to functions, nested or not, be implemented as the combination
of a pointer the actual code, plus a (possibly null) pointer to an array
of context pointers. This would seem burdensome for the usual case of
non-nested pointers. A compromise would be to distinguish nested and
non-nested functions, and represent only the latter as the combination
of a pointer to code plus a pointer to array of context pointers. But
rather than open this can of worms, I'd vote simply to disallow pointers
to nested functions entirely.

Besides, the sort of thing I usually want to do with nested functions is
to wrap up some short bit of code that gets used over and over again
within some big function, but that can't be broken out into a separate
function because it needs access to lots of the big function's locals.
In this case, I don't need to take its address.

--

Ciao,
Paul
---

Fergus Henderson

unread,
Apr 24, 1998, 3:00:00 AM4/24/98
to

"Paul D. DeRocco" <pder...@ix.netcom.com> writes:

>The only completely general solution would be to require that all
>pointers to functions, nested or not, be implemented as the combination
>of a pointer the actual code, plus a (possibly null) pointer to an array
>of context pointers. This would seem burdensome for the usual case of
>non-nested pointers.

No, there are other solutions based on "trampolines", for example the
one used in GNU C.

This topic has been debated in great detail in this newsgroup
in the distant past -- try DejaNews.

--
Fergus Henderson <f...@cs.mu.oz.au> | "I have always known that the pursuit
WWW: <http://www.cs.mu.oz.au/~fjh> | of excellence is a lethal habit"
PGP: finger f...@128.250.37.3 | -- the last words of T. S. Garp.

Igor Boukanov

unread,
Apr 24, 1998, 3:00:00 AM4/24/98
to

In comp.std.c++ Paul D. DeRocco <pder...@ix.netcom.com> wrote:
> To allow pointers to nested functions would be more complex, since the
> pointer would have to contain the address of the function and the
> address of the enclosing context. But a doubly-nested function would
> need two context addresses, and so on. Thus, the type of a pointer to a
> nested function would have to be "pointer to function taking blah-blah
> parameters and returning blah-blah, and nested n deep". In other words,
> you wouldn't be able to use a pointer to a singly-nested function where
> a pointer to a non-nested function was expected, and so on.

GCC uses on fly code generation to make possible free mixture of pointers
to non-nested and nested functions that result in no overhead for ordinary
pointers. Basically it constructs a code that that would restore necessary
context and then call the nested function itself.

And this ability to mix is exactly the thing that is nice to have when
calling routines written in other languages that expect function pointer.
You do not need to introduce a static variables to store additional
information. All other usages of the nested functions may be rewritten
in plain C++ without lost of efficiency, but that ability can not be emulated
at all.

--
Regards, Igor Boukanov.
igor.b...@fi.uib.no
http://www.fi.uib.no/~boukanov/

Valentin Bonnard

unread,
Apr 25, 1998, 3:00:00 AM4/25/98
to

Loic Tregan wrote:
>
> Manfred Knemeyer wrote:
> >
> > Nested function definitions are not allowed in C either.
>
> maybe, but in gcc yes :

gcc allows many things only remotely related with C and C++.

;-)

Try with gcc -ansi -pedantic. (I actually think that the
default behaviour of gcc/g++ w/o any options is a problem.)

--

Valentin Bonnard mailto:bonn...@pratique.fr
info about C++/a propos du C++: http://pages.pratique.fr/~bonnardv/
---

Paul D. DeRocco

unread,
Apr 28, 1998, 3:00:00 AM4/28/98
to

Igor Boukanov wrote:
>
> GCC uses on fly code generation to make possible free mixture of
> pointers
> to non-nested and nested functions that result in no overhead for
> ordinary
> pointers. Basically it constructs a code that that would restore
> necessary
> context and then call the nested function itself.

In other words, GCC generates a thunk at runtime that passes the context
into the function, perhaps by loading it into a register? This is what
Win16 used to do for callbacks, although it was loading the DS register
with the instance's data segment. The problem is, where do you put these
thunks? The obvious place is on the stack, like any other temporary, but
many architectures don't let you execute code out of pages that are used
for data. This means the compiler has to allocate the thunk somewhere
else, and include the necessary exception handling code to guarantee
that the thunk is deallocated when the stack is unwound. Certainly
doable, but slightly messy. It's probably worth it, though.

--

Ciao,
Paul

Christopher Eltschka

unread,
Apr 28, 1998, 3:00:00 AM4/28/98
to

Jens Kilian wrote:
>
> "Didier Trosset-Moreau" <t...@iprolink.fr> writes:
> > Bjarne Stroustrup himself has explained that allowing them would generate
> > lots of overhead in calls (if you think a little about the call mechanism,
> > you'll reach the same problem very early). And decided to accept no overhead
> > when designing C++.
>
> To properly implement nested functions, you must have true closures.

I disagree. To properly implement nested functions, you have to
implement
nested functions. To properly implement closures, you have to implement
closures (which indeed includes support for nested functions).

> AFAIK, even GCC supports only downward closures. If nested functions
> were supported the way they are (and have been for decades) in Lisp,
> the following would be legal:
>
> int (*)(void) make_counter(void)
> {
> int counter = 0;
> int count(void) { return counter++; }
>
> return count;
> }
>
> void
> some_function(void)
> {
> int (*counter1)(void) = make_counter();
> int (*counter2)(void) = make_counter();
>
> // This would output the sequence 0, 0, 1, 1.
> cout << counter1() << counter2() << counter1() << counter2();
> }
>
> I think it's safe to say that C/C++ will *never* support such constructs.

Other thanm nested functions, closures would cause problems with the C++
object model. Look for example at the following code:

struct X
{
X() { cout << "X()" << endl; }
~X() { cout << "~X()" << endl; }
};

typedef X* (*fun)();

fun f(int i)
{
X x;
X* c1() { return &x; }
X* c2() { return 0; }
if (i>0)
return c1;
else
return c2;
};

Now, when should the destructor of x be called?
Normally, it would be called at exit from f, but due to closures
x might still be in use (it is, if i>0).
Now, if i<=0, c2 is returned which doesn't need x, so x may be
destructed at once. However, if c1 is returned, x is still needed.
Indeed, it is still needed as long as the function pointer is in use
(including all copies). Thus it's very much overhead just to deside
when X should be destroyed.
Note that you don't have this problem if you just have nested
functions, as the pointer to the local function is invalid as soon as
f returns (just as any other pointer to local entities).

Closures might be useful, but have too complicated interaction
with the C++ object model (I guess they would work well with the
Java object model). Note however, that once you return from the
enclosing function, a closure is in no way different from a function
object, from the user's point of view. And before the return, they
are not any different to ordinary nested functions. Indeed I guess
I would never use them at all.

However, I'd still like to see nested functions in C++. While
they are not a tool you use everyday, they are very handy in
some situations, and they don't cause any problems with the C++
object model.

Barry Margolin

unread,
Apr 28, 1998, 3:00:00 AM4/28/98
to

In article <35459993...@physik.tu-muenchen.de>,

Christopher Eltschka <celt...@physik.tu-muenchen.de> wrote:
>Closures might be useful, but have too complicated interaction
>with the C++ object model (I guess they would work well with the
>Java object model).

Which probably explains why closures are generally only found in languages
with garbage collection.

--
Barry Margolin, bar...@bbnplanet.com
GTE Internetworking, Powered by BBN, Cambridge, MA
*** DON'T SEND TECHNICAL QUESTIONS DIRECTLY TO ME, post them to newsgroups.

Bradd W. Szonye

unread,
Apr 29, 1998, 3:00:00 AM4/29/98
to

> Bradd W. Szonye wrote:
> >
> > Nesting functions can be a useful scoping construct, but they add
> > little new utility or expressiveness to a language, so their
> > absence is generally not seen as a major failing of C or C++,
> > except by advocates of languages that do support function nesting.

Andreas Rossberg wrote:
>
> True nested functions (ie. closures) are a _very_ powerful feature
> that is absolutely essential if you're seriously using functionals
> (functions taking function(pointer)s as argument, also called
> higher-order functions).

Okay, no argument there. I thought the original poster was talking about
the 'toy' local functions in, say, Pascal, not the higher-order
functions available in functional programming languages, which I'll
agree are very powerful.

I'd put simple local functions in the same category as the 'super'
facility: nice to have, but not necessary if there are other features
more important to develop in the time available. Just as 'super' is
possible (more or less) with 'typedef base base_type,' simple local
functions are possible with the unnamed namespace and functor classes.

As for closures, that's a very interesting addition to the language...
but the interactions with the C++ object lifetime model could get nasty.
Also, what is the possibility of a roll-your-own closure with the
facilities already available in C++? I'm not especially knowledgeable
about the issues in implementing closures; functional programming is
just an occasional hobby for me, and I'm much more familiar with, say,
implementation of polymorphism, heap memory management, and exception
handling.

(If answers to that question are too far off-topic, e-mail me comments,
but please let me know whether it's a CC.)

My reply address is correct as-is. The courtesy of providing a correct
reply address is more important to me than time spent deleting spam.

Matthias Neeracher

unread,
Apr 29, 1998, 3:00:00 AM4/29/98
to

"Bradd W. Szonye" <bra...@concentric.net> writes:
> Nesting functions can be a useful scoping construct, but they add little
> new utility or expressiveness to a language, so their absence is
> generally not seen as a major failing of C or C++, except by advocates
> of languages that do support function nesting.

Nested *anonymous* functions would, IMHO, be quite useful in combination with
the STL:

for_each(container.begin(), container.end(),
void (*)(const Element & elt) {
// Do something with elt
}
);

I'd prefer that style over the bind2nd/mem_fun etc. template calculus.
Matthias

--
Matthias Neeracher <ne...@iis.ee.ethz.ch> http://www.iis.ee.ethz.ch/~neeri
"Paranotions, which designate constructs, may now contain metanotions and
``hypernotions'' have been introduced in order to designate protonotions"
-- A. van Wijngaarden et al., _ALGOL 68 Revised Report_

Hyman Rosen

unread,
Apr 29, 1998, 3:00:00 AM4/29/98
to

Christopher Eltschka <celt...@physik.tu-muenchen.de> writes:
> Other thanm nested functions, closures would cause problems with the C++
> object model. Look for example at the following code:
>
> struct X { };

> typedef X* (*fun)();
> fun f(int i)
> {
> X x;
> X* c1() { return &x; }
> X* c2() { return 0; }
> return i > 0 ? c1 : c2;

> };
>
> Now, when should the destructor of x be called?

At the normal time, right before f returns. Closures in C and C++ do
not, and are not meant to, behave like closures in Scheme. C/C++ is
not a garbage-collected language, and automatic variables do not
survive the exit of their block. In your example, users of the return
value of f may will access a dangling pointer. Mot only x, but c1 and
c2 themselves no longer exist after f returns. Closures in C/C++ are
still needed for mutually recursive or further nested inner functions,
however.

David R Tribble

unread,
Apr 29, 1998, 3:00:00 AM4/29/98
to

Christopher Eltschka wrote:
> Closures might be useful, but have too complicated interaction
> with the C++ object model (I guess they would work well with the
> Java object model). Note however, that once you return from the
> enclosing function, a closure is in no way different from a function
> object, from the user's point of view. And before the return, they
> are not any different to ordinary nested functions. Indeed I guess
> I would never use them at all.
>
> However, I'd still like to see nested functions in C++. While
> they are not a tool you use everyday, they are very handy in
> some situations, and they don't cause any problems with the C++
> object model.

One benefit of nested functions that I haven't seen mentioned is
that they act a lot like private member functions but they don't
need to be declared in the class declaration. For programmers (like
me) who believe it's sinful to show off class private members in
header files (to client functions that have no business seeing the
class's privates), this is a good thing.

A function nested within a normal (non-static) member function
acts like a file-static function as far as scoping, but still has
access to all the class members like a true private member
function does.

Not that I'm in favor of adding nested functions to C++, mind you.
I'd rather see a way of declaring private member functions within
a source file that didn't require adding a function declaration to
the class header file. This would accomplish the same thing for me.

In fact, it would be nice if I could somehow put only the public
members in the class declaration in the (distributed) header file,
and extend the declaration by adding the protected and private
member declarations in a local (undistributed) header file.
Now that would be true data hiding.

-- David R. Tribble, david....@noSPAM.central.beasys.com --

James Kuyper

unread,
Apr 29, 1998, 3:00:00 AM4/29/98
to

Loic Tregan wrote:
>
> Manfred Knemeyer wrote:
> >
> > Nested function definitions are not allowed in C either.
> >
>
> maybe, but in gcc yes :

gcc with no compiler options selected implements a language inspired by
C, but which comes nowhere near meeting the ANSI standards for C.

Paul D. DeRocco

unread,
Apr 29, 1998, 3:00:00 AM4/29/98
to

Bradd W. Szonye wrote:
>
> Okay, no argument there. I thought the original poster was talking
> about
> the 'toy' local functions in, say, Pascal, not the higher-order
> functions available in functional programming languages, which I'll
> agree are very powerful.

What I bemoan the lack of is precisely what you call "toy" local
functions. I know I can do fancier stuff with functors that carry data
along with them. However, every now and then, I have to write some big
function, perhaps containing a long switch statement, and I find there's
the same little tidbit of code in it over and over and over again, that
I'd really like to wrap in a function, except that it needs access to
two or three of the local variables of the outer function. For instance,
suppose I'm writing a disassembler that fetches bytes from memory, and
appends characters to a character buffer:

int disassemble(
const unsigned char* bin, // instruction
char* buf, // result buffer
unsigned buflen); // buffer length

Such code generally involves big switch statements, but I need to
produce output by appending to the buffer and checking against the
buffer length. This is done in dozens of places, so I'd like to do it
with a function:

void append(char c, char*& buf, unsigned& buflen) {
*buf++ = c;
if (!--buflen) throw length_error();
}

If I could write it as a nested function, I could leave off the buf and
buflen parameters, which makes the resulting code more efficient in
terms of space, speed, and clarity. Since I can't do that, I end up
storing buf and buflen into globals, which is a kludge.

--

Ciao,
Paul

Steven Perryman

unread,
Apr 29, 1998, 3:00:00 AM4/29/98
to

In article <q1l11.32$vA5.7...@cam-news-reader1.bbnplanet.com> Barry Margolin <bar...@bbnplanet.com> writes:
>In article <35459993...@physik.tu-muenchen.de>,
>Christopher Eltschka <celt...@physik.tu-muenchen.de> wrote:
>>Closures might be useful, but have too complicated interaction
>>with the C++ object model (I guess they would work well with the
>>Java object model).

>Which probably explains why closures are generally only found in languages
>with garbage collection.

I think it's more general than that.
As C++ has stack-based object allocation as well as heap, closures would
be a problem. This is why C++ cannot have nested classes a la Simula, cos
if the outer object was stack allocated, and the inner objects were on the
heap, the 'environment' of the inner objects would be trashed once the
outer object goes (stack frame folds etc) .

If a compiler was clever and forced outer classes to always be allocated
on the heap, then perhaps we could do it.


Regards,
Steven Perryman
ste...@nortel.co.uk

Christopher Eltschka

unread,
Apr 29, 1998, 3:00:00 AM4/29/98
to

Hyman Rosen wrote:
>
> Christopher Eltschka <celt...@physik.tu-muenchen.de> writes:
> > Other thanm nested functions, closures would cause problems with the C++
> > object model. Look for example at the following code:
> >
> > struct X { };
> > typedef X* (*fun)();
> > fun f(int i)
> > {
> > X x;
> > X* c1() { return &x; }
> > X* c2() { return 0; }
> > return i > 0 ? c1 : c2;
> > };
> >
> > Now, when should the destructor of x be called?
>
> At the normal time, right before f returns. Closures in C and C++ do
> not, and are not meant to, behave like closures in Scheme. C/C++ is
> not a garbage-collected language, and automatic variables do not
> survive the exit of their block. In your example, users of the return
> value of f may will access a dangling pointer. Mot only x, but c1 and
> c2 themselves no longer exist after f returns. Closures in C/C++ are
> still needed for mutually recursive or further nested inner functions,
> however.

But as long as f didn't return, you don't need a closure mechanism,
as the usual nested function mechanism would work well (you could, of
course, define this as a specialized closure, but then you could
as well say C++ already supports nested functions, but limited to
the first level, which is functions at global scope). As I see them,
closures are just there to ensure that variables of the outer scope
survive long enough.
Note that f.ex. Pascal doesn't have closures, but supports nested
functions at arbitrary levels including direct and indirect
recursion. This more than proves that closures are not necessary
for this.

Fergus Henderson

unread,
Apr 29, 1998, 3:00:00 AM4/29/98
to

a...@research.att.com (Andrew Koenig) writes:

>The reason C++ does not have nested functions is that implementing them
>in full generality would make every function pointer into a two-word
>object -- it would need the address of the code and the address of the
>corresponding activation record. It might be possible to avoid the
>space overhead by using the `trampoline' technique, but that requires
>the ability to generate code in data space, which not every processor
>supports.

There are trampoline-based techniques that do not require the
ability to generate code in data space.

For example, the system can have a fixed-size array of trampoline code
fragments, computed at code generation time or link time, and a
corresponding array of two-word (code address, data address) pairs.
Each trampoline fragment looks up the corresponding slot in the array,
puts the data address in a register, and jumps to the code address. In
addition to these two arrays, you have a trampoline stack pointer.
Trampolines are allocated from the trampoline stack when needed, and
deallocated when the containing function exits.

The drawback of this approach is that the size of the trampoline stack must
be fixed at link time. This does not seem too bad -- after all, many
C implementations and thread libraries used fixed-sized stacks.
Most programs would use very few trampolines at any given point in time,
so a single page (or less) would suffice for the vast majority of programs.

In addition, if the processor / OS has appropriate virtual memory
support, then you can avoid the need for a fixed-size trampoline stack,
still without doing any code generation at runtime, and with no need to
flush the instruction cache. I'll leave that one as an exercise for
the reader.

--
Fergus Henderson <f...@cs.mu.oz.au> | "I have always known that the pursuit
WWW: <http://www.cs.mu.oz.au/~fjh> | of excellence is a lethal habit"
PGP: finger f...@128.250.37.3 | -- the last words of T. S. Garp.

Hyman Rosen

unread,
Apr 29, 1998, 3:00:00 AM4/29/98
to

Christopher Eltschka <celt...@physik.tu-muenchen.de> writes:
> But as long as f didn't return, you don't need a closure mechanism,
> as the usual nested function mechanism would work well.

Perhaps I am misusing the term closure? I understand a closure to be the
combination of a function pointer plus a set of stack frame pointers, one
for each static nesting level of the pointed-to function. What do you mean
by it? Are Scheme closures different? In Scheme, automatic variables can
outlive the exit from their scope, but that's because Scheme is a garbage
collected language. Java now has the same sort of thing.

David R Tribble

unread,
Apr 29, 1998, 3:00:00 AM4/29/98
to

Christopher Eltschka <celt...@physik.tu-muenchen.de> writes:
>>> Other than nested functions, closures would cause problems with the
>>> C++ object model. Look for example at the following code:
>>>
>>> struct X { };
>>> typedef X* (*fun)();
>>> fun f(int i)
>>> {
>>> X x;
>>> X* c1() { return &x; }
>>> X* c2() { return 0; }
>>> return i > 0 ? c1 : c2;
>>> };
>>>
>>> Now, when should the destructor of x be called?

Hyman Rosen wrote:
>> At the normal time, right before f returns. Closures in C and C++ do
>> not, and are not meant to, behave like closures in Scheme. C/C++ is
>> not a garbage-collected language, and automatic variables do not
>> survive the exit of their block. In your example, users of the return
>> value of f may will access a dangling pointer. Mot only x, but c1 and
>> c2 themselves no longer exist after f returns. Closures in C/C++ are
>> still needed for mutually recursive or further nested inner
>> functions, however.

Christopher Eltschka wrote:
> But as long as f didn't return, you don't need a closure mechanism,

> as the usual nested function mechanism would work well (you could, of
> course, define this as a specialized closure, but then you could
> as well say C++ already supports nested functions, but limited to
> the first level, which is functions at global scope). As I see them,
> closures are just there to ensure that variables of the outer scope
> survive long enough.
> Note that f.ex. Pascal doesn't have closures, but supports nested
> functions at arbitrary levels including direct and indirect
> recursion. This more than proves that closures are not necessary
> for this.

Not exactly. In compiler terminology, the nested functions need
a "display block" in their call activation records. Each display
allows the nested function to access its outer function's local
variables in addition to its own local variables, and works very
well with recursion.

An example illustrates more of the problems:

class Foo
{
int bar(int i) // A member func
{
int baz() // A nested func
{
count += i; // Accesses this->count and bar.i
};

int boz() // Another nested func
{
count -= i; // Accesses this->count and bar.i
if (...)
boz(); // Recursive call
};

if (...)
baz();
else
boz();
}

int count; // Member of *this
};

Here, both nested functions baz() and boz() access a local variable
(i) of their outer function bar(). They also access a member (count)
of the 'this' object of bar(), which is technically also a local
variable. Note than boz() has to access the same 'count' and 'i'
locals even if it's been called recursively.

All of these are solved by some form of "display" activation record.
Pascal, which supports nested routines, uses this kind of mechanism.
Apparently g++ uses something similar (called a "trampoline") but
that has less overhead for non-nested routines.

-- David R. Tribble, david....@noSPAM.central.beasys.com --

Andrew Koenig

unread,
Apr 30, 1998, 3:00:00 AM4/30/98
to

In article <6i7h14$e33$1...@mulga.cs.mu.OZ.AU>,
Fergus Henderson <f...@cs.mu.OZ.AU> wrote:

> >It might be possible to avoid the
> >space overhead by using the `trampoline' technique, but that requires
> >the ability to generate code in data space, which not every processor
> >supports.

> There are trampoline-based techniques that do not require the
> ability to generate code in data space.

Maybe so, but they did not surface during the committee's discussions.
--
--Andrew Koenig
a...@research.att.com
http://www.research.att.com/info/ark

Paul D. DeRocco

unread,
Apr 30, 1998, 3:00:00 AM4/30/98
to

Fergus Henderson wrote:
>
> For example, the system can have a fixed-size array of trampoline code
> fragments, computed at code generation time or link time, and a
> corresponding array of two-word (code address, data address) pairs.
> Each trampoline fragment looks up the corresponding slot in the array,
> puts the data address in a register, and jumps to the code address.
> In addition to these two arrays, you have a trampoline stack pointer.
> Trampolines are allocated from the trampoline stack when needed, and
> deallocated when the containing function exits.
>
> The drawback of this approach is that the size of the trampoline stack
> must be fixed at link time. This does not seem too bad -- after all,
> many C implementations and thread libraries used fixed-sized stacks.

The other drawback is that you need to allocate one of these trampoline
stacks every time you start a new thread, in a multi-threaded
environment. You need, in effect, two stacks per thread, since what you
are trying to simulate is one stack that the machine architecture won't
let you do because it prohibits executing code stored on the real stack.

--

Ciao,
Paul

JHB NIJHOF

unread,
May 1, 1998, 3:00:00 AM5/1/98
to

Paul D. DeRocco <pder...@ix.netcom.com> wrote:

: What I bemoan the lack of is precisely what you call "toy" local


: functions. I know I can do fancier stuff with functors that carry data
: along with them. However, every now and then, I have to write some big
: function, perhaps containing a long switch statement, and I find there's
: the same little tidbit of code in it over and over and over again, that
: I'd really like to wrap in a function, except that it needs access to
: two or three of the local variables of the outer function. For instance,
: suppose I'm writing a disassembler that fetches bytes from memory, and
: appends characters to a character buffer:

: int disassemble(
: const unsigned char* bin, // instruction
: char* buf, // result buffer
: unsigned buflen); // buffer length

: Such code generally involves big switch statements, but I need to
: produce output by appending to the buffer and checking against the
: buffer length. This is done in dozens of places, so I'd like to do it
: with a function:

: void append(char c, char*& buf, unsigned& buflen) {
: *buf++ = c;
: if (!--buflen) throw length_error();
: }

: If I could write it as a nested function, I could leave off the buf and
: buflen parameters, which makes the resulting code more efficient in
: terms of space, speed, and clarity. Since I can't do that, I end up
: storing buf and buflen into globals, which is a kludge.

You can do that with a class:
Class Output {
private:
char* buf;
unsigned buflen;
public:
Output(char *pointer,int length)
: buf(pointer), buflen(length) {};
void operator()(char c) {

*buf++ = c;
if (!--buflen) throw length_error();
}
}

Then you can use Output as:

Output append(somebuffer, itslength);
...
append(c);

Jeroen Nijhof
---

J. Kanze

unread,
May 1, 1998, 3:00:00 AM5/1/98
to

"Didier Trosset-Moreau" <t...@iprolink.fr> writes:

|> That's the main reason, even if adding some notions of locality inside
|> functions could be a good thing.
|>
|> He also say that would encourage people to write large functions, and that
|> it was not a good thing, at all, for their OO designs !

There's a lot in C++ which could be eliminated by that argument:-).

Seriously, however, my preferred solution WOULD be more OO : I'd make
the local context available as a built-in class, from which you could
inherit. So that local functional objects could have access to the
local context.

Specifying this exactly is not trivial, however. Given the strong
template-orientation of the standard library, to be really effective,
you would want to be able to instantiate templates over the local
functional objects, which introduces a number of other technical
problems.

--
James Kanze +33 (0)1 39 23 84 71 mailto: ka...@gabi-soft.fr
GABI Software, 22 rue Jacques-Lemercier, 78000 Versailles, France
Conseils en informatique orientée objet --
-- Beratung in objektorientierter Datenverarbeitung

J. Kanze

unread,
May 1, 1998, 3:00:00 AM5/1/98
to

"Paul D. DeRocco" <pder...@ix.netcom.com> writes:

|> J. Kanze wrote:
|> >
|> > Good thing or not, they are not allowed in either C or C++. During the
|> > recent revision of the C standard, I think that they were actually
|> > considered and voted down. Of course, this might be partially because
|> > of difficulties in introducing them into the language as it exists
|> > today, without breaking anything else, and without imposing additional
|> > cost on people who don't use them.
|>
|> As far as I can figure, it would be trivial to allow nested functions,
|> as long as taking the address of them was prohibited, and as long as
|> recursion was prohibited.

In which case, most of the utility is gone. Why on earth would you want
local functions, if not to pass their address to another function?

|> The latter could be prohibited by banning
|> self-calling, and by banning declarations without definitions, thus
|> banning the forward calling necessary for mutual recursion.
|>
|> However, allowing recursion is only a small increase in complexity. The
|> mechanism for remembering the context of the original call (i.e., the
|> address of the enclosing stack frame) is well-understood, and is even
|> built into the x86 ENTER instruction.

Correct. But none of the x86 C/C++ compilers I know use this
instruction, because it costs more time than a simple PUSH EBP; MOV
EBP,ESP sequence. (Or has this changed? It's been some time since I
last looked at the generated output of a C/C++ compiler on an Intel
processor.)

|> To allow pointers to nested functions would be more complex, since the

|> pointer would have to contain the address of the function and the
|> address of the enclosing context.

There are other solutions. For example, one could generate a small bit
of code on the fly, which loads the pointer to the context (as a
constant), then jumps to the actual function.

|> But a doubly-nested function would
|> need two context addresses, and so on. Thus, the type of a pointer to a
|> nested function would have to be "pointer to function taking blah-blah
|> parameters and returning blah-blah, and nested n deep". In other words,
|> you wouldn't be able to use a pointer to a singly-nested function where
|> a pointer to a non-nested function was expected, and so on.
|>

|> The only completely general solution would be to require that all
|> pointers to functions, nested or not, be implemented as the combination
|> of a pointer the actual code, plus a (possibly null) pointer to an array
|> of context pointers. This would seem burdensome for the usual case of

|> non-nested pointers. A compromise would be to distinguish nested and
|> non-nested functions, and represent only the latter as the combination
|> of a pointer to code plus a pointer to array of context pointers. But
|> rather than open this can of worms, I'd vote simply to disallow pointers
|> to nested functions entirely.
|>
|> Besides, the sort of thing I usually want to do with nested functions is
|> to wrap up some short bit of code that gets used over and over again
|> within some big function, but that can't be broken out into a separate
|> function because it needs access to lots of the big function's locals.
|> In this case, I don't need to take its address.

That's easily done with a local class. (For that matter, most of what
can be done with a nested function can easily be done with a nested
class. On the condition that the function to which you pass the address
expects a pointer to a class rather than a pointer to a function.)

J. Kanze

unread,
May 1, 1998, 3:00:00 AM5/1/98
to

"Paul D. DeRocco" <pder...@ix.netcom.com> writes:

You chose a bad example. This is precisely the type of thing that IS
best done with a class, since you probably don't want any access to buf
outside of append. The function should thus take a reference to a
Buffer, which encapsulates buf and buflen, rather than two separate
arguments.

Paul D. DeRocco

unread,
May 2, 1998, 3:00:00 AM5/2/98
to

JHB NIJHOF wrote:
>
> You can do that with a class:
> Class Output {
> private:
> char* buf;
> unsigned buflen;
> public:
> Output(char *pointer,int length)
> : buf(pointer), buflen(length) {};
> void operator()(char c) {
> *buf++ = c;
> if (!--buflen) throw length_error();
> }
> }
>
> Then you can use Output as:
>
> Output append(somebuffer, itslength);
> ...
> append(c);

That's a nice way to solve the readability problem, but it's still
rather inefficient internally, since append(c) is really
append->operator()(c), and the access to buf and buflen involve
dereferencing the this pointer.

--

Ciao,
Paul
---

Fergus Henderson

unread,
May 3, 1998, 3:00:00 AM5/3/98
to

"Paul D. DeRocco" <pder...@ix.netcom.com> writes:

>Fergus Henderson wrote:
>>
>> For example, the system can have a fixed-size array of trampoline code
>> fragments, computed at code generation time or link time, and a
>> corresponding array of two-word (code address, data address) pairs.
>> Each trampoline fragment looks up the corresponding slot in the array,
>> puts the data address in a register, and jumps to the code address.
>> In addition to these two arrays, you have a trampoline stack pointer.
>> Trampolines are allocated from the trampoline stack when needed, and
>> deallocated when the containing function exits.
>>
>> The drawback of this approach is that the size of the trampoline stack
>> must be fixed at link time. This does not seem too bad -- after all,
>> many C implementations and thread libraries used fixed-sized stacks.
>
>The other drawback is that you need to allocate one of these trampoline
>stacks every time you start a new thread, in a multi-threaded
>environment. You need, in effect, two stacks per thread, since what you
>are trying to simulate is one stack that the machine architecture won't
>let you do because it prohibits executing code stored on the real stack.

You don't need to have one stack of trampoline code fragments per thread,
because you can reuse the same trampoline code fragments for the different
threads. You do have to have a separate trampoline data address stack
for each thread. However, you can allocate that on the heap, lazily,
so threads which don't use trampolines need not incur any overhead for
this.

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

I'll explain in a little bit more detail how you can reuse the same
trampoline code fragments for different threads, and how you can
implement this all in an efficient manner.

Each trampoline code fragment could be reduced to just a single (CISC)
instruction:

jsr do_trampoline

All it does is push the program counter on the stack,
and jump to `do_trampoline'. Most of the work is done in
do_trampoline(), of which there need only be one copy.
Note that this code fragment is *very* short!
This allows you to use a relatively large trampoline
code fragment stack without using up much code space.

The do_trampoline() function would look something like this:

typedef void Func (void);
struct Slot { Func *code_address; void *data_address }:

// `__thread_local' causes data to be allocated
// in thread-local storage
__thread_local Slot *trampoline_stack;
__thread_local int trampoline_stack_size;
__thread_local int trampoline_stack_pointer;

extern char *trampoline_code_start;

// pseudo-code
do_trampoline() {
int trampoline_num = (saved_pc - trampoline_code_start) / TRAMPOLINE_SIZE;
Func *func = trampoline_stack[trampoline_num].code_address;
void *data = trampoline_stack[trampoline_num].data_address;
return (*func)(data); // tailcall
}

Here I've used `__thread_local' to declare thread-local storage.
By doing this, I'm skipping over some details, of course, but
if you want to know how to implement that, I can explain that too.
`saved_pc' is the program counter that was pushed onto the stack
by the trampoline code fragment.
`trampoline_code_start' is the address at the beginning of the
trampoline code fragment array.
`TRAMPOLINE_SIZE' is the size of each trampoline code fragment.

Apart from that, all the compiler needs is a couple of routines to
allocate and deallocate trampolines:

Func *push_trampoline(Func *func, void *data) {
if (!trampoline_stack) {
trampoline_stack_size = INITIAL_SIZE;
trampoline_stack = (Slot *)
malloc(trampoline_stack_size * sizeof(Slot));
trampoline_stack_pointer = 0;
}
if (trampoline_stack_ptr >= trampoline_stack_size) {
trampoline_stack_size *= 2;
trampoline_stack = (Slot *)
realloc(trampoline_stack_size * sizeof(Slot));
}
trampoline_stack[trampoline_stack_ptr].code_address = func;
trampoline_stack[trampoline_stack_ptr].data_address = data;
return (Func *) (trampoline_code_start +
TRAMPOLINE_SIZE * trampoline_stack_pointer++);
}

void pop_trampoline() {
trampoline_stack_pointer--;
}

This implementation would completely avoid "distributed fat".
For programs which don't use trampolines at all, the linker would
not need to link in the trampoline code stack or any of the above code.
For programs which do take the address of nested functions,
the parts of the program that use this feature will be the only
parts that incur any execution overhead. And even for those parts,
the overheads should be fairly small.

--
Fergus Henderson <f...@cs.mu.oz.au> | "I have always known that the pursuit
WWW: <http://www.cs.mu.oz.au/~fjh> | of excellence is a lethal habit"
PGP: finger f...@128.250.37.3 | -- the last words of T. S. Garp.

Jason Merrill

unread,
May 3, 1998, 3:00:00 AM5/3/98
to

>>>>> David R Tribble <david....@noSPAM.central.beasys.com> writes:

> All of these are solved by some form of "display" activation record.
> Pascal, which supports nested routines, uses this kind of mechanism.
> Apparently g++ uses something similar (called a "trampoline") but
> that has less overhead for non-nested routines.

gcc uses a dynamic chain, which is an alternative to displays. But only
the C frontend (and, naturally, the Pascal frontend) supports this; g++
does not, because nobody has done the necessary work.

Jason

Valentin Bonnard

unread,
May 4, 1998, 3:00:00 AM5/4/98
to

Andreas Rossberg <ross...@ips.cs.tu-bs.de> writes:

> For example, would C++ have closures then the whole tedious
> business with iterators in the STL would be almost redundant, because
> you can factor out the most common kinds of iterations over arbitrary
> containers into a small set of simple library template functions like
> iterate, fold, map, etc.

The iterators in the STL just do what you describe: factor out
iterations over arbitrary containers into a set of simple
library template functions: for_each, copy, find, ect.

--

Valentin Bonnard mailto:bonn...@pratique.fr
info about C++/a propos du C++: http://pages.pratique.fr/~bonnardv/

JHB NIJHOF

unread,
May 4, 1998, 3:00:00 AM5/4/98
to

Paul D. DeRocco <pder...@ix.netcom.com> wrote:
: JHB NIJHOF wrote:
:>
:> You can do that with a class:
:> Class Output {
:> private:
:> char* buf;
:> unsigned buflen;
:> public:
:> Output(char *pointer,int length)
:> : buf(pointer), buflen(length) {};
:> void operator()(char c) {
:> *buf++ = c;
:> if (!--buflen) throw length_error();
:> }
:> }
:>
:> Then you can use Output as:
:>
:> Output append(somebuffer, itslength);
:> ...
:> append(c);

: That's a nice way to solve the readability problem, but it's still
: rather inefficient internally, since append(c) is really
: append->operator()(c), and the access to buf and buflen involve
: dereferencing the this pointer.

It seems to be efficient enough for use in STL, though
(after inlining, of course): this is how the function adapters
like bind2nd() work.

--
Jeroen Nijhof J.H.B....@aston.ac.uk
Accordion Links http://www-th.phys.rug.nl/~nijhojhb/accordions.html

Christopher Eltschka

unread,
May 4, 1998, 3:00:00 AM5/4/98
to

Hyman Rosen wrote:
>
> Christopher Eltschka <celt...@physik.tu-muenchen.de> writes:
> > But as long as f didn't return, you don't need a closure mechanism,
> > as the usual nested function mechanism would work well.
>
> Perhaps I am misusing the term closure? I understand a closure to be the
> combination of a function pointer plus a set of stack frame pointers, one
> for each static nesting level of the pointed-to function. What do you mean
> by it? Are Scheme closures different? In Scheme, automatic variables can
> outlive the exit from their scope, but that's because Scheme is a garbage
> collected language. Java now has the same sort of thing.

Maybe my understanding of the term "closure" is wrong. The posting which
introduced the term to the thread spoke about "true closures" with an
example where the function context outlives the return, and the
behaviour
of the program demonstrated just this by creating two such contexts.
Also it mentioned the gcc model as not being "true closures". Therefore
I concluded that closures are function contexts which "survive"
the return of the function (which makes sense as those contexts are in
some way "closed" by the return: they cannot be accessed directly
any more, but only indirectly by the local functions).

BTW, you don't need a frame pointer for each nesting level. You
just need one for the level below. From there, you can get to the other
levels sequencially.

Antonio

unread,
May 5, 1998, 3:00:00 AM5/5/98
to


Jim Coplien's book (C++ Idioms) has an interesting implementation
of nested functions. I think it's quite easy and very portable,
maybe worth to take into consideration.

Hope it helps,
Antonio

John Goodwin

unread,
May 5, 1998, 3:00:00 AM5/5/98
to

On 19 Apr 98 03:54:41 GMT, "Loďc Trégan" <loic....@isdnet.net>
wrote:

>Are nested functions a good thing ? (of course, i have my opinion...)
>
>void f()
>{
> void InternalA() {}
> void InternalB() {}
>
> InternalA();
> InternalB();
>}
>
>if YES, why they are nt in the draft ?
>if NO, why are they allowed in C and not C++ ?
>
>thanks.
>---

I was once involved in an audit on a program of around 110,000 lines
of code.

It was believed to contain about a dozen goto's, but in fact turned
out to have around 100.

On investigation it was found that EVERY SINGLE ONE of the goto's was
there because a local function was required.

Some of the drones who considered themselves "purists" wanted the code
modified with semaphores and extra if statements, but sanity ruled,
and the goto,s were allowed to stand.

It would have been nice to be able to replace them tidily with local
functions but ....

-----------

I've now read the rest of the thread, and realise that what I would
like is a very small subset of what could be involved in providing
local functions.

To handle the cases I have in mind, you can put two restrictions on
your local functions:

1) No address taking.
2) No other local function of the calling function is in scope
inside the local function.

This creates no extra overhead, and would provide a significant
benefit in those cases where it is needed.

But it's not going to happen so I suppose we'll just have to accept
it.

JG

Steve Clamage

unread,
May 6, 1998, 3:00:00 AM5/6/98
to

In article 5103...@news.demon.co.uk, J...@opticon.demon.co.uk (John Goodwin) writes:
>
>On investigation it was found that EVERY SINGLE ONE of the goto's was
>there because a local function was required. ...

>It would have been nice to be able to replace them tidily with local
>functions but ....
>
>I've now read the rest of the thread, and realise that what I would
>like is a very small subset of what could be involved in providing
>local functions.
>
>To handle the cases I have in mind, you can put two restrictions on
>your local functions:
>
>1) No address taking.
>2) No other local function of the calling function is in scope
> inside the local function.

Perhaps you could explain why classes and/or function objects do
not solve the problem equally well.

The features already in C++ provide you with the functionality of
nested functions except for lexical closure. But neither the "goto"
solution nor your restricted version of nested functions provide
lexical closure either.

A class with member functions eliminates both the restrictions
above, and so would seem to be more powerful than what you are asking.

---
Steve Clamage, stephen...@sun.com
---

John Goodwin

unread,
May 7, 1998, 3:00:00 AM5/7/98
to

On 06 May 98 03:49:37 GMT, cla...@Eng.Sun.COM (Steve Clamage) wrote:

>In article 5103...@news.demon.co.uk, J...@opticon.demon.co.uk (John Goodwin) writes:
>>
>>On investigation it was found that EVERY SINGLE ONE of the goto's was
>>there because a local function was required. ...
>>It would have been nice to be able to replace them tidily with local
>>functions but ....
>>
>>I've now read the rest of the thread, and realise that what I would
>>like is a very small subset of what could be involved in providing
>>local functions.
>>
>>To handle the cases I have in mind, you can put two restrictions on
>>your local functions:
>>
>>1) No address taking.
>>2) No other local function of the calling function is in scope
>> inside the local function.
>
>Perhaps you could explain why classes and/or function objects do
>not solve the problem equally well.
>

OK

On general 'good practice' grounds::

It would require moving initialisations away from declarations.

On code efficiency grounds::
It would require an extra function call _every_ time the 'outer'
function was invoked.

On aesthetic grounds:

It would be verbose, messy, and tend to obsfucate the code.

Having said that, I agree that in _many_ cases, the methods you
suggest are quite satisfactory.

JG

Paul D. DeRocco

unread,
May 8, 1998, 3:00:00 AM5/8/98
to

J. Kanze wrote:
>
> "Paul D. DeRocco" <pder...@ix.netcom.com> writes:
>
> |> However, allowing recursion is only a small increase in
> |> complexity. The
> |> mechanism for remembering the context of the original call (i.e.,
> |> the
> |> address of the enclosing stack frame) is well-understood, and is
> |> even
> |> built into the x86 ENTER instruction.
>
> Correct. But none of the x86 C/C++ compilers I know use this
> instruction, because it costs more time than a simple PUSH EBP; MOV
> EBP,ESP sequence. (Or has this changed? It's been some time since I
> last looked at the generated output of a C/C++ compiler on an Intel
> processor.)

No, the compilers I use avoid ENTER/LEAVE. I'm merely saying that the
algorithm is well-known, and that ENTER would probably be more efficient
for nested functions, even though it isn't for non-nested ones.

--

Ciao,
Paul

0 new messages