disgusting compiler !! hahaha!!

J

Joe Pfeiffer

ralph said:
BartC said:
[...]

Making them *default* is stupid; the default should be zero based.

What's stupid is having the 1st, 2nd, 3rd and 4th elements of an array
indexed as 0, 1, 2 and 3.

I find arguments of the form "This is stupid!" "No, that's stupid!"
intensely boring.

Ha. I tend to agree with you.

I would add "disgusting" to the list as well. While the OP's post
generated fun and amusement for a few, over-all I found his whine list
rather silly, with the possible exception of Nested Functions*.

If those features really bother the OP, then Visual Basic 6.0 should
suit him very well.

-ralph
[*Not sure about the value of nested functions. Never used them
myself. The few examples I've run across give the appearance of being
useful - but limited to narrow solutions and always seem slightly
contrived. But that's just my opinion, thus quite safely ignored. <g>]

My experience, after spending my formative programming years in Pascal,
is that nested functions are one of those ideas that sound cool but turn
out to be nearly useless. Any of the versions of modules I've
encountered (including C's compilation of separate files with 'static'
meaning file scope) have been infinitely more useful in practice.
 
M

Malcolm McLean

My experience, after spending my formative programming years in Pascal,
is that nested functions are one of those ideas that sound cool but turn
out to be nearly useless. Any of the versions of modules I've
encountered (including C's compilation of separate files with 'static'
meaning file scope) have been infinitely more useful in practice.
A file is a natural unit. So is a node in a call tree. So the C way
of doing things is quite elegant.
A nested / local function makes the enclosing function harder to read,
and if it's allowed access to caller's local variables, it makes the
state of the variables hard to follow. You've got to check that
every call isn't modifying your loop counter.
There's also the issue of too much indentation. In C, indentation
represents either loop nesting level or alternative branching, so
two meanings. If you add indentation for nested functions, then you've
added another meaning, you've also blown up the number of indentations.
 
O

Osmium

CHIN Dihedral said:

Making them *default* is stupid; the default should be zero based.
What's stupid is having the 1st, 2nd, 3rd and 4th elements of an array
indexed as 0, 1, 2 and 3.

I find arguments of the form "This is stupid!" "No, that's stupid!"
intensely boring.



Ha. I tend to agree with you.



I would add "disgusting" to the list as well. While the OP's post

generated fun and amusement for a few, over-all I found his whine list

rather silly, with the possible exception of Nested Functions*.



If those features really bother the OP, then Visual Basic 6.0 should

suit him very well.



-ralph

[*Not sure about the value of nested functions. Never used them

myself. The few examples I've run across give the appearance of being

useful - but limited to narrow solutions and always seem slightly

contrived. But that's just my opinion, thus quite safely ignored. <g>]

I remember in the old 1-based VB days that a string longer than 64K will
result in undefined behaviors.

I remember when there was a guy at the gas station that not only put gas in
your gas tank he washed your windshield too!
 
J

Jorgen Grahn

Le 07/05/2014 20:37, Richard a écrit :

The APL Language had a global variable called "Origin" that could be
zero or one. According to this value array would start at 1 (default) or
at zero (if you set Origin to zero).

And the BASIC dialect I used briefly in the 80s had "OPTION BASE 0".
(And now that I google it, I see that Microsoft have it too, in Visual
Basic.)

Anyway, in my part of the programming world origin 0 won a long time
ago. All languages I'm likely to ever use do it that way.

/Jorgen
 
B

BartC

Malcolm McLean said:
A file is a natural unit. So is a node in a call tree. So the C way
of doing things is quite elegant.
A nested / local function makes the enclosing function harder to read,
and if it's allowed access to caller's local variables, it makes the
state of the variables hard to follow.

I have the same problem with nested directories on a disk drive.

Maybe going from one level to two levels was a good idea, to separate out
your projects.

But going to N levels? I've spent endless amounts of time after downloading
sources or applications trying to find where, for example, the include files
happen to be! With most directories only containing .. one other directory.

With one application, I've counted a maximum of 15 file levels:

\Arduino\hardware\arduino\firmwares\wifishield\wifiHD\src\SOFTWARE_FRAMEWORK\SERVICES\LWIP\lwip-1.3.2\src\include\ipv4\lwip

(Not including the root directory! And this is part of the support for a
weedy little microprocessor too. Notice 'arduino' and 'src' twice each.
Compiling any programs under this system -- there is a gcc compiler in the
tree, in \Arduino\hardware\tools\avr\avr\bin, only 6 levels deep -- involves
include paths up to 12 deep, many with repeated paths that include "." and
"..", and up to three "avr" for good measure!)

I think this is becoming like certain C include files: not designed to be
human readable anymore (which is important when trying to make sense of this
stuff from the outside). I'm not surprised there is so much bloatware when
projects get so big and disorganised that people give up on them.

Perhaps it's a good thing then that nested functions haven't really caught
on.
 
W

Walter Banks

Keith said:
It's not quite that simple. References to objects defined in a
containing function are non-trivial. There are at least two common ways
to implement this:

- A "static link", a pointer in a nested function to
the frame for its parent, forming a linked list for multiple
nesting levels); or

- A "display", an array of pointers to the frames for all lexically
enclosing functions.

In either case, the compiler has to generate extra code to maintain
these pointers. (No such extra code is needed for a program that
doesn't have nested functions, or possibly if it does but they don't
refer to parent declarations).

And C function pointers make it possible to call a nested function when
its parent is not executing. If the nested function refers to
declarations in its parent, presumably the behavior is undefine (or, as
the gcc documentation puts it, "all hell will break loose").

An example:

#include <stdio.h>

void (*funcptr)(void);

static void outer(void) {
int outer_var = 42;
void inner(void) {
printf("in inner, outer_var = %d\n", outer_var);
}
funcptr = inner;
puts("outer");
inner();
}

int main(void) {
outer();
funcptr();
}

When I run this program on my system, the output is:

outer
in inner, outer_var = 42
in inner, outer_var = 42

but only by luck; the indirect call has undefined behavior since
outer_var doesn't exist at that point.

I will answer this with more detail later but the function
pointer in your example is the same as taking the pointer
address of a variable and then referencing it out of scope.

inner scope is limited to in your case the function
outer.

The point of nested functions is a function that has
limited scope in the same way as local variables
have limited scope.

w..
 
W

Walter Banks

David said:
I often use languages that support local functions - Pascal and Python.
It is rare that I find them useful, except for lambda functions in
Python. When programming in C, I usually use gcc which has support for
local functions, but I have never felt it would significantly improve my
programs. I think that in most cases where local functions really would
make a difference to the structure and quality of the program, you are
probably better off using C++ with access to class member functions
(including local classes) and lambdas.


Local functions can often be implemented easily - they can be treated as
a normal "static" function by the compiler. But sometimes the
interaction between variables in the outer function and their usage
inside the local function can make a real mess - the generated static
function needs pointers to outer function variables, variables that used
to be in registers now need to go on the stack, and your optimisation is
lost. And when people start doing "clever" things like taking the
address of the local function, it gets worse - on gcc, this is
implemented using "trampolines" which are run-time generated code put on
the stack.

All in all, general local functions are a pain - and if you restrict
them too much (such as disallowing their address to be taken), you lose
many of the possible uses (such as for a sorting function for qsort()).

Although gcc has nested functions (and has had for many years - they
needed the functionality for languages such as Pascal and Ada), they are
seldom used in C. C++ lambdas and class members are usually a better
choice if you need such structures.

(Note - I don't know the details of /why/ trampolines are needed for
nested functions in C, while they are not needed for C++ lambdas.)

Nested functions are just a scoping issue both for use and implementation.
Think of all of the expected expected restrictions on local variables.
We can create pointers to local variables and use those pointers as
long as the original variable is in scope. Inner functions can have
pointers to as long as scope is respected. The scoping restriction
can be an advantage for example function pointers with a sort.

I have done implementations for both C compilers and and other
languages for nested functions. I have not seen a particular problem
or optimization problems during implementation. The nested function
becomes part of the application structure and is dealt with as such.

My usage experience is mostly using pascal where I have tended to
use local functions a lot to isolate the logic of function implementation
from the details. Maybe as many as half of the local functions were
only called once in a function.

w..
 
T

Thomas Richter

Am 09.05.2014 20:41, schrieb Keith Thompson:
It's not quite that simple. References to objects defined in a
containing function are non-trivial. There are at least two common ways
to implement this:

- A "static link", a pointer in a nested function to
the frame for its parent, forming a linked list for multiple
nesting levels); or

