Ian Collins said:
Tim said:
Stephen Sprunk said:
On 15-Jun-13 15:49, Eric Sosman wrote:
On 6/15/2013 3:49 PM, Stephen Sprunk wrote:
Without VLAs, array sizes are constrained to "constant integer
expressions". For some reason, a "const int" is not a "constant
integer expression", so using one as such would be a constraint
violation.
...
The C-like subset of C++ does not have this defect, so it's
puzzling why later revisions of C itself have not fixed it.
This is simpler, for instance, than introducing VLAs in the
first place. [...]
Simpler, well, maybe -- but some complications remain:
const int n = 1 + rand() % 53; int v[n];
Just knowing that `n' is `const' is not enough; one must also
remember things about its initializer.
Surely the compiler would know whether n's value was known or not.
An unknown value would only be allowed if VLAs were supported.
One can write rules to deal with such things -- C++ has such
rules -- but the very existence of the rules shows that "simpler"
is a bit more complex than "dead easy."
Fair enough. However, I think it's a reasonable to expect this to
be fixed by now, especially given the C-like subset of C++ did so
long ago. [snip]
This assumes that other people agree that it's a problem, and
obviously they don't. What are the benefits, and what makes
them worth the costs? Unless and until someone presents a
compelling argument on this question, it's unlikely that C
will adopt such a change. And rightly so.
One could argue that VLAs are the cost of not making this simple and
long overdue change. The archaic kludges (textual substitution,
abuse of enums) used as a work around the lack of proper const are
yet another cost we have to bear.
If having VLAs is one of the consequences of not adopting the C++
rule allowing constant-expression-defined-variables in other
constant expressions, then IMO that consequence alone makes it
worth not including the C++ feature, even if there were no other
reasons favoring excluding it.
Ignoring that aspect (which I think was given not completely
seriously anyway), let's draw up a list of consequences, or
attributes, for each of the three approaches. This turns out to
be a bigger list than I was expecting:
#define enum 'const' variables
-------------------- -------------------- ---------------------
textual binding lexical binding lexical binding
no scoping regular scoping regular scoping
(or, scope may be
limited w/ #undef)
no reference as a no reference as a may be referenced
variable variable as a variable,
eg, address taken
uses preprocessor
works easily w/ -D=
arithmetic types only type int arithmetic types
(address values pointer types?
allowed, but using
textual binding)
individual values sets of values individual values
(allowing enhanced
type checking)
K&R C C90 (or before?) would need additional
language definition
(for C)
textual + semantic syntactic (ie, name semantic (ie, to know
(ie, whether value defined always may whether a variable can
may be used in a be used in constant be used as a constant,
constant expression expression) some semantic analysis
depends on both needed)
textual analysis and
semantic analysis)
no change to other no change to other interacts with other
language aspects language aspects language aspects
(based on my perhaps
flawed understanding
of the C++ feature)
same in C and C++ same in C and C++ present in C++,
(slightly different not present in C
semantics in the
two languages?)
Now on to (my own) subjective reactions.
Comparing #define and 'const' variables, the main plusses for
using #define are also its main minuses: it uses a simple,
long-known and well-understood mechanism, with all of the usual
warts associated with the preprocessor. Some people depolore
using the preprocessor on general principles and try to avoid it
pretty much at all costs, but I'm not in that camp. I know the
preprocessor has warts, but really they aren't that bad here for
just #define of simple constant expressions. Lack of lexically
scoped definitions is a drawback for #define; this is partially
fixable with #undef, which works sort of okay but definitely has
a high wart factor.
Looking at the flip side, 'const' variables don't have the warts
that using #define does: analysis is easier, and lexical scoping
is a plus. I'm not sure how important the scoping issue is; I
would want to look at a variety of code bases before assigning a
particular weight to that aspect. The big drawback of 'const'
variables is that adding them to C would mean a larger and more
complicated language definition. It's easy to underestimate the
impact of "little" changes like this. I recently went through
the exercise of reading the C++ language definition document (ie,
the C++ Standard, although not the most recently approved one).
Until doing that I didn't really appreciate just how large and
complicated C++ has become. How did it get that way? As the
saying goes, one feature at a time...
Another consideration: not having 'const' variables in C
increases the semantic gap between C and C++. I'm not sure if
the weight for this should be positive or negative, but in any
case the absolute value is small IMO.
Comparing using 'enum' and const variables is more interesting.
Relative to const variables, the biggest downside of defining
constants using 'enum' is that they are limited to values of type
int. (I know the syntac form is unappealing to some but to me
this seems like a minor issue.) Now look at the positives: we
know something defined in an enum can be used in a constant
expression; enum lets us define several related values that can
be identified together, eg, for checking the cases of 'switch()'
statements; a value defined in an enum is identified with a
particular type, which facilitates improved typed checking should
we want to do that.
Besides offering a larger range of types, const variables are
part of "ordinary" C, ie, the definitions look just like
executable C code. I don't think this is a big plus. In fact it
may not be a plus at all - compile time and execution time are
fundamentally different regimes, and making them look the same
may be more confusing than helpful. Using 'const' to define
compile time variables may be seen as a very limited form of
metaprogramming. It certainly isn't obvious that lowering
the boundary between programming and metaprogramming leads to
better programs. Clearly it is possible make them look the
same, but that doesn't mean it's desirable.
Bottom line: I stand by my original assessment -- until and
unless someone presents a compelling argument for introducing a
new language feature (for C) in this area, the language is better
left as is. (Note: for such an argument to be compelling, it
should include a statement of what use cases it means to address,
along with some sort of evidence that those use cases are
significant and substantial.) Furthermore, even assuming that
adopting a new language feature seems advisable, extending enums
in some way that would allow for greater type variability looks
like a better bet than adopting the const variable feature.