some well known stupidness in c99

R

Rui Maciel

fir said:
#define is not acceptable by me I dislike
using preprocessor and use only few includes
(do not like it either) and nothing more of it

It sounds like you don't do any work with C or C++.


Rui Maciel
 
R

Rui Maciel

fir said:
defines are not such stupid but preprocesor
can be consideren not as part of c but
<snip/>

The C preprocessor is C. It is defined in the C standard. It can't be more
C than that.


Rui Maciel
 
J

Joachim Schmitz

fir wrote:
When I wrote some messages on this group
(not many times that was my third visit here)
really I often met a ppl which I see try to make 'much effort to not
understand me'
(Really, I see suc thing here)

It would be hard to explain such basic
things why

coinst int table_max = 4096;

is better than

enum { table_max = 4096 }

How can one syntax error be better than another? ;-)
 
K

Keith Thompson

fir said:
It is sad to say so but there is some real and terrible stupidnes in
c99. When i wrote programs in c (and I am involved c user) I just use
const int for array boundaries but in c99 (and some previous too,
terribly) it will not compile (see for example
http://stackoverflow.com/questions/1712592/variably-modified-array-at-file-scope
) It is terribly wrong thing for ppl who just wants to use const int
to this purposes and not to use preprocesor - as I do. It is terribly
stupidness in my opinion, it have to be changed.

C is not, never has been, and never will be a 100% cleanly designed
language. Its own creator, Dennis Ritchie, wrote that "C is quirky,
flawed, and a tremendous success."

It has evolved over decades from earlier versions, and from earlier
languages (B, BCPL), and most updates to the language have carefully
maintained (mostly) backward compatibility, to avoid breaking existing
code.

Personally, I agree that it would be nice if C treated const definitions
with constant initializers the way C++ does, so that given

const int answer = 42;

answer would be a constant expression, and I would support such a
change in a future revision. Kenneth Brody pointed out a case where
such a change could break existing code:

===== file1.c

const int table_max = 100;

===== file2.c

extern const int table_max;

This could probably be worked around by making the definition introduce
a constant expression *and* a read-only object. Or, I suppose, the
commitee could decide that it's ok to break existing code in this case,
though that's probably unlikely.

But such a change would require some significant work to define its
behavior in *all* cases, followed by changes to all conforming C
implementations.

I suspect the committee would hesitate to make such a change when there
are already ways to do this, namely:

#define table_max 100 /* should probably be TABLE_MAX */

and

enum { table_max = 100 };

(The latter has the drawback that it works only for constants of
type int.)

I know you've said that you dislike the preprocessor, but you can't
reasonably expect the committee to reject a solution that's been
in common use for decades, and that works perfectly well, because
some guy named "fir" doesn't like it (no offense intended).

Major revisions of the C standard have been issued in 1989/1990,
1999, and 2011. Assuming that new revisions are produced on a
similar schedule, we can expect a new one in 2021 or so. The best
case scenario for the change you advocate is that the committee
spends time between now and 2021 nailing down its definition,
followed by at least several years before implementers fully support
the new standard. You'll then be able to use the new feature --
but only if your code doesn't need to be compiled with pre-C2021
compilers.

So you can either spend the next decade complaining that C's lack
of this feature is "stupid", or you can grit your teeth and use an
enum or #define.