- A "display", an array of pointers to the frames for all lexically
enclosing functions.

In either case, the compiler has to generate extra code to maintain
these pointers. (No such extra code is needed for a program that
doesn't have nested functions, or possibly if it does but they don't
refer to parent declarations).

Why? On a typical implementation on a typical CPU, such objects are
placed on the hardware stack, along with the return address and (some or
all) of the arguments of the called function. Since the stack frame of
the calling function is known, and the stack frame of the called
function is known, the objects of the calling function can be accessed
from the called function just by an offset from the current stack frame,
knowing the size of the objects on the stack. Thus, there is no overhead
necessary.
And C function pointers make it possible to call a nested function when
its parent is not executing. If the nested function refers to
declarations in its parent, presumably the behavior is undefine (or, as
the gcc documentation puts it, "all hell will break loose").

That seems to be necessary, i.e. function pointers to internal functions
cannot be exported to the outside, unless the "inner function" does not
refer to the stack frame of the calling function. (Such inner functions
could be declared "static", i.e. then inner functions would just have
internal linkage and their own name space).

Greetings,
Thomas
 
D

David Brown

GCC uses trampolines for C because taking the address of the nested
function must result in a function pointer. C++ doesn't need that for
lambdas since they are objects instead of functions.

Thanks - that makes sense. To make the local function work, it needs a
certain amount of context (pointers to local data, etc.). When you have
an object, such as a lambda, that context can be part of the object -
when you only have a standard function pointer, you need a trampoline.
 
