Discussion:
Proposal to remove forward declaration requirements
(too old to reply)
Rick C. Hodgin
2015-11-10 22:27:47 UTC
Permalink
Raw Message
I'd like to propose that the next standard include a relaxation which
allows the need for forward declarations to be removed, to be replaced
with an option allowing a multi-pass compilation to resolve declarations
unknown until subsequent source code is processed.

Thank you for considering this feature which should've been included
in the C71 standard.

Best regards,
Rick C. Hodgin
Jakob Bohm
2015-11-11 02:41:27 UTC
Permalink
Raw Message
Post by Rick C. Hodgin
I'd like to propose that the next standard include a relaxation which
allows the need for forward declarations to be removed, to be replaced
with an option allowing a multi-pass compilation to resolve declarations
unknown until subsequent source code is processed.
Please don't (for multiple reasons).


Enjoy

Jakob
--
Jakob Bohm, CIO, Partner, WiseMo A/S. https://www.wisemo.com
Transformervej 29, 2860 Søborg, Denmark. Direct +45 31 13 16 10
This public discussion message is non-binding and may contain errors.
WiseMo - Remote Service Management for PCs, Phones and Embedded
Kaz Kylheku
2015-11-11 02:44:40 UTC
Permalink
Raw Message
Post by Rick C. Hodgin
I'd like to propose that the next standard include a relaxation which
allows the need for forward declarations to be removed, to be replaced
with an option allowing a multi-pass compilation to resolve declarations
unknown until subsequent source code is processed.
Thank you for considering this feature which should've been included
in the C71 standard.
There was no C in 1971. The C from 1975 or thereabouts had such a feature,
at least for functions:

main()
{
int x = never_seen_before();
/* x gets 42 */
}

/* later in the same file, or in a different one */
never_seen_before()
{
return 42;
}

This is deprecated now.
Keith Thompson
2015-11-11 03:19:11 UTC
Permalink
Raw Message
Post by Kaz Kylheku
Post by Rick C. Hodgin
I'd like to propose that the next standard include a relaxation which
allows the need for forward declarations to be removed, to be replaced
with an option allowing a multi-pass compilation to resolve declarations
unknown until subsequent source code is processed.
Thank you for considering this feature which should've been included
in the C71 standard.
There was no C in 1971. The C from 1975 or thereabouts had such a feature,
main()
{
int x = never_seen_before();
/* x gets 42 */
}
/* later in the same file, or in a different one */
never_seen_before()
{
return 42;
}
This is deprecated now.
It's not merely deprecated. Calling an undeclared function has been
illegal (a constraint violation) since C99.
--
Keith Thompson (The_Other_Keith) kst-***@mib.org <http://www.ghoti.net/~kst>
Working, but not speaking, for JetHead Development, Inc.
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
Rick C. Hodgin
2015-11-11 05:10:36 UTC
Permalink
Raw Message
Post by Kaz Kylheku
There was no C in 1971.
C was released in 1972. My point was that it should
have been there in drafts and discussions before
it was ever released. I.e. from day one following
the, "You know what we should do?" moment
where discussions about writing C began.