Incidentally, posting in comp.lang.c that this is "stupid"
is definitely not the best way to get this change to happen.
comp.std.c, which discusses the C standard (as opposed to the
langauge defined by that standard) would be better, but even it
has no official standing with the committee, though a few members
do hang out there. (Check the group's history before posting;
it's likely that this idea has been proposed there before.)

And telling the committee that their work is "terribly stupidness"
will not get you anywhere.

And please either use something other than Google Groups to post here,
or clean up the double-spacing it imposes on quoted text before clicking
"send".
 
E

Eric Sosman

[...]
It would be hard to explain such basic
things why

coinst int table_max = 4096;

is better than

enum { table_max = 4096 }

C's enum is not of much use, I'll agree: It's only an int
with lipstick, plus just enough mystery to be alluring before
one gets to know it better. I wouldn't recommend enum as a
substitute for the kind of "coinst" you desire, even if it can
sometimes be put to that use.

But where's the case that `#define table_max 4096' is
inferior to "coinst"? So far, the *only* reason that's been
put forward is "fir doesn't like it." Somehow, "I don't like
it, therefore it's stupid" doesn't sound like the sort of
proof Euclid would be proud of.
(this reason is not purely aesthetic but i
meant that giving up with const ints here
and go intu enums would be terrible aesthetic
failure for me) Seem terribly obvious for me,
and not seeing it end using enums here seem
to be very stupid for me

defines are not such stupid but preprocesor
can be consideren not as part of c but some
external tool to use over it (it was initialy
considered like that, check out c history note)
and i also treat it such way (as external tool
over c, with not to much use for me personally)

In the very earliest days of C, the preprocessor was in
truth a separate entity. It didn't exist until about 1972-3,
and for a while the early compilers didn't even run it on
source files that didn't appear to use it. But its integration
into the language did not take long: by 1978 we find K&R saying
"The C compiler *contains* [emphasis mine] a preprocessor" (p. 207),
not "Hey, there's this extra tool you can use if you like."

If you've been doing the arithmetic, you'll see that the
preprocessor is at most forty years old and that C's inventor
considered it as contained in the compiler as of at least
thirty-four years ago. Since the preprocessor has been part
of the language for at least eighty-five percent of its entire
existence, the assertion that it should be thought of as "some
external tool" cannot be taken seriously.

The preprocessor is a full-fledged part of the language,
and provides the solution to your problem. Just use it!
 
J

James Kuyper

Why not? I can do this in another language

Because, at compile time for file2.c, file1.c is not visible, and the
value of table_max cannot be known. Any language that allows you to do
this does not support true separate compilation.
In any case, the language ought to provide true named constants, which are
pretty fundamental, not expect everyone to mess around with enums, defines,
and const attributes. Then the above won't be a problem.

Object-like macros have a name, and can expand into constant
expressions. Enumeration constants have a name and a constant value. In
what way do either them fail to qualify as "true named constants"?
You'll need to add at least one additional adjective to the phrase
"named constants" to justify rejecting them.
 
B

BartC

fir said:
W dniu niedziela, 18 listopada 2012 21:10:33 UTC+1 użytkownik Eric Sosman
napisał:
It would be hard to explain such basic
things why

coinst int table_max = 4096;

is better than

enum { table_max = 4096 }

I can see exactly why the use of enum doesn't seem satisfactory (being
designed for a different purpose, for a start).

But neither is 'const int' much better, defining an int *variable* not a
value, which by some luck happens to be usable as a compile-time value.

For some inexplicable reason, the designers of C could find no use for a
construct such as:

constant int abc = 8192;
constant double xyz = 60.7796;

whose values are *guaranteed* to be always what they say, where the names
obey normal scoping rules, and which was part of plenty of other languages.

However if you are offended by such things as enums and defines, then
there's a lot worse in the language!

C is pretty ancient and is not going to change dramatically any time soon.
 
K

Keith Thompson

fir said:
When I wrote some messages on this group (not many times that was my
third visit here) really I often met a ppl which I see try to make
'much effort to not understand me' (Really, I see suc thing here)

It would be hard to explain such basic things why

coinst int table_max = 4096;

is better than

enum { table_max = 4096 }

(this reason is not purely aesthetic but i meant that giving up with
const ints here and go intu enums would be terrible aesthetic failure
for me) Seem terribly obvious for me, and not seeing it end using
enums here seem to be very stupid for me