D

David Brown

There is an even simpler implementation where the local function can't
access the local variables of its enclosing function. (I found out I could
do this simply by commenting out a check on functions being defined inside
another.)

The local function is compiled as though it was outside. The sole
advantage is that the name of the local function only has a scope within
its enclosing function. (And the same name can be reused inside another
function, if the compiler provides a unique name for each nested function.)

Yes, such limited local functions are easy to implement - but they are
also of limited use. The only use they have is limited scope of the
name, but since it is easy to get the same effect by using a normal
static function and simply not calling it from other functions, it is
not a big benefit. It would have some uses - just like function-local
static variables - but the real power of local functions comes when you
have access to the local variables of the surrounding scope.
 
G

glen herrmannsfeldt

Richard Damon said:
(snip)
(snip)
(snip)
If the nested functions can only be directly called by their surrounding
function, then maybe it is simple, but the more normal use case is for
these functions to be used for callback functions for functions that the
surrounding function calls (like the comparison function for qsort() ).
For this sort of use case, granting access to the surrounding functions
local variables isn't trivial, and may impose a cost on all function
pointers.

Even then, it shouldn't be so hard until you add recursion, but then
C always allows recursion, so you have to handle it.

You have to find the right stack frame for the appropriate instance
of the calling function.

This was all figured out when the first PL/I compiler was written,
as the language was defined to allow it. Even more, PL/I allows
for multitasking, so there could also be different tasks
running the same function at the same time.

-- glen
 
B

BartC

glen herrmannsfeldt said:
Even then, it shouldn't be so hard until you add recursion, but then
C always allows recursion, so you have to handle it.