Best regards,
Rick C. Hodgin
Kaz Kylheku
2015-11-11 16:01:51 UTC
Permalink
Raw Message
Post by Rick C. Hodgin
Post by Kaz Kylheku
There was no C in 1971.
C was released in 1972.
I misremembered the dates a bit; so I went back to Dennis Ritchie's paper,
"Development of C" [https://www.bell-labs.com/usr/dmr/www/chist.pdf]:

Around 1971, it was still the B language, I think:

"In 1971 I began to extend the B language by adding a character type and
also rewrote its compiler to generate PDP-11 machine instructions instead
of threaded code. Thus the transition from B to C was contemporaneous with
the creation of a compiler capable of producing programs fast and small
enough to compete with assembly language. I called the slightly-extended
language NB, for ‘new B.’"

[ ... ]

"By early 1973, the essentials of modern C were complete."

[ ... ]

"During 1973-1980, the language grew a bit: the type structure gained
unsigned, long, union, and enumeration types, and structures became nearly
first-class objects (lacking only a notation for literals)."
Rick C. Hodgin
2015-11-11 16:04:39 UTC
Permalink
Raw Message
Post by Kaz Kylheku
Post by Rick C. Hodgin
Post by Kaz Kylheku
There was no C in 1971.
C was released in 1972.
I misremembered the dates a bit; so I went back to Dennis Ritchie's paper,
"In 1971 I began to extend the B language by adding a character type and
also rewrote its compiler to generate PDP-11 machine instructions instead
of threaded code. Thus the transition from B to C was contemporaneous with
the creation of a compiler capable of producing programs fast and small
enough to compete with assembly language. I called the slightly-extended
language NB, for 'new B.'"
[ ... ]
"By early 1973, the essentials of modern C were complete."
[ ... ]
"During 1973-1980, the language grew a bit: the type structure gained
unsigned, long, union, and enumeration types, and structures became nearly
first-class objects (lacking only a notation for literals)."
Yes. In 1971, as he was extending B to pre-C, he should've been
considering not having forward declaration requirements, but only
the ability to possess them. They are often useful, but should not
always be required.

Best regards,
Rick C. Hodgin
jacob navia
2015-11-11 12:26:27 UTC
Permalink
Raw Message
Post by Rick C. Hodgin
I'd like to propose that the next standard include a relaxation which
allows the need for forward declarations to be removed, to be replaced
with an option allowing a multi-pass compilation to resolve declarations
unknown until subsequent source code is processed.
Thank you for considering this feature which should've been included
in the C71 standard.
Best regards,
Rick C. Hodgin
Suppose this:

someFn("abc");

No declarations of "someFn" anywhere in the compilation unit since it is
defined somewhere else, i.e. in another source file.

That would be legal?

In that case we have to give up compilation by modules and compile all
the source files at each time since we can never know when something has
changed in module A that affects modules B and C that need recompilation...

Besides that small problem, the reader of the code has NO CLUE as to
where in the source code the definition can be... Just start grepping
around, filter the tons of lines to find the definition...
Rick C. Hodgin
2015-11-11 12:54:23 UTC
Permalink
Raw Message
Post by jacob navia
Post by Rick C. Hodgin
I'd like to propose that the next standard include a relaxation which
allows the need for forward declarations to be removed, to be replaced
with an option allowing a multi-pass compilation to resolve declarations
unknown until subsequent source code is processed.
Thank you for considering this feature which should've been included
in the C71 standard.
Best regards,
Rick C. Hodgin
someFn("abc");
No declarations of "someFn" anywhere in the compilation unit since it is
defined somewhere else, i.e. in another source file.
That would be legal?
Under the proposed relaxation, it would not be legal. The use of
anything in a source file would be required to be known to the source
or one of its include files, either through forward declarations, or
to be found at a point later in the source file than where it's first
referenced.

Note also that this is a relaxation of the REQUIREMENT of having
forward declarations. It is not a proposal to remove them completely.
Only that in many cases they are not needed.
Post by jacob navia
In that case we have to give up compilation by modules and compile all
the source files at each time since we can never know when something has
changed in module A that affects modules B and C that need recompilation...
I do not propose that change. I only that on source files like this:

int main(int argc, char* argv[])
{
printf("%s\n", my_function());
exit(0);
}

char* my_function(void)
{
return("Hello, world!\n");
}

...that I don't need to forward-declare my_function() because it's
right there in the source file, just declared later on. I'm fully
prepared to take the mild performance hit in compilation time to
gain such a feature. And I think every other C developer out there
is as well because such a feature would ONLY be required when there
were unknowns found in the first pass, which must be resolved in a
second pass. And in the case of legitimate unknowns (something's
wrong with the source file, something's missing) then it will result
in an error which will terminate the compilation anyway. And in the
case of illegitimate unknowns (it is known later in the file, just
not at the point of first use), then a mild performance hit to parse
back through the second pass on those lines... it doesn't seem like
too much to bear, or inquire for.
Post by jacob navia
Besides that small problem, the reader of the code has NO CLUE as to
where in the source code the definition can be... Just start grepping
around, filter the tons of lines to find the definition...
Knowledge of the system design a developer works on is crucial to
achieving success in development. When that knowledge is not present,
grepping around is the appropriate action.

Best regards,
Rick C. Hodgin
Wojtek Lerch
2015-11-12 05:04:04 UTC
Permalink
Raw Message
On 11/11/2015 7:54 AM, Rick C. Hodgin wrote:
...
...
Post by Rick C. Hodgin
printf("%s\n", my_function());
...
Post by Rick C. Hodgin
char* my_function(void)
What about things that are not functions?

int main( void ) {
next = getfoo();
var.next = next;
struct foo *next;
}

struct foo *getfoo( void ) {
return malloc( sizeof(struct foo) );
}

struct foo var;

struct foo {
struct foo *next;
};

m***@yahoo.co.uk
2015-11-11 12:56:23 UTC
Permalink
Raw Message
Post by jacob navia
Post by Rick C. Hodgin
I'd like to propose that the next standard include a relaxation which
allows the need for forward declarations to be removed, to be replaced
with an option allowing a multi-pass compilation to resolve declarations
unknown until subsequent source code is processed.
someFn("abc");
No declarations of "someFn" anywhere in the compilation unit since it is
defined somewhere else, i.e. in another source file.
That would be legal?
If I understand the OP correctly: No, that would not be legal.

What would be legal, is call somefn at one point in the compilation
unit, and then at some later point *in the same compilation unit*
define somefn.

That would allow people to write the external functions first,
calling the local static functions which are defined lower down the
file. I quite often end up defining the local static functions first
because I'm too lazy to write out separate declarations.

It is clearly *possible* (C++ manages something similar in the definition
of inline member functions - they can use other members that are declared
lower down). I really can't see much demand for it though.
Kaz Kylheku
2015-11-11 16:12:32 UTC
Permalink
Raw Message
Post by m***@yahoo.co.uk
If I understand the OP correctly: No, that would not be legal.
What would be legal, is call somefn at one point in the compilation
unit, and then at some later point *in the same compilation unit*
define somefn.
That would allow people to write the external functions first,
calling the local static functions which are defined lower down the
file.
Anything which encourages this awful style of arranging a program, sets
me dead against itself as a matter of a knee-jerk reflex. :)
Rick C. Hodgin
2015-11-11 16:23:02 UTC
Permalink
Raw Message
Post by Kaz Kylheku
Post by m***@yahoo.co.uk
If I understand the OP correctly: No, that would not be legal.
What would be legal, is call somefn at one point in the compilation
unit, and then at some later point *in the same compilation unit*
define somefn.
That would allow people to write the external functions first,
calling the local static functions which are defined lower down the
file.
Anything which encourages this awful style of arranging a program, sets
me dead against itself as a matter of a knee-jerk reflex. :)
Which awful style is that, Kaz?