In my opinion, the difference *is* purely esthetic (apart from the
fact that the "const" version doesn't work in C).

I guess that English is not your first language, so perhaps you're
not aware that the word "stupid" is quite insulting. I suggest
not using it in a technical discussion like this. (No offense;
your English is certainly better than my own nonexistent Polish.)

Despite the similarity of the words "const" and "constant",
they're really not the same thing in C. A "constant expression"
is, roughly, an expression that can be evaluated at compile time,
and that can be used where you'd use a literal constant like 42.
(I say "roughly" because the language defines which expressions
are to be treated as constant expressions; there are expressions
that a particular compiler *can* evaluate at compile time that
don't qualify. It's necessary to have a consistent definition for
all compilers; the set of constant expressions doesn't expand as
compilers become more clever.)

The keyword "const", on the other hand, really means "read-only";
it means that an object's value may not be modified after it's been
created and initialized. For example, this:

const time_t now = time(NULL);
srand(now);
const int r = rand();

is perfectly valid, even though the compiler cannot possibly
determine the values of "now" and "r" before the program executes.
It just means that, for example, "r++" is not allowed.

C++ has added a special-case rule, that if an object is declared
"const" (read-only), and its initialization is a constant expression,
then the object's name can be used as a constant expression.
(I'm probably not stating the rule *quite* correctly, but that's
close enough.)
defines are not such stupid but preprocesor can be consideren not as
part of c but some external tool to use over it (it was initialy
considered like that, check out c history note) and i also treat it
such way (as external tool over c, with not to much use for me
personally)

Any program that uses any header (<stdio.h>, <stdlib.h>, etc.) must
use the preprocessor. The preprocessor is absolutely part of the
language; it's defined that way by the ISO C standard. You can't
avoid it. If you dislike the preprocessor that much, perhaps you'd
prefer some other language.

[...]

You'd like C to permit the name of a const-defined object as a
constant expression, as C does. I think most of us agree with you.
But the plain fact is that C doesn't support that, and it's not
going to any time soon.

You have two and only two options:
1. Use C.
2. Don't use C.

Make your choice and move on.
 
B

BartC

James Kuyper said:
Because, at compile time for file2.c, file1.c is not visible, and the
value of table_max cannot be known. Any language that allows you to do
this does not support true separate compilation.

Well, OK, in the language I had in mind, it would be tricky to do mutual
imports and exports (file1 importing names from file2, and vice versa). But
just to do the above example, it would look like this:

In file1:

global const table_max = 100

And in file2:

import file1

which makes available 'table_max' as a compile-time constant.

It does require that file1 is compiled (or somehow processed) first. After
that, the source does not need to be visible to file2 (what's important is
the exports file created for file1, which is what is processed by the
'import' statement, but this is little different to the common header file
that C might use, if 'table_max' is to be accessed from several other
modules).

It's also possible to write:

global const table_max = a + b + c.len # a bit like sizeof(c)

where a, b and c are locals, but only table_max is exported.

This sort of stuff is fiddly to do in C. Apparently you can't even export
the size of a local array as a compile-time constant (according to a recent
thread). And most global names seem to need to be defined twice: a shared
declaration, and a definition. It seems weak on importing and exporting
(imo).

Object-like macros have a name, and can expand into constant
expressions. Enumeration constants have a name and a constant value. In
what way do either them fail to qualify as "true named constants"?
You'll need to add at least one additional adjective to the phrase
"named constants" to justify rejecting them.

In another post in this thread, I gave the example of a proposed 'constant'
attribute which illustrates what I had in mind for a 'true named constant'.
This does exactly what is intended, without going around the houses trying
to achieve the same with whatever else C has lying around.

Actually I can't see why something so simple is not part of the language
anyway, *even if* the same thing can be achieved in several other indirect
ways.

The problem with named macros is mainly that of scope. Also, while you can
have a macro ABC that expands to the literal '73128', it could also expand
to '73)+*('!

There are one or two other things, for example a macro defined as a complex
100-term expression, used in 100 places in the code, could mean parsing
10000 tokens and evaluating the expression 100 times. A dedicated 'constant
feature would evaluate the expression once, and would occupy 100 tokens in
the code.

The problem with enums I think is more of 'type' (only available for int).
Also you always get the impression they were created for a specific purpose
(related sets of constants), and it seems like you're hijacking the feature
when you use it just because 'constant' isn't available.
 
E

Eric Sosman

James Kuyper said:
Because, at compile time for file2.c, file1.c is not visible, and the
value of table_max cannot be known. Any language that allows you to do
this does not support true separate compilation.

Well, OK, in the language I had in mind, it would be tricky to do mutual
imports and exports (file1 importing names from file2, and vice versa). But
just to do the above example, it would look like this:
[...]

You've missed James' point. Try this example:

/* file1.c */
const int table_max = 100;

/* file2.c */
const int table_max = 200;

/* victim.c */
extern const int table_max;
int table[table_max];

From these three files I want to make two programs: One
that links file1.o+victim.o, the other using file2.o+victim.o.
Explain how table_max is a compile-time constant in victim.

Also, I just wrote file1.c and file2.c today, but I wrote
and compiled victim.c->victim.o a month ago. Again, explain
how table_max was a compile-time constant a month before I
decided on any of the values it might someday have.
 
F

fir

W dniu niedziela, 18 listopada 2012 23:20:51 UTC+1 użytkownik Keith Thompson napisał:
[skip]

Make your choice and move on.

Can add one thing - I wrote about this
shocking thing that I cannot use const int
with array definietion (this disability is quite shocking and unpleasanyt for me)

How can it (it is parametrization of array sizes) be done in other general ways is yet
different question (but do not matter now, I
rethink it and maybe write on it l8r)
 
J

James Kuyper

In another post in this thread, I gave the example of a proposed 'constant'
attribute which illustrates what I had in mind for a 'true named constant'.
This does exactly what is intended, without going around the houses trying
to achieve the same with whatever else C has lying around.

I'll have to take a look at that other message, and perhaps respond
there, but all a "named constant" needs to be "true" is to be named, and
constant. If you have a more sophisticated concept in mind, it needs a
more complicated name to cover the additional features.
Actually I can't see why something so simple is not part of the language
anyway, *even if* the same thing can be achieved in several other indirect
ways.

The problem with named macros is mainly that of scope. Also, while you can
have a macro ABC that expands to the literal '73128', it could also expand
to '73)+*('!

I appreciate the value that scoping adds to a named constant, but a lack
of scoping doesn't disqualify macros or enumeration constants from the
category of "true named constants", but only from the more restricted
category of "true named scoped constants". The fact that macros can be
#defined to expand into things other than constant expressions excludes
such macros from the category of "named constants" - but that doesn't
prevent macros which DO expand into constant expressions from qualifying
as "true named constants".
The problem with enums I think is more of 'type' (only available for int).
Also you always get the impression they were created for a specific purpose
(related sets of constants), and it seems like you're hijacking the feature
when you use it just because 'constant' isn't available.

It's just a related set of constants, where the set size is 1 (rendering
"related" rather irrelevant). It seems to me a reasonable special case
use of a more general construct, though the general case is a more
reasonable use.