I'm not sure why recursion comes into it. C functions will have local frame
variables allocated on a stack whether recursive or not (they might be
optimised out, but you can't depend on that).
You have to find the right stack frame for the appropriate instance
of the calling function.

The problem is when you have function D inside C inside B inside A.

You call A() which calls B() which calls C() which passes a pointer to D to
some outside code.

Later on, after A() has returned, a call is made directly to D().

The problem now is that those locals belonging to A or B or C do not exist,
yet D might try and reference them. Some of those locals might depend on
parameters passed to A or B or C, so it can't just create suitable dummy
locals to make do.

(What D can access with no problem, is anything that isn't a local frame
variable belonging to A or B or C: typedefs, constants (ideally, proper
named constants that I've proposed elsewhere), static variables and enums.)
This was all figured out when the first PL/I compiler was written,
as the language was defined to allow it. Even more, PL/I allows
for multitasking, so there could also be different tasks
running the same function at the same time.

I wonder why such a language never really took off?
 
W

Walter Banks

David said:
Yes, such limited local functions are easy to implement - but they are
also of limited use. The only use they have is limited scope of the
name, but since it is easy to get the same effect by using a normal
static function and simply not calling it from other functions, it is
not a big benefit. It would have some uses - just like function-local
static variables - but the real power of local functions comes when you
have access to the local variables of the surrounding scope.

The implementation I did was to essentially extend variable scoping. It
potentially allowed local functions to be declared anywhere a local
variable could be declared.

The code in the local functions in my implementation had access to
all variables in scope global or local to the host function. There
are some interesting uses for local functions. They require language
support and at this point C has some implementations of local function
but the language doesn't support them. Years ago I did fly it by a
WG-14 meeting and there was essentially no support for local
functions to be added to the language.

w..
 
W

Walter Banks

glen said:
Even then, it shouldn't be so hard until you add recursion, but then
C always allows recursion, so you have to handle it.

You have to find the right stack frame for the appropriate instance
of the calling function.

This was all figured out when the first PL/I compiler was written,
as the language was defined to allow it. Even more, PL/I allows
for multitasking, so there could also be different tasks
running the same function at the same time.

Recursion implementation in nested functions is mostly scope
management.

PL/I was one of those languages that was ahead of its time. It was
developed about the same time as basic at a time when fortran was
king.

w..
 
W

Walter Banks

BartC said:
Walter Banks said:
David Brown wrote:
I can't make sense of you here. I said specifically that arrays with
ranges or enumerated types as indexes "would lead to clearer code and
better compile-time checking for ranges and types". I am not "trying to
deny this to C programmers" - I think it would be a useful enhancement
to the language, which is why I wrote about it. But I also think that
it is unlikely to become a part of C, especially as you can implement it
in C++.

C as a language could use range syntax (as if it really need a larger
grammar)
'a' .. 'z' type syntax has a lot of uses and commonly found in code so
that a
tight compiler implementation would be useful.

For example

if ( a in [-5..46]) // stolen from pascal
{
. . .
}

Similarly with switch case

case ['0'..'9'] :
or
case [100..200] :

Even simpler is to use:

if (a in -5..46) ...

ie. matching against a range (instead of being a member of a set as I
assumed the [...] construction was). This would directly map to:

if (a>=-5 && a<=46) ...

(and hopefully implemented so that a is evaluated once).
Many compilers already have the code generation for these constructs and
generate it by pattern matching to the most common source constructs.

The argument against ranges for switch cases, when it frequently comes up in
c.l.c., is that someone might be tempted to write:

case 'A'..'Z':

instead of:

case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': case
'H':
case 'I': case 'J': case 'K': case 'L': case 'M': case 'N': case 'O': case
'P':
case 'Q': case 'R': case 'S': case 'T': case 'U': case 'V': case 'W': case
'X':
case 'Y': case 'Z':

(and the former does have a certain conciseness about it). This won't be
quite right if it happens that EBCDIC is being used. But if that is not the
case, or letters are not involved, then it is silly to have to type out
dozens of consecutive case labels.

Good point, set vs range district ion. My pascal background is showing.
The counter argument to your case 'A'..'Z' example is C applications
are actually rarely ported. From a language point of view this is not a
valid argument from an application developer point of view it is a very
readable short cut.

'A'..'Z' is clear the full english alphabet range and

'A'..'K', 'M'..'Z' Christmas list is a lot clearer than the alternative.


w..


w..
 
K

Keith Thompson

Richard Damon said:
If the nested functions can only be directly called by their surrounding
function, then maybe it is simple, but the more normal use case is for
these functions to be used for callback functions for functions that the
surrounding function calls (like the comparison function for qsort() ).
For this sort of use case, granting access to the surrounding functions
local variables isn't trivial, and may impose a cost on all function
pointers.

Or nested functions can call each other:

void outer(void) {
int foo;
void inner1(void) {
/* ... */
}
void inner2(void) {
/* ... */
}
/* ... */
}

If inner1 refers to foo, it may have been called directly by
outer (going up one level of stack frames) or by inner2 (going
up two levels). Or it could have been called recursively, so
you might have to search up the stack for an arbitrary number of
levels before finding a stack frame that belongs to the innermost
currently active call of outer (if the compiler uses a static link
rather than a display).

I don't suggest that this is an insurmountable problem (and it's
already been solved for languages that have nested functions,
and by gcc for its extended dialect of C), but it is non-trivial.

As for using function pointers to call a nested function after its
parent has terminated, that's not a major problem; it's similar to
referring to local variables after the function that defines them
has terminated. *If* nested function definitions were added to the
language, I expect the standard would merely add a few words to say
that such calls have undefined behavior. (A particular compiler
might optionally add a check to detect such calls at run time and/or
warn about the possibilty at compile time.) It would be simpler
to say that *all* such calls have undefined behavior, whether the
inner function refers to objects defined by its parent or not.

But the committee has shown no particular interest in adding this
to the language.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
474,073
Messages
2,570,539
Members
47,197
Latest member
NDTShavonn

Latest Threads

Top