// No forward declaration requirement:
main(); // Calls support1();
support1(); // Calls support2();
support2(); // No other calls

Instead of:

// No forward declaration requirement
support2(); // No other calls
support1(); // Calls support2();
main(); // Calls support1();

Why would anyone want to write code backwards?

Or:

// Forward declaration for support1();
// Forward declaration for support2();
main(); // Calls support1();
support1(); // Calls support2();
support2(); // No other calls

So now I've added unnecessary repetition and redundancy, coupled to
the need to keep two things in sync while I'm editing and maintaining
my source code.

?? Better ?? No, sir. It's catering to 1970s hardware, and not to
2015s hardware. It's catering to the requirements of lazy compiler
writers, and not to the needs of the many non-compiler-writer living,
breathing human beings who must also write C code in their lives.

Our goals should be to meet people's needs, not machine's needs, when
there is no machine need which supersedes people needs.

Best regards,
Rick C. Hodgin
Kaz Kylheku
2015-11-11 17:18:11 UTC
Permalink
Raw Message
Post by Rick C. Hodgin
Post by Kaz Kylheku
Post by m***@yahoo.co.uk
If I understand the OP correctly: No, that would not be legal.
What would be legal, is call somefn at one point in the compilation
unit, and then at some later point *in the same compilation unit*
define somefn.
That would allow people to write the external functions first,
calling the local static functions which are defined lower down the
file.
Anything which encourages this awful style of arranging a program, sets
me dead against itself as a matter of a knee-jerk reflex. :)
Which awful style is that, Kaz?
The one where you call a something before defining it.