I don't object to bringing C into alignment with C++ on this issue; only
to mis characterization of the way in which the C alternatives differ
from what you can do in C++ and other languages.
 
B

Ben Bacarisse

BartC said:
For some inexplicable reason, the designers of C could find no use for
a construct such as:

constant int abc = 8192;
constant double xyz = 60.7796;

Of course they could see a use for such a thing, but that's not a good
way to design anything. At the very least you must decide if the
benefit is worth the cost.

If the objective had been to face the criticism of CS types 40 years
later, then C would have been a very different language, but it's goals
were narrow. In that sense, C is a victim of its success.

Because the target machines were quite small, the design method was
(roughly speaking) to add just enough type information to a typeless
language like B or BCPL to allow array indexing and struct accesses of
non word sized units. You also get simplified arithmetic because the
type lets you know what kind of + to use.

To keep the compiler small, the decisions were (I imagine) not whether
there is a use for this or that but whether there is any way to do
without it. Macros provide a lot features for very little code, and
they give you named constants for free.
 
B

BartC

Eric Sosman said:
Because, at compile time for file2.c, file1.c is not visible, and the
Well, OK, in the language I had in mind
... it would look like this:
[...]

You've missed James' point. Try this example:

/* file1.c */
const int table_max = 100;

/* file2.c */
const int table_max = 200;

/* victim.c */
extern const int table_max;
int table[table_max];

From these three files I want to make two programs: One
that links file1.o+victim.o, the other using file2.o+victim.o.
Explain how table_max is a compile-time constant in victim.

We're talking about compile-time constants, not link-time ones.

Also about something that C might be made to do, not as it is now. However
I'm assuming that this new C would need still the dimension of table[] -
table_max - to be known to the compiler.

So when victim is compiled, it will use the current value of table_max in
whichever file1 or file2 has been chosen. To create two programs with two
different values of table_max, requires recompilation of victim in each
case.