It wouldn't be as awful though if it were *enforced* as the one way to
do it. What we don't want is to support an ad-hoc mess where the definition
can be hiding anywhere among the uses, so that when you're considering a
particular use, you don't know in which direction the definition lies.
Post by Rick C. Hodgin
main(); // Calls support1();
support1(); // Calls support2();
support2(); // No other calls
This will devolve over time, e.g:

support3() {...} // calls support2();
main(); // Calls support1();
support1() {...} // Calls support3();
support2() {...}

Unfortunately this tends to happen in translation units with many public
functions, which are also used internally. The header file supplies
all the declarations beforehand, and so things get ... out of hand.

From time to time, it is good to do some housekeeping to put the functions
in order; on the other hand, that introduces diffs into the version history
that can cause gratuitous merge conflicts.
Post by Rick C. Hodgin
// No forward declaration requirement
support2(); // No other calls
support1(); // Calls support2();
main(); // Calls support1();
This latter one is the best way to write computer programs
in any language. Low level routines first, then top.

It follows how we design systems, and how our best minds think.

The language doesn't require you to write any forward declarations
if you follow this way --- except, of course, when you introduce nontrivial
loops into the call graph with mutual recursion. That's what forward
declarations are for.

Ideally, the presence of declarations in an impelmentation file signals to the
reader "recursion among functions is going on here".
Rick C. Hodgin
2015-11-11 17:38:13 UTC
Permalink
Raw Message
Post by Kaz Kylheku
Post by Rick C. Hodgin
Post by Kaz Kylheku
Post by m***@yahoo.co.uk
If I understand the OP correctly: No, that would not be legal.
What would be legal, is call somefn at one point in the compilation
unit, and then at some later point *in the same compilation unit*
define somefn.
That would allow people to write the external functions first,
calling the local static functions which are defined lower down the
file.
Anything which encourages this awful style of arranging a program, sets
me dead against itself as a matter of a knee-jerk reflex. :)
Which awful style is that, Kaz?
The one where you call a something before defining it.
It wouldn't be as awful though if it were *enforced* as the one way to
do it. What we don't want is to support an ad-hoc mess where the definition
can be hiding anywhere among the uses, so that when you're considering a
particular use, you don't know in which direction the definition lies.
Post by Rick C. Hodgin
main(); // Calls support1();
support1(); // Calls support2();
support2(); // No other calls
support3() {...} // calls support2();
main(); // Calls support1();
support1() {...} // Calls support3();
support2() {...}
Unfortunately this tends to happen in translation units with many public
functions, which are also used internally. The header file supplies
all the declarations beforehand, and so things get ... out of hand.
From time to time, it is good to do some housekeeping to put the functions
in order; on the other hand, that introduces diffs into the version history
that can cause gratuitous merge conflicts.
Post by Rick C. Hodgin
// No forward declaration requirement
support2(); // No other calls
support1(); // Calls support2();
main(); // Calls support1();
This latter one is the best way to write computer programs
in any language. Low level routines first, then top.
It follows how we design systems, and how our best minds think.
No. I encounter your position and that explanation often in computer
programming, but it is in no way universe. It is also not how our minds
think. We think in broad strokes first, then details. We look to
understand the big picture, then if we NEED to, only then do we go down
into details.
Post by Kaz Kylheku
The language doesn't require you to write any forward declarations
if you follow this way --- except, of course, when you introduce nontrivial
loops into the call graph with mutual recursion. That's what forward
declarations are for.
Ideally, the presence of declarations in an impelmentation file signals to the
reader "recursion among functions is going on here".
Best regards,
Rick C. Hodgin
Rick C. Hodgin
2015-11-11 17:40:47 UTC
Permalink
Raw Message
Post by Rick C. Hodgin
Post by Kaz Kylheku
This latter one is the best way to write computer programs
in any language. Low level routines first, then top.
It follows how we design systems, and how our best minds think.
No. I encounter your position and that explanation often in computer
programming, but it is in no way universe. It is also not how our minds
Should be "it is in no way universal."
Post by Rick C. Hodgin
think. We think in broad strokes first, then details. We look to
understand the big picture, then if we NEED to, only then do we go down
into details.
Best regards,
Rick C. Hodgin
Kaz Kylheku
2015-11-11 18:31:34 UTC
Permalink
Raw Message
Post by Rick C. Hodgin
Post by Kaz Kylheku
This latter one is the best way to write computer programs
in any language. Low level routines first, then top.
It follows how we design systems, and how our best minds think.
No. I encounter your position and that explanation often in computer
programming, but it is in no way universe. It is also not how our minds
think. We think in broad strokes first, then details. We look to
understand the big picture, then if we NEED to, only then do we go down
into details.
Ken Thompson said this in an interview once:

[Interviewer:] Your nominators and endorsers for the Kanai Award consistently
characterized your work as simple yet powerful. How do you discover such
powerful abstractions?

Thompson: It is the way I think. I am a very bottom-up thinker. If you give
me the right kind of Tinker Toys, I can imagine the building. I can sit there
and see primitives and recognize their power to build structures a half mile
high, if only I had just one more to make it functionally complete. I can see
those kinds of things.

The converse is true, too, I think. I can't from the building imagine the
Tinker Toys. When I see a top-down description of a system or language that
has infinite libraries described by layers and layers, all I just see is a
morass. I can't get a feel for it. I can't understand how the pieces fit; I
can't understand something presented to me that's very complex. Maybe I do
what I do because if I built anything more complicated, I couldn't understand
it. I really must break it down into little pieces.

[Unix and Beyond: an Interview with Ken Thompson]
Rick C. Hodgin
2015-11-11 19:07:34 UTC
Permalink
Raw Message
Post by Kaz Kylheku
Post by Rick C. Hodgin
Post by Kaz Kylheku
This latter one is the best way to write computer programs
in any language. Low level routines first, then top.
It follows how we design systems, and how our best minds think.
No. I encounter your position and that explanation often in computer
programming, but it is in no way universe. It is also not how our minds
think. We think in broad strokes first, then details. We look to
understand the big picture, then if we NEED to, only then do we go down
into details.
[Interviewer:] Your nominators and endorsers for the Kanai Award consistently
characterized your work as simple yet powerful. How do you discover such
powerful abstractions?
Thompson: It is the way I think. I am a very bottom-up thinker. If you give
me the right kind of Tinker Toys, I can imagine the building. I can sit there
and see primitives and recognize their power to build structures a half mile
high, if only I had just one more to make it functionally complete. I can see
those kinds of things.
The converse is true, too, I think. I can't from the building imagine the
Tinker Toys. When I see a top-down description of a system or language that
has infinite libraries described by layers and layers, all I just see is a
morass. I can't get a feel for it. I can't understand how the pieces fit; I
can't understand something presented to me that's very complex. Maybe I do
what I do because if I built anything more complicated, I couldn't understand
it. I really must break it down into little pieces.
[Unix and Beyond: an Interview with Ken Thompson]
Tinker Toys to buildings is one thing. Software details to higher
level constructs is another, unless you're talking about, as I presume
Thompson is here, the fundamental abilities which carry out very
specific workloads, such as the various unix script sand commands.

But jumping into a new project and trying to look at the renderPixel()
function without understanding its contextual usage is an issue. It
will not take on any meaning if you don't know why it exists.