I haven't proposed any mechanism for making this selection in C. My point is
simply that it is practical. (In another language, I've just tried this on
one of my projects:

file1:
global const table_max = 100 # ('int' is implied here)

file2:
global const table_max = 200

victim (actually it's called 'test'):
import file2 # choose file2 in this instance
[table_max]int table
....
println table.len

The above translates into C code, the crux of which is

static int table[200];

printi(200);
println();

It works.)
Also, I just wrote file1.c and file2.c today, but I wrote
and compiled victim.c->victim.o a month ago. Again, explain
how table_max was a compile-time constant a month before I
decided on any of the values it might someday have.

Yes, there are still build dependencies, the same as there might be now when
you use header files:

you compiled victim.c a month ago, using a header file1.h containing some
particular function prototype which is contained in file1.c. Today you
modify the function in file1.c so that the prototype needs to change.
victim.c needs to be recompiled if you want to link it with a file1 object
file created today.

The information exported automatically in my file1/2 examples above, is
little different from manually creating a header containing the same
information (except some are harder in C, such as array dimensions).
 
J

James Kuyper

Eric Sosman said:
Because, at compile time for file2.c, file1.c is not visible, and the
....
You've missed James' point. Try this example:

/* file1.c */
const int table_max = 100;

/* file2.c */
const int table_max = 200;

/* victim.c */
extern const int table_max;
int table[table_max];

From these three files I want to make two programs: One
that links file1.o+victim.o, the other using file2.o+victim.o.
Explain how table_max is a compile-time constant in victim.

We're talking about compile-time constants, not link-time ones.

Precisely. Since victim.o has already been compiled before being linked
to either file1.o of file2.o, it can't be known at compile time for
victim.o whether table_max is 100 or 200, so it is not a compile-time
constant.
Also about something that C might be made to do, not as it is now. However
I'm assuming that this new C would need still the dimension of table[] -
table_max - to be known to the compiler.

So when victim is compiled, it will use the current value of table_max in
whichever file1 or file2 has been chosen.

At compile time, neither file1 nor file2 has yet been chosen. Connecting
victim.o to either file1 or file2 is part of linkage, not compilation.
... To create two programs with two
different values of table_max, requires recompilation of victim in each
case.

No, it requires linkage of victim, not recompilation.
I haven't proposed any mechanism for making this selection in C. My point is
simply that it is practical. (In another language, I've just tried this on
one of my projects:

file1:
global const table_max = 100 # ('int' is implied here)

file2:
global const table_max = 200

victim (actually it's called 'test'):
import file2 # choose file2 in this instance
[table_max]int table

If that's inside victim , then you're not doing true separate
compilation, which is precisely what I pointed out in my earlier
message. Compilation of victim requires compilation of file2.
...
println table.len

The above translates into C code, the crux of which is

static int table[200];

printi(200);
println();

It works.)
Also, I just wrote file1.c and file2.c today, but I wrote
and compiled victim.c->victim.o a month ago. Again, explain
how table_max was a compile-time constant a month before I
decided on any of the values it might someday have.

Yes, there are still build dependencies, the same as there might be now when
you use header files:

And a #defined macro in a C header file serves the same purpose.
 
B

BartC

James Kuyper said:
You've missed James' point. Try this example:

/* file1.c */
const int table_max = 100;

/* file2.c */
const int table_max = 200;

/* victim.c */
extern const int table_max;
int table[table_max];

From these three files I want to make two programs: One
that links file1.o+victim.o, the other using file2.o+victim.o.
Explain how table_max is a compile-time constant in victim.

We're talking about compile-time constants, not link-time ones.

Precisely. Since victim.o has already been compiled before being linked
to either file1.o of file2.o, it can't be known at compile time for
victim.o whether table_max is 100 or 200, so it is not a compile-time
constant.

The linking is a red herring. I'm also using the scheme in my example where
there is no static linking.

When the compiler does need the information to do it's job properly, this
has to be gleaned from whatever source-level files are available. (It
wouldn't pick up function prototypes from an .o file.)
Also about something that C might be made to do, not as it is now.
However
I'm assuming that this new C would need still the dimension of table[] -
table_max - to be known to the compiler.

So when victim is compiled, it will use the current value of table_max in
whichever file1 or file2 has been chosen.

At compile time, neither file1 nor file2 has yet been chosen. Connecting
victim.o to either file1 or file2 is part of linkage, not compilation.

As C works at present. If you wanted to import and export compile-time
constants, then something different is needed. (Although I've partly lost
track of why we want to do this: I think because 'constants' are implemented
now as variables with 'const' attribute, and Kenneth Brody gave an example
of such a variable being extern so that the value was not visible.)
victim (actually it's called 'test'):
import file2 # choose file2 in this instance
[table_max]int table

If that's inside victim , then you're not doing true separate
compilation, which is precisely what I pointed out in my earlier
message. Compilation of victim requires compilation of file2.

It requires an export file for file2. This is very little different to the
compiler needing a manually created .h file to tell it something about the
resources in an external .c file.

In C, if I wanted to make use of a library that was only shipped in .obj,
..lib or .dll format for example, I would probably use a .h file designed for
use with that library, containing everything I need to know to use the
library, including any named constants.

In my example, the exports file serves the same purpose. Except the contents
are created automatically. (And means most global objects are only defined
in one place. In fact, it is so handy, that I now have a program to do the
same for all my C functions; I haven't written any function prototypes for a
month!)
 
J

James Kuyper

James Kuyper said:
You've missed James' point. Try this example:

/* file1.c */
const int table_max = 100;

/* file2.c */
const int table_max = 200;

/* victim.c */
extern const int table_max;
int table[table_max];

From these three files I want to make two programs: One
that links file1.o+victim.o, the other using file2.o+victim.o.
Explain how table_max is a compile-time constant in victim.

We're talking about compile-time constants, not link-time ones.

Precisely. Since victim.o has already been compiled before being linked
to either file1.o of file2.o, it can't be known at compile time for
victim.o whether table_max is 100 or 200, so it is not a compile-time
constant.

The linking is a red herring. I'm also using the scheme in my example where
there is no static linking.

It really doesn't matter how linkage occurs. If compilation of victim
requires access to information from the compilation of file1 or file2,
depending upon which one is used, then it is not true separate
compilation. It may be a useful concept when discussing a language that
works that way, but talking about "compile time constants" in comparison
to C, when using a language where "compile time" means something
radically different from what it does in C is pretty much meaningless.
It's like comparing the ERA of a baseball player with that of a football
player.
 
B

BartC

To keep the compiler small, the decisions were (I imagine) not whether
there is a use for this or that but whether there is any way to do
without it. Macros provide a lot features for very little code, and
they give you named constants for free.

I've remarked before that I believe the development of C has been held back
*because* of it's macro language.

Whenever any change or enhancement is proposed, there is usually some way to
achieve the same with a macro. Usually not very elegantly either!
 
B

BartC

James Kuyper said:
On 11/18/2012 07:59 PM, BartC wrote:


It really doesn't matter how linkage occurs. If compilation of victim
requires access to information from the compilation of file1 or file2,
depending upon which one is used, then it is not true separate
compilation.

In that case C doesn't have true separate compilation either, if it needs
header files to tell it something about the contents of separate modules.

How was the information in the headers created? I'm pretty sure that it
would have been by someone with access to those modules! In that case that
person is the 'compiler'.
It may be a useful concept when discussing a language that
works that way, but talking about "compile time constants" in comparison
to C, when using a language where "compile time" means something
radically different from what it does in C is pretty much meaningless.

I'm sure that 'compile-time' means pretty much the same, ie. a value known
to the compiler.
 
I

Ian Collins

But where's the case that `#define table_max 4096' is
inferior to "coinst"? So far, the *only* reason that's been
put forward is "fir doesn't like it." Somehow, "I don't like
it, therefore it's stupid" doesn't sound like the sort of
proof Euclid would be proud of.

The one that really really bugged me was attempting to debug some code
that had a excess of #define constants was the inability of the debugger
to evaluate them. At the time I ended up bunging them all in enums.
The majority of them were consecutive sequences (return codes for
example), so the enum made for a better deign. The rest ended up in a
collection of odds and sods enums. Inelegant but debugger friendly.

Another somewhat contrived example is:

#define X 42

void f( const int* p );

int main(void)
{
f(&X);
}
 

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,079
Messages
2,570,574
Members
47,206
Latest member
Zenden

Latest Threads

Top