Best regards,
Rick C. Hodgin
Rick C. Hodgin
2015-11-11 19:10:44 UTC
Permalink
Raw Message
Post by Kaz Kylheku
Post by Rick C. Hodgin
Post by Kaz Kylheku
This latter one is the best way to write computer programs
in any language. Low level routines first, then top.
It follows how we design systems, and how our best minds think.
No. I encounter your position and that explanation often in computer
programming, but it is in no way universe. It is also not how our minds
think. We think in broad strokes first, then details. We look to
understand the big picture, then if we NEED to, only then do we go down
into details.
[Interviewer:] Your nominators and endorsers for the Kanai Award consistently
characterized your work as simple yet powerful. How do you discover such
powerful abstractions?
Thompson: It is the way I think. I am a very bottom-up thinker. If you give
me the right kind of Tinker Toys, I can imagine the building. I can sit there
and see primitives and recognize their power to build structures a half mile
high, if only I had just one more to make it functionally complete. I can see
those kinds of things.
The converse is true, too, I think. I can't from the building imagine the
Tinker Toys. When I see a top-down description of a system or language that
has infinite libraries described by layers and layers, all I just see is a
morass. I can't get a feel for it. I can't understand how the pieces fit; I
can't understand something presented to me that's very complex. Maybe I do
what I do because if I built anything more complicated, I couldn't understand
it. I really must break it down into little pieces.
[Unix and Beyond: an Interview with Ken Thompson]
BTW, I think Thompson changed later in life:

-----
From Google's "Go" Language FAQ:

https://golang.org/doc/faq

Q: "What are the guiding principles in the [Go] design?

A: "Programming today involves too much bookkeeping, repetition,
and clerical work. As Dick Gabriel says, ''Old programs read like
quiet conversations between a well-spoken research worker and a
well-studied mechanical colleague, not as a debate with a compiler.
Who'd have guessed sophistication bought such noise?'' The
sophistication is worthwhile -- no one wants to go back to the old
languages -- but can it be more quietly achieved?

"Go attempts to reduce the amount of typing in both senses of the
word. Throughout its design, we have tried to reduce clutter and
complexity. There are no forward declarations and no header files;
everything is declared exactly once. Initialization is expressive,
automatic, and easy to use. Syntax is clean and light on keywords."

-----
"Everything is declared exactly once."

Ken Thompson is one of three original authors of the Go Language:
https://en.wikipedia.org/wiki/Go_%28programming_language%29

They took ideas from C, but them migrated them to make more sense
to developers. One of the first things to go was the manual need
for headers and forward declarations.

Best regards,
Rick C. Hodgin
Kaz Kylheku
2015-11-11 16:10:43 UTC
Permalink
Raw Message
Post by jacob navia
Post by Rick C. Hodgin
I'd like to propose that the next standard include a relaxation which
allows the need for forward declarations to be removed, to be replaced
with an option allowing a multi-pass compilation to resolve declarations
unknown until subsequent source code is processed.
Thank you for considering this feature which should've been included
in the C71 standard.
Best regards,
Rick C. Hodgin
someFn("abc");
No declarations of "someFn" anywhere in the compilation unit since it is
defined somewhere else, i.e. in another source file.
That would be legal?
It is valid in the 1990 dialect of ISO C, its predecessors, and
related historic dialects.
Post by jacob navia
In that case we have to give up compilation by modules and compile all
the source files at each time since we can never know when something has
Yet in those languages, it wasn't the case.
Post by jacob navia
changed in module A that affects modules B and C that need recompilation...
someFn("abc") implies that there exists somewhere a function
"int someFn(char *)" or perhaps "int someFn(const char *)".

If this isn't the case, recompiling the caller will not help, because
the caller doesn't have a matching declaration; it will still be incorrect
after recompilation.

If it *is* the case that someFn is defined in the expected way, but changes are
made to someFn which alter its *behavior*, there is no need to recompile
the caller; no aspect of the compile-time dependency has changed.

It's exactly the same situation as if we had a "someFn.h" header file
declaring the function, but made a change which only affects "someFn.c"
and not "someFn.h". In that case, only "someFn.c" has to be recompiled.

The problem with implicit declaration is that if we change the *interface*
of someFn, we do not get a diagnostic for all the places which are calling it
wrong.
Post by jacob navia
Besides that small problem, the reader of the code has NO CLUE as to
where in the source code the definition can be... Just start grepping
around, filter the tons of lines to find the definition...
Mandatory declarations do not help with this issue in any way.
Loading...