Sysop: | Amessyroom |
---|---|
Location: | Fayetteville, NC |
Users: | 28 |
Nodes: | 6 (0 / 6) |
Uptime: | 55:06:01 |
Calls: | 422 |
Files: | 1,025 |
Messages: | 90,706 |
On 19.04.2025 21:36, bart wrote:
On 19/04/2025 20:15, James Kuyper wrote:
How could I possibly justify concluding that
the problem lies anywhere other than in your own abilities?
Did I say I find it difficult? [...]
Yes, repeatedly. You've described it as a source of errors and confusion.
So your attitude is to totally ignore such criticism? C is perfect? [...]
No, I said nothing to suggest that. It is a less than perfect language,
but the amount of trouble you have with it is far more than it's imperfections justify.
On Sun, 20 Apr 2025 14:53:54 +0100
bart <bc@freeuk.com> wrote:
On 20/04/2025 12:00, Janis Papanagnou wrote:
Exactly 19 days ago I'd even have written such a proposal, and,
fitting to the calendar date, of course I'd have used a distinct
name for the feature; 'for_losers(var,expr,expr[,expr])'.
This is quite telling in that:
(1) You regard the idea of desiring such a feature as a joke
(2) You consider those who'd like to use it as 'losers'
Just such a feature seems to be the primary style of 'for' in
languages such as Ada, Python, Modern Fortran, Odin, Ruby, Julia,
Rust, Matlab, Algol68, Euphoria, Ocaml, Logo, Nim, PL/I, Haxe, Lua,
....
In majority of languages in your list 'for' loops iterates through all elements of collection. If collection happens to be a range
(terminology shared by python and Rust) then 'for' behaves as a
counting loop. If it isn't then it does not. Even Matlab, despite its fortranic roots, belongs to that group.
C++ achieve the same objectives [with typical C++ ugliness] by means
of std::foreach.
If I am not mistaken, all exception to that pattern are old languages.
Even Bash has it. All losers?
Not all, just many.
Interestingly, so does BCPL: you may be aware that C was influenced
by B, which itself was supposed to be influenced by BCPL. So what the
hell happened?
So, what happened?
That's what I'm asking! BCPL, an older and more primitive language
than the other two, has such a 'for' as described above:
FOR N = E1 TO E2 DO
It seems to have been dropped by B, which has only 'while', until C introduced a souped-up, 3-element version of 'while', named 'for'.
Clearly the author of both (Ken Thompson) had no great love for
'for'-loops; real ones.
This is quite telling in that:
(1) You regard the idea of desiring such a feature as a joke
(2) You consider those who'd like to use it as 'losers'
BTW, a more serious question. Would a change of the "C" language
have syntax constructs with such _optional_ components?
Why is that a big deal?
C's 'for' already has optional parts, although the semicolons need to be present. Do you mean allowing the comma in your example to be optional
as well as the expression?
[...], and why are we losers to do so?
On Sun, 20 Apr 2025 14:53:54 +0100
bart <bc@freeuk.com> wrote:
[...]
Just such a feature seems to be the primary style of 'for' in
languages such as Ada, Python, Modern Fortran, Odin, Ruby, Julia,
Rust, Matlab, Algol68, Euphoria, Ocaml, Logo, Nim, PL/I, Haxe, Lua,
....
In majority of languages in your list 'for' loops iterates through all elements of collection. If collection happens to be a range
(terminology shared by python and Rust) then 'for' behaves as a
counting loop. If it isn't then it does not. Even Matlab, despite its fortranic roots, belongs to that group.
C++ achieve the same objectives [with typical C++ ugliness] by means
of std::foreach.
If I am not mistaken, all exception to that pattern are old languages.
Even Bash has it. All losers?
Not all, just many.
On 20.04.2025 15:53, bart wrote:
This is quite telling in that:
(1) You regard the idea of desiring such a feature as a joke
(2) You consider those who'd like to use it as 'losers'
No. As so often you make up things, and you are interpreting
things like your preconditioned brain wants to see them.
Exactly 19 days ago I'd even have written such a proposal, and,
fitting to the calendar date, of course I'd have used a distinct
name for the feature; 'for_losers(var,expr,expr[,expr])'.
BTW, a more serious question. Would a change of the "C" language
have syntax constructs with such _optional_ components?
Why is that a big deal?
Why do you think I said or implied it would be a "big deal"?
C's 'for' already has optional parts, although the semicolons need to be
present. Do you mean allowing the comma in your example to be optional
as well as the expression?
I think the question in context of my sample is clear enough
and I see no point in further explanations or repetitions.
On 20/04/2025 11:18, Janis Papanagnou wrote:
On 19.04.2025 15:05, bart wrote:
But overloading in the form of 30-40 integer types to represent the 8
types i8-i64 and u8-u64, plus the 16 different combinations of writing 'unsigned long long int', and so on, is fine. As are the dozens
(hundreds?) of macros in stdint.h/inttypes.h.
Show me a for-loop that cannot be expressed with existing features. Many
are likely to be clearer!
I don't know why people think that cramming as much code as possible
into for(...) is a good style of coding.
Either into one over-long line,
or spilling over multiple lines; both should fail a code review.
[...]
I'll tell you a secret: a few years ago, I also added a C-style 'for'
statement to my language. So it had two kinds of 'for' that looked like
this:
for i in a..b do # also for i[:=a] to b do
cfor A, B, C do # A B C are arbitrary expressions
This was because I'd got fed up with people telling me all these
wonderful things that C's for could do, that I couldn't. So now I could
do the same!
The feature sets in languages should (IMO) follow a design principle.
It may make sense in one language or not in another language. I don't
know "your language" so I cannot suggest you anything. All I can say
is that such featuritis is, as "design principle", not something that
I'd say you've done right as a basic design approach of your language.
(But as often said; I don't care about "your language" and what you
do in your personal environment.)
So, you don't like the idea of having multiple simple features, each
with a well defined job that a compiler can check.
[...]
On 20.04.2025 13:43, bart wrote:
On 20/04/2025 11:18, Janis Papanagnou wrote:
On 19.04.2025 15:05, bart wrote:
But overloading in the form of 30-40 integer types to represent the 8
types i8-i64 and u8-u64, plus the 16 different combinations of writing
'unsigned long long int', and so on, is fine. As are the dozens
(hundreds?) of macros in stdint.h/inttypes.h.
I haven't said this "is fine". It wasn't at all the topic here.
Show me a for-loop that cannot be expressed with existing features. Many
are likely to be clearer!
(There have been sufficient examples posted.)
It makes not much sense to spread them across a multi-line block
of statements. - If you haven't ever experienced that, and still
cannot see and understand that if explained to you, then I can't
help you.[*]
The point is that if you have programming elements that are
related to a loop construct (initialization, exit condition,
update for next iteration) it makes sense to keep them together.
[*] Maybe you should visit some University courses. Despite your
long programming history you really seem to miss some elementary
basic programming experiences. Or are you mentally so bound to
primitive loops and your brain so inflexible that nothing helps.
I don't know why people think that cramming as much code as possible
into for(...) is a good style of coding.
But that's not what programmers should do.
Why do you again make
up things. - The point is to keep things together that belong
together.
So, you don't like the idea of having multiple simple features, each
with a well defined job that a compiler can check.
Why do you think so.
I merely noted that if you, as the language designer, let yourself
get triggered by feature wishes (whether they fit in your language
design or not) that this is not what I'd consider to be sensible.
bart <bc@freeuk.com> writes:
for(p=sqliteHashFirst(&pSchema->trigHash); p; p=sqliteHashNext(p)){
sqlite3DeleteTrigger(db, (Trigger*)sqliteHashData(p));
}
I might write it like this:
for ( p = sqliteHashFirst(&pSchema->trigHash);
p != NULL;
p = sqliteHashNext(p) )
{
sqlite3DeleteTrigger(db, (Trigger*)sqliteHashData(p));
}
I have certain preferences (spaces around most operators, explicit
comparison to NULL, willingness to split long lines) that other C
programmers may or may not share.
It keeps it even more together, which you seem to like.
That's something you invented. I find it ugly, and I presume you'd
agree. The fact that you think that someone else would prefer it
indicates that you don't understand how other people think.
I'd rather not write `for (ch in 0..255)` because it's a syntax error.
You have the luxury of using your own language.
I said I tried one like C's, and it was never used. There is enough
flexibility in the rest to deal with anything that comes up.
It was never used by whom?
If you don't like C-style for loop, they absolutely should not
exist in a language for which you are, if I understand correctly,
the sole implementer and the sole user.
I understand and completely accept that you find the while loop
clearer, and I have no interest in changing your mind.
I find the for loop clearer. I won't speak for anyone else, but I
suspect a lot of C programmers would also find the for loop clearer.
On 19/04/2025 20:22, James Kuyper wrote:
On 4/19/25 12:36, Kaz Kylheku wrote:
On 2025-04-19, Scott Lurndal <scott@slp53.sl.home> wrote:...
bart <bc@freeuk.com> writes:
On 18/04/2025 19:10, James Kuyper wrote:
If all you can do is "hope for the best", you're doing it wrong. It's >>>>>> your job to ensure that they are not arbitrary unrelated expressions, >>>>>> but correctly related expressions, and that's no different from your >>>>>> responsibility for all of the other expressions that make up your
program.
If you find that problematic, you shouldn't be programming in
any language, but certainly not in C.
I see it didn't take you long to get to the personal insult. What is it >>>>> with this group?
It's not an insult, it is a simple fact.
It's not a fact that someone who finds tools problematic shouldn't
be using them.
I wasn't talking about him finding the tools problematic. I was talking
aobut him find it difficult to ensure that the expressions are not
arbitrary unrelated expressions, but are in fact correctly related
expressions. If you cannot ensure that A, B, and C have the correct
relationship to make for(A; B; C) work as needed, then you also lack to
ability to make sure that the expressions in {A; B; C:} work together as
needed, and that ability is fundamental to computer programming.
In other words, the feature is dumb.
The compiler cannot do any checking: for (i=0; i<n; ++n) is fine.
Even in BASIC, if I do this:
for i=1 to n
next n
it will say that n does not match. And here it is optional; in C that
part is necessary.
So, BASIC's for-loop is less dumb that C's.
But, you have a bizarre take on this: if somebody calls it out, then
rather than agree with them, you will personally insult the person who
said it, and suggest that if they are incapable of such a simple check,
then they shouldn't be coding.
The fact is that people make typos (obviously, not you or JP or SL or
KT!), and here you would really prefer that the compiler could report
them, but with this feature, it often can't.
But the thing is, once you split it into multiple lines, then there is
little advantage over using a regular 'while' loop:
p = sqliteHashFirst(&pSchema->trigHash);
while (p != NULL)
{
sqlite3DeleteTrigger(db, (Trigger*)sqliteHashData(p));
p = sqliteHashNext(p) )
}
On 2025-04-20, bart <bc@freeuk.com> wrote:
But the thing is, once you split it into multiple lines, then there is
little advantage over using a regular 'while' loop:
The advantage may be little, but there is some.
p = sqliteHashFirst(&pSchema->trigHash);
while (p != NULL)
{
sqlite3DeleteTrigger(db, (Trigger*)sqliteHashData(p));
p = sqliteHashNext(p) )
}
- In a while loop, if you "continue", it will jump to the top of
the loop, without executing the step.
- The loop controls are not gathered in one place.
- Because they are not gathered in one place, not only is it less>readable, but we cannot use while write a macro such as:
for_sqlite_hash (p, &pSchema->trigHash) {
if (some_condition_over(p))
continue; // doesn't stupidly repeat for the same p!
}
On 19/04/2025 20:22, James Kuyper wrote:...
I wasn't talking about him finding the tools problematic. I was talking
aobut him find it difficult to ensure that the expressions are not
arbitrary unrelated expressions, but are in fact correctly related
expressions. If you cannot ensure that A, B, and C have the correct
relationship to make for(A; B; C) work as needed, then you also lack to
ability to make sure that the expressions in {A; B; C:} work together as
needed, and that ability is fundamental to computer programming.
In other words, the feature is dumb.
The compiler cannot do any checking: for (i=0; i<n; ++n) is fine.
Even in BASIC, if I do this:
for i=1 to n
next n>
it will say that n does not match. And here it is optional; in C that
part is necessary.
But, you have a bizarre take on this: if somebody calls it out, then
rather than agree with them, you will personally insult the person who
said it, and suggest that if they are incapable of such a simple check,
then they shouldn't be coding.
The fact is that people make typos (obviously, not you or JP or SL or
bart <bc@freeuk.com> writes:
But I hear so much about how wonderful it is, how multi-purpose, how
indispensible, how superior to an ordinary 'for' (admittedly from
people who don't have a choice) that I didn't want to miss out!
Again, stop misrepresenting what other people say.
Wonderful? No, I don't recall anyone other than you using that word,
and only sarcastically.
Multi-purpose? Yes, absolutely.
Indispensible? No, I don't believe anyone has made that claim, and it
would be inaccurate.
Superior to an ordinary 'for'? Certainly a lot of people think it is, because of its flexibility.
bart <bc@freeuk.com> wrote:
On 19/04/2025 20:22, James Kuyper wrote:...
I wasn't talking about him finding the tools problematic. I was talking
aobut him find it difficult to ensure that the expressions are not
arbitrary unrelated expressions, but are in fact correctly related
expressions. If you cannot ensure that A, B, and C have the correct
relationship to make for(A; B; C) work as needed, then you also lack to
ability to make sure that the expressions in {A; B; C:} work together as >>> needed, and that ability is fundamental to computer programming.
In other words, the feature is dumb.
The feature allows you to pack all of the loop management stuff together where it's easy to check and think about. It can be misused, by putting together stuff that has nothing to do with loop management, just as an assignment statement can be misused by writing, for instance
annual_salary = current_income + age;
The compiler cannot do any checking: for (i=0; i<n; ++n) is fine.
The compiler cannot tell you what's wrong with that assignment
statement, either.
bart <bc@freeuk.com> wrote:
On 19/04/2025 20:22, James Kuyper wrote:
On 4/19/25 12:36, Kaz Kylheku wrote:
On 2025-04-19, Scott Lurndal <scott@slp53.sl.home> wrote:...
bart <bc@freeuk.com> writes:
On 18/04/2025 19:10, James Kuyper wrote:
If all you can do is "hope for the best", you're doing it wrong. It's >>>>>>> your job to ensure that they are not arbitrary unrelated expressions, >>>>>>> but correctly related expressions, and that's no different from your >>>>>>> responsibility for all of the other expressions that make up your >>>>>>> program.
If you find that problematic, you shouldn't be programming in
any language, but certainly not in C.
I see it didn't take you long to get to the personal insult. What is it >>>>>> with this group?
It's not an insult, it is a simple fact.
It's not a fact that someone who finds tools problematic shouldn't
be using them.
I wasn't talking about him finding the tools problematic. I was talking
aobut him find it difficult to ensure that the expressions are not
arbitrary unrelated expressions, but are in fact correctly related
expressions. If you cannot ensure that A, B, and C have the correct
relationship to make for(A; B; C) work as needed, then you also lack to
ability to make sure that the expressions in {A; B; C:} work together as >>> needed, and that ability is fundamental to computer programming.
In other words, the feature is dumb.
The compiler cannot do any checking: for (i=0; i<n; ++n) is fine.
Even in BASIC, if I do this:
for i=1 to n
next n
it will say that n does not match. And here it is optional; in C that
part is necessary.
So, BASIC's for-loop is less dumb that C's.
But, you have a bizarre take on this: if somebody calls it out, then
rather than agree with them, you will personally insult the person who
said it, and suggest that if they are incapable of such a simple check,
then they shouldn't be coding.
The fact is that people make typos (obviously, not you or JP or SL or
KT!), and here you would really prefer that the compiler could report
them, but with this feature, it often can't.
There is low probablity of writing standard loop wrong and most
people are not bothered that some errors are not detected at
compile time. If you are trouble by this, solution is simple:
do not write 'for' loops different than the simple one:
If you are bothered that other people do not think that C
flexibility is a problem, then you would need _much_ stronger
argument, starting with some real data.
I don't know why people think that cramming as much code as possible
into for(...) is a good style of coding. Either into one over-long line,
or spilling over multiple lines; both should fail a code review.
Actually here's a example from sqlite3.c:
for(p=sqliteHashFirst(&pSchema->trigHash); p; p=sqliteHashNext(p)){
sqlite3DeleteTrigger(db, (Trigger*)sqliteHashData(p));
}
And this is how you might be forced to write it instead:
p=sqliteHashFirst(&pSchema->trigHash);
while (p) {
sqlite3DeleteTrigger(db, (Trigger*)sqliteHashData(p));
p=sqliteHashNext(p);
}
Yes, it's spread over two more lines, but so what? It's much clearer:
the initialisation is done once and then it's out of the way. Setting p
to the next value is now physically written after the body.
bart <bc@freeuk.com> wrote:
I don't know why people think that cramming as much code as possible
into for(...) is a good style of coding. Either into one over-long line,
or spilling over multiple lines; both should fail a code review.
Actually here's a example from sqlite3.c:
for(p=sqliteHashFirst(&pSchema->trigHash); p; p=sqliteHashNext(p)){
sqlite3DeleteTrigger(db, (Trigger*)sqliteHashData(p));
}
And this is how you might be forced to write it instead:
p=sqliteHashFirst(&pSchema->trigHash);
while (p) {
sqlite3DeleteTrigger(db, (Trigger*)sqliteHashData(p));
p=sqliteHashNext(p);
}
Yes, it's spread over two more lines, but so what? It's much clearer:
the initialisation is done once and then it's out of the way. Setting p
to the next value is now physically written after the body.
Apparently you do not get why sqlite3.c version is better.
In short, this is separation of concerns. The 'for' construct
is responsible for iteration. Body of 'for' loop is responsible
for computation. You may replace body by empty instruction,
so there are no computation but the loop still correctly goes
over the same sequence. Or you may add instructions to perform
more computation. 'while' version mixes computation with
stepping to the next element. Since in 'for' version all parts
dealing with iteration are together it is easy to check that
they are correct. With 'while' version probablity of forgeting
to step to next element is higher.
In short, this is separation of concerns.
FYI: I do not remember making error in a simple 'for' loop.
I do remember cases when I had to use 'while' for iteration
(in non-C languages) and stepping was wrong/missing. So
for me simple loops (your 98% of cases) are not an issue.
The issue are remaining cases, and for them C 'for' works
better.
On 21/04/2025 04:16, Kaz Kylheku wrote:
- Because they are not gathered in one place, not only is it less>readable, but we cannot use while write a macro such as:
for_sqlite_hash (p, &pSchema->trigHash) {
if (some_condition_over(p))
continue; // doesn't stupidly repeat for the same p!
}
I can't write such macros at all. I'm not even sure what this does.
I'd call that a win!
On 2025-04-21, bart <bc@freeuk.com> wrote:
On 21/04/2025 04:16, Kaz Kylheku wrote:
- Because they are not gathered in one place, not only is it less>readable, but we cannot use while write a macro such as:
for_sqlite_hash (p, &pSchema->trigHash) {
if (some_condition_over(p))
continue; // doesn't stupidly repeat for the same p!
}
I can't write such macros at all. I'm not even sure what this does.
Have you never worked with a large codebase written by someone other
than you?
When you open a random file in an unfamiliar code base, pretty
much any function call that is not in the standard library triggers
the "I don't know what this does" response.
You have to learn some of that program's definitions in order to
effectively work with that program. At least those which are relevant to
your intended task. You set up your "jump to definition" editor-fu and
start reading.
A loop macro like for_sqlite_hash (p, &pSchema->trigHash) is so obvious
that the only reason you'd look at its definition is to confirm that
it's not doing something stupid (which can be defined as just about
anything different from what it *looks* like it is doing).
I'd call that a win!
Now you're calling the inability of the programmer to implement a
nice space-saving notation over something verbose a "win".
If so, why isn't "for (a; b; c)" also a "win" over "do i = x, y".
On 21/04/2025 19:43, Kaz Kylheku wrote:
On 2025-04-21, bart <bc@freeuk.com> wrote:
On 21/04/2025 04:16, Kaz Kylheku wrote:
- Because they are not gathered in one place, not only is it less>readable, but we cannot use while write a macro such as:
for_sqlite_hash (p, &pSchema->trigHash) {
if (some_condition_over(p))
continue; // doesn't stupidly repeat for the same p!
}
I can't write such macros at all. I'm not even sure what this does.
Have you never worked with a large codebase written by someone other
than you?
How large are we talking about?
I've delved into largish apps in the context of getting my C compiler working. I would describe that experience as 'brutal'.
If you need to
debug someone else's codebase, not to find bugs in that program, but to
find why your implementation is failing, then you want as conservative a coding style as possible.
When you open a random file in an unfamiliar code base, pretty
much any function call that is not in the standard library triggers
the "I don't know what this does" response.
Yes, a function call. Macros are in an entirely different, evil category.
The Lua sources (only a mere 30Kloc), use macros extensively, but also
have a habit of giving them ordinary looking names in lower case so that
they look like function calls.
Preprocessing such code doesn't help either, since a simple function
call can expand into a horrendously complex expression that can be 100s
of characters long and have parentheses nested 10 deep.
You have to learn some of that program's definitions in order to
effectively work with that program. At least those which are relevant to
your intended task. You set up your "jump to definition" editor-fu and
start reading.
A loop macro like for_sqlite_hash (p, &pSchema->trigHash) is so obvious
Please humour me: What Does It Do?
that the only reason you'd look at its definition is to confirm that
it's not doing something stupid (which can be defined as just about
anything different from what it *looks* like it is doing).
I'd call that a win!
Now you're calling the inability of the programmer to implement a
nice space-saving notation over something verbose a "win".
If so, why isn't "for (a; b; c)" also a "win" over "do i = x, y".
Your example is just this:
X(Y, Z)
and you're claiming it is something wonderful. Is it?
I don't know. I
might guess from its name that it is something to do with loops.
So what do X and Y represent, and what do they expand to?
What I might deduce, is that in C if you have a block of code like this:
{body}
You can turn that into a loop by putting a for-header to its left:
for(...) {body}
That for-header can come from a macro, so can be used to apply a loop to
such a block, without the code block itself needing to be a macro argument.
I will admit that is a useful side-effect of how a for-loop works, so
that it becomes a helpful feature if you ever need to write such macros.
I can't do that in my language; the macros are too simple, and the loop
body would need to be an argument, requiring closures etc. But then, the existing loop features are adequate, and it is also easy to add new ones
by changing the language.
A macro solution would anyway be poor in terms of nice syntax, error reporting and so on.
(I'm just remembered I have a visualisation that can turn C syntax into
my syntax (but it's not good enough to compile). It turns:
for(p=sqliteHashFirst(&pSchema->trigHash); p; p=sqliteHashNext(p)){
sqlite3DeleteTrigger(db, (Trigger*)sqliteHashData(p));
}
into:
p := pSchema^.trigHash.first
while p do
sqlite3DeleteTrigger(db, ref Trigger(p^.data))
p := p^.next
od
You can now see that the C version involves macros. The increment has
now also been resolved into a simple member access.
Turning it into decent syntax and GETTING RID of macros has produced
cleaner looking code!)
On 21/04/2025 14:51, Waldek Hebisch wrote:
bart <bc@freeuk.com> wrote:
I don't know why people think that cramming as much code as possible
into for(...) is a good style of coding. Either into one over-long line, >>> or spilling over multiple lines; both should fail a code review.
Actually here's a example from sqlite3.c:
for(p=sqliteHashFirst(&pSchema->trigHash); p; p=sqliteHashNext(p)){ >>> sqlite3DeleteTrigger(db, (Trigger*)sqliteHashData(p));
}
And this is how you might be forced to write it instead:
p=sqliteHashFirst(&pSchema->trigHash);
while (p) {
sqlite3DeleteTrigger(db, (Trigger*)sqliteHashData(p));
p=sqliteHashNext(p);
}
Yes, it's spread over two more lines, but so what? It's much clearer:
the initialisation is done once and then it's out of the way. Setting p
to the next value is now physically written after the body.
Apparently you do not get why sqlite3.c version is better.
In short, this is separation of concerns. The 'for' construct
is responsible for iteration. Body of 'for' loop is responsible
for computation. You may replace body by empty instruction,
so there are no computation but the loop still correctly goes
over the same sequence. Or you may add instructions to perform
more computation. 'while' version mixes computation with
stepping to the next element. Since in 'for' version all parts
dealing with iteration are together it is easy to check that
they are correct. With 'while' version probablity of forgeting
to step to next element is higher.
You have to analyse it first. The kind of loop this expresses is:
p = startvalue()
while (p) {
<body>
p = nextvalue()
}
Notice how I chose to express it: it reflects how I would describe it in English:
* Set P to Start Value
* While P isn't Null:
* Execute the body
* Set P to Next Value
So, how would /you/ describe it in English? (Or in any language if like,
as the ordering is more important.)
In short, this is separation of concerns.
You seem be picking and choosing which concerns are in need of separation!
The example is this: for(A; B; C) D
You are saying that D must be separate from A B C, but specifically from C.
I'm asking, why shouldn't A B C also be separate from each other?
Especially A, which is only executed once, after which it's no longer
part of the loop.
As C works now, the alternative might be: A; while (B) { D; C;}.
I wouldn't have minded a syntax like: A; while (B; C) {D} but C doesn't
have that. In that case, the 'while' version still wins over 'for' IMO.
FYI: I do not remember making error in a simple 'for' loop.
I do remember cases when I had to use 'while' for iteration
(in non-C languages) and stepping was wrong/missing. So
for me simple loops (your 98% of cases) are not an issue.
The issue are remaining cases, and for them C 'for' works
better.
That's bizarre. It is exactly like saying you don't have a problem with writing (or forgetting to write) 'break' in 99% of switch-case blocks, because in those 1% of cases where you do want fallthroughit is 'automatic'.
I expect you also either think that:
for (ch = 0; ch <= 255; ++ch)
is better than: 'do ch = 0, 255', or is much more tolerant of it than I am.
Here's how that C for-loop works, in English:
* Set ch to 0
* While c is less than or equal to 255: (typo left in!)
* Execute the body
* Set ch to ch + 1
Here is how that compact version works, in English:
* For ch having the values 0 to 255 inclusive:
* Execute the body
However I see that nobody in this group has any objective interest in language ergonomics and aesthetics. They more interested in defending
'their' language.
C-style for loops have been used successfully for decades, and have
been adopted by other languages (including bash, which isn't
particularly C-like).
I don't now think think there is any argument that will make any
difference. People here genuinely think that writing:
for (ch = 0; ch <= 255; ++ch)
is far superior to this 'primitive' version:
do ch = 0, 255
No amount of discussion or arguments will make them change their minds.
(BTW I had to fix two typos in the C, as in the first version I
initially used 'ch, c and c'!)
Apparently, the C form is superior because that construct can also be
used to conveniently express link-list traversal, and a lot more.
Even the idea of having *two* kinds of loop, one as it works now, and
one more streamlined, was not acceptable: too much 'overloading' of the language.
On 21.04.2025 23:21, Keith Thompson wrote:
[...]
C-style for loops have been used successfully for decades, and have
been adopted by other languages (including bash, which isn't
particularly C-like).
I have to disagree on that. First I'm positive that Bash adopted
the Ksh loops (but incompletely!), and not the "C" loops.
And, as opposed to Ksh (and "C"), Bash doesn't support FP valued
loops.
(As previously said, Unix shell in general and Bash specifically
is not a good comparison WRT "C" loops.)
Janis
bart <bc@freeuk.com> wrote:
BTW2: When looking at 'for' loop you are supposed to see pattern,
without need to track all steps.
The intent of 'for' is to iterate over some collection. Each of
A, B, C is needed to know the collection.
Especially A, which is only executed once, after which it's no longer
part of the loop.
Without A you do not know range of iteration.
I expect you also either think that:
for (ch = 0; ch <= 255; ++ch)
is better than: 'do ch = 0, 255', or is much more tolerant of it than I am.
In 1975 C version was better: it allowed flexible loops using very
simple compiler.
But saying that C is bad rarely brings someting new, people here
know most of C warts and have ways to cope with them. If they
thought that some other language is really better they would use
it instead of C. FYI, I do probably majority of my coding in
different languages.
On 21/04/2025 21:25, Kaz Kylheku wrote:
On 2025-04-21, bart <bc@freeuk.com> wrote:
op_arith(L, l_addi, luai_numadd);
Innocent-looking isn't it? It expands to this:
Given that, do you /really/ need that extra layer of complexity via that >macro?
If I had to write 17 loops over SQLite hashes, I'd rather type
for_sqlite_hash(p, hash) than
for (p = sqliteHashFirst(hash); p; p = sqliteHashNext(hash)).
I guess that answers that question! I'd use macros only as a last
resort; you would use them at the first opportunity.
On 2025-04-21, bart <bc@freeuk.com> wrote:
On 21/04/2025 19:43, Kaz Kylheku wrote:
On 2025-04-21, bart <bc@freeuk.com> wrote:
On 21/04/2025 04:16, Kaz Kylheku wrote:
- Because they are not gathered in one place, not only is it less>readable, but we cannot use while write a macro such as:
for_sqlite_hash (p, &pSchema->trigHash) {
if (some_condition_over(p))
continue; // doesn't stupidly repeat for the same p!
}
I can't write such macros at all. I'm not even sure what this does.
Have you never worked with a large codebase written by someone other
than you?
How large are we talking about?
Several 100K to millions.
I've delved into largish apps in the context of getting my C compiler
working. I would describe that experience as 'brutal'.
Well, that brutal experience is the job of the career software engineer, believe it or not. Very few work only on their own original code.
If you need to
debug someone else's codebase, not to find bugs in that program, but to
find why your implementation is failing, then you want as conservative a
coding style as possible.
When you open a random file in an unfamiliar code base, pretty
much any function call that is not in the standard library triggers
the "I don't know what this does" response.
Yes, a function call. Macros are in an entirely different, evil category.
No, they aren't. They are just another definition to understand.
They can be misused, and also used to make solutions that are more complicated than some great ideas that don't involve macros.
So can anything: an open coded function can be misused to make some complicated solution that can be more nicely done with macros.
The Lua sources (only a mere 30Kloc), use macros extensively, but also
have a habit of giving them ordinary looking names in lower case so that
they look like function calls.
So does ISO C; e.g. assert (expr); offsetof (type, member). So what?
Macros that provide syntax should blend into the language.
Please humour me: What Does It Do?
It is intended to condense an interation that *you* open-coded, in this example upthread:
p = sqliteHashFirst(&pSchema->trigHash);
while (p != NULL)
{
sqlite3DeleteTrigger(db, (Trigger*)sqliteHashData(p));
p = sqliteHashNext(p) )
}
So although I didn't show the definition, I think, of course the intent that it
does this (i.e. is relevant to the discussion thread) and not something else.
for_sqlite_hash (p, hashptr) stmet
must initialize p using sqliteHashFirst(hashPtr);
If I had to write 17 loops over SQLite hashes, I'd rather type for_sqlite_hash(p, hash) than
for (p = sqliteHashFirst(hash); p; p = sqliteHashNext(hash)).
On 2025-04-21, bart <bc@freeuk.com> wrote:
I don't now think think there is any argument that will make any
difference. People here genuinely think that writing:
for (ch = 0; ch <= 255; ++ch)
is far superior to this 'primitive' version:
do ch = 0, 255
Obviously, the former is more primitive, because the second can
be made out of the first with a preprocessor, whereas the converse
is not true. If A can be made out of B, but B not out of A,
B is more primitive.
The former is undeniably more verbose for the uses provided by the
second.
It is also much more flexible.
Thanks to the macro preprocessor, it can almost express the second form,
just with more punctuation:
for_range (ch, 0, 255)
If a built-in "do" range loop existed in C, it would likely have
the parentheses.
No amount of discussion or arguments will make them change their minds.
Mostly, you are projecting onto people opinions they don't actually
have.
You're also not a fan of unbridled language extension, based on your
past opinions about, oh, C++ and whatnot.
Your primary reaction to some new idea is to reject it as complex
fluff that you'd rather not undertand.
How brushed up are you in using C17 or C23?
You've also expressed oppositions to extending C, because
development of C makes it a moving target for your projects.
Of course people are going to push back on the idea of making new>statements, if they can be decently obtained or simulated using
preprocessing.
Would you support an "unless" statement being added to C, given
that we can just:
#define unless(x) if(!(x))
Or, unless which does not take else:
#define unless(x) if (x) { } else
Adding a new statement to C is a big deal, compared to someone
adding a macro to their code.
Literally the first question about any proposal for a new language
feature is going to be: can obtain that, or substantially obtain it
using existing features
On 21/04/2025 21:25, Kaz Kylheku wrote:
On 2025-04-21, bart <bc@freeuk.com> wrote:
On 21/04/2025 19:43, Kaz Kylheku wrote:
On 2025-04-21, bart <bc@freeuk.com> wrote:
On 21/04/2025 04:16, Kaz Kylheku wrote:
- Because they are not gathered in one place, not only is it less>readable, but we cannot use while write a macro such as:
for_sqlite_hash (p, &pSchema->trigHash) {
if (some_condition_over(p))
continue; // doesn't stupidly repeat for the same p!
}
I can't write such macros at all. I'm not even sure what this does.
Have you never worked with a large codebase written by someone other
than you?
How large are we talking about?
Several 100K to millions.
Characters, lines or files of source code, or bytes of binary?
I've delved into largish apps in the context of getting my C compiler
working. I would describe that experience as 'brutal'.
Well, that brutal experience is the job of the career software engineer,
believe it or not. Very few work only on their own original code.
If you need toNo, they aren't. They are just another definition to understand.
debug someone else's codebase, not to find bugs in that program, but to
find why your implementation is failing, then you want as conservative a >>> coding style as possible.
When you open a random file in an unfamiliar code base, pretty
much any function call that is not in the standard library triggers
the "I don't know what this does" response.
Yes, a function call. Macros are in an entirely different, evil category. >>
I've seen a 33KB C file that expanded to 4MB after preprocessing. Macros
are evil.
They can be misused, and also used to make solutions that are more
complicated than some great ideas that don't involve macros.
You can tell when there is abuse going on, because you find that instead
of optimising doubling the speed of a program, it will make it 3-4 times
as fast. Because layers of macros hide the fact that that there are also multiple layers of functions which need serious inlining.
So can anything: an open coded function can be misused to make some
complicated solution that can be more nicely done with macros.
The Lua sources (only a mere 30Kloc), use macros extensively, but also
have a habit of giving them ordinary looking names in lower case so that >>> they look like function calls.
So does ISO C; e.g. assert (expr); offsetof (type, member). So what?
Macros that provide syntax should blend into the language.
These didn't provide syntax.
Here's a nice example:
op_arith(L, l_addi, luai_numadd);
Innocent-looking isn't it? It expands to this:
{TValue*v1=(&((base+((((int)((((i)>>((((0+7)+8)+1)))&((~((~(Instruction)0)<<(8)))<<(0)))))))))->val);TValue*v2=(&((base+((((int)((((i)>>(((((0+7)+8)+1)+8)))&((~((~(Instruction)0)<<(8)))<<(0)))))))))->val);{StkId((n2)=((lua_Number)(((((v2)->value_).i)))),1):0))){pc++;{TValue*io=((&(ra)->val));((io)->value_).n=(((n1)+(n2)));((io)->tt_=(((3)|((1)<<4))));};}};};};
ra=(base+(((int)((((i)>>((0+7)))&((~((~(Instruction)0)<<(8)))<<(0)))))));if(((((v1))->tt_)==(((3)|((0)<<4))))&&((((v2))->tt_)==(((3)|((0)<<4))))){lua_Integer
i1=(((v1)->value_).i);lua_Integer i2=(((v2)->value_).i);pc++;{TValue*io=((&(ra)->val));((io)->value_).i=(((lua_Integer)(((lua_Unsigned)(i1))+((lua_Unsigned)(i2)))));((io)->tt_=(((3)|((0)<<4))));};}else{lua_Number
n1;lua_Number n2;if((((((v1))->tt_)==(((3)|((1)<<4))))?((n1)=(((v1)->value_).n),1):(((((v1))->tt_)==(((3)|((0)<<4))))?((n1)=((lua_Number)(((((v1)->value_).i)))),1):0))&&(((((v2))->tt_)==(((3)|((1)<<4))))?((n2)=(((v2)->value_).n),1):(((((v2))->tt_)==(((3)|((0)<<4))))?
From this, I had to find the bug in my compiler.
(Lua is an interpreter; I write interpreters and they are several times faster than Lua for equivalant inputs. I don't need to use such tricks.)
bart <bc@freeuk.com> writes:
On 21/04/2025 22:06, Waldek Hebisch wrote:
bart <bc@freeuk.com> wrote:
t iteration goes over all elements in the hash table.
BTW2: When looking at 'for' loop you are supposed to see pattern,
without need to track all steps.
That's one of the disadvantages of using the same, often
/inappropriate/ keyword for every kind of pattern.
You think it's inappropriate.
Would your objections go away if a different keyword were used?
*You* find it much cleaner and simpler. I don't. What makes you right
and everyone else wrong?
All three would be IMHO clearer if each of the three clauses were on a separate line. The fact that you can write a badly formatted C-style
for loop is not an argument against C-style for loops.
bart <bc@freeuk.com> wrote:
You have to analyse it first. The kind of loop this expresses is:
p = startvalue()
while (p) {
<body>
p = nextvalue()
}
Notice how I chose to express it: it reflects how I would describe it in
English:
* Set P to Start Value
* While P isn't Null:
* Execute the body
* Set P to Next Value
So, how would /you/ describe it in English? (Or in any language if like,
as the ordering is more important.)
I would describe original sqlite3.c loop as "iteration over elements
of hash table". That assumes that Sqlite folks choose sensible
names. This is higher level view than you apparently have.
bart <bc@freeuk.com> wrote:
On 21/04/2025 21:25, Kaz Kylheku wrote:
On 2025-04-21, bart <bc@freeuk.com> wrote:
On 21/04/2025 19:43, Kaz Kylheku wrote:
On 2025-04-21, bart <bc@freeuk.com> wrote:
On 21/04/2025 04:16, Kaz Kylheku wrote:Have you never worked with a large codebase written by someone other >>>>> than you?
- Because they are not gathered in one place, not only is it less> >>>>>> readable, but we cannot use while write a macro such as:
for_sqlite_hash (p, &pSchema->trigHash) {
if (some_condition_over(p))
continue; // doesn't stupidly repeat for the same p! >>>>>>> }
I can't write such macros at all. I'm not even sure what this does. >>>>>
How large are we talking about?
Several 100K to millions.
Characters, lines or files of source code, or bytes of binary?
I would say lines of code. In modern time 1 million characters
codebase is not big. I hope that we are not in era of 1 million
files codebases, but who knows, we may be there in few years.
On 21/04/2025 23:16, Kaz Kylheku wrote:
On 2025-04-21, bart <bc@freeuk.com> wrote:
No amount of discussion or arguments will make them change their minds.
Mostly, you are projecting onto people opinions they don't actually
have.
Read the thread. Nobody has changed their opinion on C for-loops, or
agreed with its various problems, or thought that a streamlined loop
would be a welcome.
(Only Keith cautiously welcome the idea of such a feature, while MS said
he would vote against it, and JP said they would have proposed it on
April 1st.)
You're also not a fan of unbridled language extension, based on your
past opinions about, oh, C++ and whatnot.
I don't agree with language-building features. You just end up with a monstrosity like C++.
Your primary reaction to some new idea is to reject it as complex
fluff that you'd rather not undertand.
How brushed up are you in using C17 or C23?
C23 is nothing particularly interesting. A few features I wont't be able
to rely on until I'm dead.
You've also expressed oppositions to extending C, because
development of C makes it a moving target for your projects.
As I said, development of is not that interesting. Basically sticking
plaster fixes.
Of course people are going to push back on the idea of making new>statements, if they can be decently obtained or simulated using
preprocessing.
Would you support an "unless" statement being added to C, given
that we can just:
#define unless(x) if(!(x))
Or, unless which does not take else:
#define unless(x) if (x) { } else
Adding a new statement to C is a big deal, compared to someone
adding a macro to their code.
You can't /just/ add such a macro. To be useful, it has to be
standardised.
Literally the first question about any proposal for a new language
feature is going to be: can obtain that, or substantially obtain it
using existing features
I think C's macros are the main thing that have hindered its development.
Why add a new core feature, when some tacky, half-assed macro can be
used? Or, sometimes, a typedef.
There is no need to add a formal lengthof() operator when every user can create their own variation of ARRAYLEN in a few seconds.
Result: most people still end up writing
sizeof(myarray)/sizeof(myarray[0]).
In the meantime, I'm been able to
write myarray.len for 43 years, and also:
print a, b # who cares about format codes
As for MIN/MAX, I routinely use:
max(a, b)
max(x, y) # overloaded per type
a max:=b # augmented assignment
a.max # maximum value of a's type
T.max # maximum value of type T
Oh, and I already have Unless:
unless a then b end
return 0 unless cond
On 20/04/2025 17:46, Janis Papanagnou wrote:
On 20.04.2025 13:43, bart wrote:
On 20/04/2025 11:18, Janis Papanagnou wrote:
On 19.04.2025 15:05, bart wrote:
But overloading in the form of 30-40 integer types to represent the 8
types i8-i64 and u8-u64, plus the 16 different combinations of writing
'unsigned long long int', and so on, is fine. As are the dozens
(hundreds?) of macros in stdint.h/inttypes.h.
I haven't said this "is fine". It wasn't at all the topic here.
Sorry, I forgot the rules. [...]
In C? I don't recall any examples in C that could be written without 'for'.
It makes not much sense to spread them across a multi-line block
of statements. - If you haven't ever experienced that, and still
cannot see and understand that if explained to you, then I can't
help you.[*]
Here's the example you snipped:
for(p=sqliteHashFirst(&pSchema->trigHash); p; p=sqliteHashNext(p)){
sqlite3DeleteTrigger(db, (Trigger*)sqliteHashData(p));
}
In my opinion, that for-header is too complex. You can barely make out
the condition (that lonely 'p' which you have to hunt for).
The point is that if you have programming elements that are
related to a loop construct (initialization, exit condition,
update for next iteration) it makes sense to keep them together.
So what about this then (another example you snipped):
for(p=sqliteHashFirst(&pSchema->trigHash); p; sqlite3DeleteTrigger(db, (Trigger*)sqliteHashData(p)), p=sqliteHashNext(p));
It keeps it even more together, which you seem to like.
[...]
I think it is YOU whose brain is so bound to having just the ONE kind of
loop to do half a dozen different jobs, none of them well.
[...]
Why do you again make
up things. - The point is to keep things together that belong
together.
Why are you so crazy about putting everything onto one line? [...]
You're
saying that:
ONE; TWO; THREE
is always better than:
ONE;
TWO;
THREE;
[...]
This is an example which I started off trying to simplify:
for (monthIdx = 0; monthIdx < 12 && yearDay >=
yearDays[leapYear][monthIdx]; monthIdx++) ;
I simply can't see it.
So the first step is to turn it into a while loop:
monthIdx = 0;
while (monthIdx < 12 && yearDay >= yearDays[leapYear][monthIdx])
monthIdx++;
Now at least I can see the condition! I'm sorry, but if prefer the
original, then I don't want to have to debug or maintain your code.
[...]
I just don't get the love. Or maybe, nobody here (perhaps apart from
Keith) is willing to admit that there's anything wrong with 'for'.
[...]
On 21/04/2025 14:51, Waldek Hebisch wrote:
[ sqlite3.c code ]
You have to analyse it first. The kind of loop this expresses is:
p = startvalue()
while (p) {
<body>
p = nextvalue()
}
Notice how I chose to express it: it reflects how I would describe it in English:
* Set P to Start Value
* While P isn't Null:
* Execute the body
* Set P to Next Value
So, how would /you/ describe it in English? (Or in any language if like,
as the ordering is more important.)
[...]
However I see that nobody in this group has any objective interest in language ergonomics and aesthetics.
They more interested in defending 'their' language.
Janis Papanagnou <janis_papanagnou+ng@hotmail.com> writes:
On 21.04.2025 23:21, Keith Thompson wrote:
[...]
C-style for loops have been used successfully for decades, and have
been adopted by other languages (including bash, which isn't
particularly C-like).
I have to disagree on that. First I'm positive that Bash adopted
the Ksh loops (but incompletely!), and not the "C" loops.
Bash has two kinds of for loops. From the bash manual:
for NAME [ [in WORDS ...] ; ] do COMMANDS; done
and
for (( EXPR1 ; EXPR2 ; EXPR3 )) [;] do COMMANDS ; done
I'm referring to the second form, as I'm sure you know.
I see that ksh has something very similar.
I have no idea which shell
added the second form earlier, but both are clearly derived from C,
directly or indirectly.
[...]
[...][...] But you can still do things that
you can't do with a simple counted for loop, for example printing
powers of 3:
for (( i = 1; i < 1000; i *= 3 )) ; do echo $i ; done
On 20/04/2025 23:36, Keith Thompson wrote:
bart <bc@freeuk.com> writes:
for(p=sqliteHashFirst(&pSchema->trigHash); p; p=sqliteHashNext(p)){ >>> sqlite3DeleteTrigger(db, (Trigger*)sqliteHashData(p));
}
I might write it like this:
for ( p = sqliteHashFirst(&pSchema->trigHash);
p != NULL;
p = sqliteHashNext(p) )
{
sqlite3DeleteTrigger(db, (Trigger*)sqliteHashData(p));
}
I have certain preferences (spaces around most operators, explicit
comparison to NULL, willingness to split long lines) that other C
programmers may or may not share.
I rarely see complex loops split over multiple lines
(mainly when they
get so long that they'd overflow the line, but they can still be complex enough before then).
But the thing is, once you split it into multiple lines, then there is
little advantage over using a regular 'while' loop:
[...]
It keeps it even more together, which you seem to like.
That's something you invented. I find it ugly, and I presume you'd
agree. The fact that you think that someone else would prefer it
indicates that you don't understand how other people think.
AFAIK it is legal C code, and I invented it because somebody said things
that belong together should be together in one place.
However, I have
seen actual examples like that, in for-headers that that use
comma-separated expressions.
I'd rather not write `for (ch in 0..255)` because it's a syntax error.
It's a syntax error because the form doesn't naturally exist in C; you'd
have to emulate using macros, which is a poor solution.
You have the luxury of using your own language.
That 'ch in 0..255' form or close equivalent is supported by that long
set of languages I listed earlier. It's not my invention, I just copied it.
It is just something that is desirable. Look again at the C version: it
looks off. (At least, you'd use a shorter loop index!)
[...]
If you don't like C-style for loop, they absolutely should not
exist in a language for which you are, if I understand correctly,
the sole implementer and the sole user.
But I hear so much about how wonderful it is, how multi-purpose, how indispensible, how superior to an ordinary 'for' (admittedly from people
who don't have a choice) that I didn't want to miss out!
bart <bc@freeuk.com> writes:
I guess that answers that question! I'd use macros only as a last
resort; you would use them at the first opportunity.
Obviously the entire world-wide programming community
disagrees with your opinions.
On 21/04/2025 22:06, Waldek Hebisch wrote:
[...]
The intent of 'for' is to iterate over some collection. Each of
A, B, C is needed to know the collection.
Sure, but ALL ON THE SAME LINE?
All within the same parentheses?
Putting multiple things on the same line is usually frowned upon.
Look at these ludicrous examples:
[...]
bart <bc@freeuk.com> wrote:
On 21/04/2025 21:25, Kaz Kylheku wrote:
On 2025-04-21, bart <bc@freeuk.com> wrote:
On 21/04/2025 19:43, Kaz Kylheku wrote:
Have you never worked with a large codebase written by someone other >>>>> than you?
How large are we talking about?
Several 100K to millions.
Characters, lines or files of source code, or bytes of binary?
I would say lines of code. In modern time 1 million characters
codebase is not big. I hope that we are not in era of 1 million
files codebases, but who knows, we may be there in few years.
On 22/04/2025 00:12, Keith Thompson wrote:
[...]
This is an interesting point: too much stuff on one line is usually
frowned upon: multiple statements, multiple variables being declared etc.
But when it's a for-loop, then apparently anything goes.
All three would be IMHO clearer if each of the three clauses were on a
separate line. The fact that you can write a badly formatted C-style
for loop is not an argument against C-style for loops.
Complex loops are nearly always badly formatted and written on one line. Nobody cares. As you've demonstrated.
bart <bc@freeuk.com> wrote:
I've seen a 33KB C file that expanded to 4MB after preprocessing. Macros
are evil.
If macros were forbiden authors would probably write 4MB "by hand"
(or rather using editor cut and paste). Actually, creating first
version probably would be not that hard, but maintanence would
take a lot of effort (due to volume).
Of course, macro generated code is likely to contain significant
fraction of useless instructions, but removing them by hand would
be substantial work. And trying to directly produce trimmed
version would need a lot of work. It is much cheaper to
depend on optimizer to remove redundancies and useless instructions.
Assuming that hand written version would be 80% smaller, you would
have 800KB to write, that is more than 20 times the original
source. So in this case macros probably saved about 95% of
work needed when coding without macros.
?((n2)=((lua_Number)(((((v2)->value_).i)))),1):0))){pc++;{TValue*io=((&(ra)->val));((io)->value_).n=(((n1)+(n2)));((io)->tt_=(((3)|((1)<<4))));};}};};};Here's a nice example:
op_arith(L, l_addi, luai_numadd);
Innocent-looking isn't it? It expands to this:
{TValue*v1=(&((base+((((int)((((i)>>((((0+7)+8)+1)))&((~((~(Instruction)0)<<(8)))<<(0)))))))))->val);TValue*v2=(&((base+((((int)((((i)>>(((((0+7)+8)+1)+8)))&((~((~(Instruction)0)<<(8)))<<(0)))))))))->val);{StkId
ra=(base+(((int)((((i)>>((0+7)))&((~((~(Instruction)0)<<(8)))<<(0)))))));if(((((v1))->tt_)==(((3)|((0)<<4))))&&((((v2))->tt_)==(((3)|((0)<<4))))){lua_Integer
i1=(((v1)->value_).i);lua_Integer
i2=(((v2)->value_).i);pc++;{TValue*io=((&(ra)->val));((io)->value_).i=(((lua_Integer)(((lua_Unsigned)(i1))+((lua_Unsigned)(i2)))));((io)->tt_=(((3)|((0)<<4))));};}else{lua_Number
n1;lua_Number
n2;if((((((v1))->tt_)==(((3)|((1)<<4))))?((n1)=(((v1)->value_).n),1):(((((v1))->tt_)==(((3)|((0)<<4))))?((n1)=((lua_Number)(((((v1)->value_).i)))),1):0))&&(((((v2))->tt_)==(((3)|((1)<<4))))?((n2)=(((v2)->value_).n),1):(((((v2))->tt_)==(((3)|((0)<<4))))
From this, I had to find the bug in my compiler.
Not bad, I sometimes had to look for bugs in much bigger piece of
code.
(Lua is an interpreter; I write interpreters and they are several times
faster than Lua for equivalant inputs. I don't need to use such tricks.)
IIUC Lua has some interesting properties. Above I see masking and
tag manipulations, presumably this is needed to implement Lua
properties.
I would expect that Lua has more than one artithemetic
operation, so sharing code saves coding effort (actually, most
of tag mainipulations probably can be shared by all Lua instructions).
bart <bc@freeuk.com> writes:
On 22/04/2025 00:12, Keith Thompson wrote:
bart <bc@freeuk.com> writes:
On 21/04/2025 22:06, Waldek Hebisch wrote:You think it's inappropriate.
bart <bc@freeuk.com> wrote:
t iteration goes over all elements in the hash table.
BTW2: When looking at 'for' loop you are supposed to see pattern, >>>>> without need to track all steps.
That's one of the disadvantages of using the same, often
/inappropriate/ keyword for every kind of pattern.
Would your objections go away if a different keyword were used?
I already said that I would have prefered if 'while' was used.
Then either 'for' wasn't used, or was used for the kind of for-loop
that is common in other languages.
So you wouldn't mind if C had both `while (expr)` and
`while (expr1; expr2; expr3)`, with the latter having the semantics
of C's current for loop?
But that doesn't answer my question. You'd prefer "while", but would
using "while" rather than "for" resolve your objections?
For example, in my post to which you replied, I discussed at
some length how I like to split for loops if they're too long.
You snipped that discussion *and then* insinuated that I don't care
about complex loops written on one line. Please stop doing things
like that.
On 22.04.2025 00:54, bart wrote:
Look at these ludicrous examples:
I suggest to just not make up ludicrous examples. We would certainly
all be happy if you'd stop that.
On 2025-04-22, bart <bc@freeuk.com> wrote:
C23 is nothing particularly interesting. A few features I wont't be able
to rely on until I'm dead.
Thus if C got, today, the loop features you advocate for, you wouldn't
use them; they would necessarily be after C23.
The time to lobby for them was 30-40 years ago, and in the right way.
You've also expressed oppositions to extending C, because
development of C makes it a moving target for your projects.
As I said, development of is not that interesting. Basically sticking
plaster fixes.
But that's exactly what some for loop from A to B would be, though.
No; it just has to be defined at the top of a file which needs it.
All of a sudden, something has to be in a document you can't be
bothered to read in order to be useful?
Your own stuff is not standardized; presumably you worked on
it because you find it useful.
I think C's macros are the main thing that have hindered its development.
C macros definitely hinder all kinds of tooling.
Result: most people still end up writing
sizeof(myarray)/sizeof(myarray[0]).
This leads to a bug when myarray is actually a pointer.
GCC has a warning for sizeof ptr / sizeof ptr[0] specifically
to catch bugs in this idiom.
It's pretty gross. To my understanding there is some initiative
to get some array length operator.
Of course, you will never use it.
In the meantime, I'm been able to
write myarray.len for 43 years, and also:
print a, b # who cares about format codes
As for MIN/MAX, I routinely use:
max(a, b)
max(x, y) # overloaded per type
a max:=b # augmented assignment
a.max # maximum value of a's type
T.max # maximum value of type T
Oh, and I already have Unless:
unless a then b end
return 0 unless cond
Not only you, but countless term projects in undergraduate compiler
class. So what?
On 22/04/2025 04:31, Kaz Kylheku wrote:
The time to lobby for them was 30-40 years ago, and in the right way.
I'm pretty sure I started complaining here 20 years ago! Perhaps more.
Your own stuff is not standardized; presumably you worked on
it because you find it useful.
It is standardised in that no basic language feature, no standard
library function, needs some declaration to be used.
proc main =
puts("hello"
end
In C that would need '#include <stdio>'. Here, 'puts' is defined in a
file which is part of my standard library which is automatically
included by default.
Not only you, but countless term projects in undergraduate compiler
class. So what?
Well, exactly. So how come we ended up with C as the world's primary
systems language? Why hasn't C long acquired such useful core features?
On 2025-04-22, bart <bc@freeuk.com> wrote:
On 22/04/2025 04:31, Kaz Kylheku wrote:
The time to lobby for them was 30-40 years ago, and in the right way.
I'm pretty sure I started complaining here 20 years ago! Perhaps more.
That's not the "right way" though; that has no hope of producing
results.
Your own stuff is not standardized; presumably you worked on
it because you find it useful.
It is standardised in that no basic language feature, no standard
library function, needs some declaration to be used.
I like that. I would never design a language like Python where you need
to "import" stuff that is already built in.
That is not a language.
Before speaking to you, I don't need to say, "from English import
articles; from articles import the, a".
I hope you are remembering all the times I agree with you.
(I don't agree with your definition of "standardized"; that refers
to being documented, in detail, so that multiple implementations
are encouraged to interoperate (exchange source code)).
proc main =
puts("hello"
end
In C that would need '#include <stdio>'. Here, 'puts' is defined in a
file which is part of my standard library which is automatically
included by default.
On 21/04/2025 23:16, Kaz Kylheku wrote:
On 2025-04-21, bart <bc@freeuk.com> wrote:
I don't now think think there is any argument that will make any
difference. People here genuinely think that writing:
   for (ch = 0; ch <= 255; ++ch)
is far superior to this 'primitive' version:
   do ch = 0, 255
Obviously, the former is more primitive, because the second can
be made out of the first with a preprocessor, whereas the converse
is not true. If A can be made out of B, but B not out of A,
B is more primitive.
The former is undeniably more verbose for the uses provided by the
second.
It is also much more flexible.
Thanks to the macro preprocessor, it can almost express the second form,
just with more punctuation:
  for_range (ch, 0, 255)
If a built-in "do" range loop existed in C, it would likely have
the parentheses.
No amount of discussion or arguments will make them change their minds.
Mostly, you are projecting onto people opinions they don't actually
have.
Read the thread. Nobody has changed their opinion on C for-loops, or
agreed with its various problems, or thought that a streamlined loop
would be a welcome.
(Only Keith cautiously welcome the idea of such a feature, while MS said
he would vote against it, and JP said they would have proposed it on
April 1st.)
You're also not a fan of unbridled language extension, based on your
past opinions about, oh, C++ and whatnot.
I don't agree with language-building features. You just end up with a monstrosity like C++.
Your primary reaction to some new idea is to reject it as complex
fluff that you'd rather not undertand.
How brushed up are you in using C17 or C23?
C23 is nothing particularly interesting. A few features I wont't be able
to rely on until I'm dead.
You've also expressed oppositions to extending C, because
development of C makes it a moving target for your projects.
As I said, development of is not that interesting. Basically sticking
plaster fixes.
Of course people are going to push back on the idea of making new>statements, if they can be decently obtained or simulated using
preprocessing.
Would you support an "unless" statement being added to C, given
that we can just:
#define unless(x) if(!(x))
Or, unless which does not take else:
#define unless(x) if (x) { } else
Adding a new statement to C is a big deal, compared to someone
adding a macro to their code.
You can't /just/ add such a macro. To be useful, it has to be
standardised. And then, you'd probably need #include <stdunless.h> to
use it.
Literally the first question about any proposal for a new language
feature is going to be: can obtain that, or substantially obtain it
using existing features
I think C's macros are the main thing that have hindered its development.
Why add a new core feature, when some tacky, half-assed macro can be
used? Or, sometimes, a typedef.
At one time, every other app I looked at defined its own ARRAYLEN macro,
or MIN/MAX, or STREQ.
There is no need to add a formal lengthof() operator when every user can create their own variation of ARRAYLEN in a few seconds.
Result: most people still end up writing
sizeof(myarray)/sizeof(myarray[0]).
In the meantime, I'm been able to
write myarray.len for 43 years, and also:
  print a, b      # who cares about format codes
As for MIN/MAX, I routinely use:
  max(a, b)
  max(x, y)       # overloaded per type
  a max:=b        # augmented assignment
  a.max           # maximum value of a's type
  T.max           # maximum value of type T
Oh, and I already have Unless:
  unless a then b end
  return 0 unless cond
It is not a macro.
proc main =
puts("hello"
end
In C that would need '#include <stdio>'. Here, 'puts' is defined in a
file which is part of my standard library which is automatically
included by default.
Which precludes user overrides of the standard library at compile
time or run-time.
On 22/04/2025 17:10, Scott Lurndal wrote:
proc main =
puts("hello"
end
In C that would need '#include <stdio>'. Here, 'puts' is defined in a
file which is part of my standard library which is automatically
included by default.
Which precludes user overrides of the standard library at compile
time or run-time.
I said 'by default'. Obviously there are ways of replacing the library
or leaving it out completely.
On 22/04/2025 02:06, Waldek Hebisch wrote:
bart <bc@freeuk.com> wrote:
I've seen a 33KB C file that expanded to 4MB after preprocessing. Macros >>> are evil.
If macros were forbiden authors would probably write 4MB "by hand"
(or rather using editor cut and paste). Actually, creating first
version probably would be not that hard, but maintanence would
take a lot of effort (due to volume).
It would probably be done by a script. This resulting code is not
something to be written or consumed by a human. Which is an odd thing to
say about a HLL.
The example below is not as bad: the lines are only up to 1K chars
instead of 50K.
(I thought my own generated C code was poor; it's actually a million
times more readable.)
Of course, macro generated code is likely to contain significant
fraction of useless instructions, but removing them by hand would
be substantial work. And trying to directly produce trimmed
version would need a lot of work. It is much cheaper to
depend on optimizer to remove redundancies and useless instructions.
How about writing decent, sensible code in the first place?
Assuming that hand written version would be 80% smaller, you would
have 800KB to write, that is more than 20 times the original
source. So in this case macros probably saved about 95% of
work needed when coding without macros.
You haven't seen this example, you don't know how bad it is, or how
effective it was as a solution to whatever problem it was trying to
solve, and yet YOU'RE MAKING EXCUSES FOR IT!
This is a really odd phenomenon: is there no C code on the planet, that anybody here will admit that is badly written? Or that might be forced
into being badly written due to shortcomings of the language?
Here's a challenge for everyone here: please post an example, or a link
to an example, that you think is truly appalling code.
)?((n2)=((lua_Number)(((((v2)->value_).i)))),1):0))){pc++;{TValue*io=((&(ra)->val));((io)->value_).n=(((n1)+(n2)));((io)->tt_=(((3)|((1)<<4))));};}};};};Here's a nice example:
        op_arith(L, l_addi, luai_numadd);
Innocent-looking isn't it? It expands to this:
{TValue*v1=(&((base+((((int)((((i)>>((((0+7)+8)+1)))&((~((~(Instruction)0)<<(8)))<<(0)))))))))->val);TValue*v2=(&((base+((((int)((((i)>>(((((0+7)+8)+1)+8)))&((~((~(Instruction)0)<<(8)))<<(0)))))))))->val);{StkId
ra=(base+(((int)((((i)>>((0+7)))&((~((~(Instruction)0)<<(8)))<<(0)))))));if(((((v1))->tt_)==(((3)|((0)<<4))))&&((((v2))->tt_)==(((3)|((0)<<4))))){lua_Integer
i1=(((v1)->value_).i);lua_Integer
i2=(((v2)->value_).i);pc++;{TValue*io=((&(ra)->val));((io)->value_).i=(((lua_Integer)(((lua_Unsigned)(i1))+((lua_Unsigned)(i2)))));((io)->tt_=(((3)|((0)<<4))));};}else{lua_Number
n1;lua_Number
n2;if((((((v1))->tt_)==(((3)|((1)<<4))))?((n1)=(((v1)->value_).n),1):(((((v1))->tt_)==(((3)|((0)<<4))))?((n1)=((lua_Number)(((((v1)->value_).i)))),1):0))&&(((((v2))->tt_)==(((3)|((1)<<4))))?((n2)=(((v2)->value_).n),1):(((((v2))->tt_)==(((3)|((0)<<4)))
 From this, I had to find the bug in my compiler.
Not bad, I sometimes had to look for bugs in much bigger piece of
code.
So that means there's nothing wrong with it? Remember this is one
expression.
(Lua is an interpreter; I write interpreters and they are several times
faster than Lua for equivalant inputs. I don't need to use such tricks.)
IIUC Lua has some interesting properties. Above I see masking and
tag manipulations, presumably this is needed to implement Lua
properties.
Here you are making excuses again. Yes, Lua uses tags on some 64-bit
value; so what? That can be done using ordinary code. Or if macros have
to be used, they can be simple too.
My own product uses apparently inefficient 128-bit values, it does
everything 'wrong' but it uses readable HLL code with minimal use of
macros, and yet it can run at several times the speed of Lua even unoptimised. You don't need to write gobbledygook.
BTW here is that macro definition from Lua:
 #define op_arith(L,iop,fop) { \
  TValue *v1 = vRB(i); \
  TValue *v2 = vRC(i); \
  op_arith_aux(L, v1, v2, iop, fop); }
It calls yet more macros, and those in turn call others; here are a few
for vRB:
 #define vRB(i)   s2v(RB(i))
 #define s2v(o)   (&(o)->val)
 #define RB(i)   (base+GETARG_B(i))
 #define GETARG_A(i)   getarg(i, POS_A, SIZE_A)
 #define getarg(i,pos,size)   (cast_int(((i)>>(pos)) & MASK1(size,0)))
 #define cast_int(i)   cast(int, (i))
 #define MASK1(n,p)   ((~((~(Instruction)0)<<(n)))<<(p))
 #define cast(t, exp)   ((t)(exp))
I think the maximum depth here is 7-deep:
 op_arith -> vRB -> RB -> GETARG -> getarg -> cast_int -> cast
This is not normal coding; it has replaced using functions to build
programs, with macros. And it has replaced coding in C, with
functional-style coding via the macro language.
So what language would say this program is written in?
 I would expect that Lua has more than one artithemetic
operation, so sharing code saves coding effort (actually, most
of tag mainipulations probably can be shared by all Lua instructions).
Any reason to uses *seven* levels of macro invocation? That is all in
one branch.
Do you think that's too many? Perhaps only 3 levels would have sufficed.
Or would you automatically say it was reasonable no matter how many
levels there were, just to disagee with me and to side with a fellow C programmer?
On 22/04/2025 09:54, Janis Papanagnou wrote:
On 21.04.2025 17:36, bart wrote:
On 21/04/2025 14:51, Waldek Hebisch wrote:
[ sqlite3.c code ]
You have to analyse it first. The kind of loop this expresses is:
No. That's just *your* _transcription_ of the original code
   p = startvalue()
   while (p) {
      <body>
      p = nextvalue()
   }
Notice how I chose to express it: it reflects how I would describe it in >>> English:
You are *not* describing _the task_, you are merely translating the
_chosen commands_ of the procedural language [that you have chosen]
   * Set P to Start Value
   * While P isn't Null:
     * Execute the body
     * Set P to Next Value
Whatever programming language and constructs you use, this is not
more than a rephrasing the used language constructs (as opposed to
the task).
(It's not surprising; you can observe that folks that are familiar
only with imperative languages tend to this sort of fallacy.)
So, how would /you/ describe it in English? (Or in any language if like, >>> as the ordering is more important.)
Iterate over the elements and sequentially perform <the task> on
the elements. Or a bit more formally
  iterate (elements, task)
in some functional programming language. Or any other variant on
any abstraction level that is appropriate.
Alternatively, you might describe it as "Perform the task on each
element" :
    task(e) for e in elements
Sometimes you want to emphasise moving through the elements, sometimes
you want to emphasise what you are doing with them - but either way is
fine.
You may or may not include an order in the iteration, depending on what
you actually want to do here (I have no idea of the purpose of the
original code).
I would suggest Bart thinks this through using a real-world task.
Consider making a cake by mixing the ingredients you have laid out on
your kitchen top. Would you use Janis' formulation?
    Go through the ingredients, putting each in the bowl.
Or mine?
    Put in the bowl each of the ingredients.
Or Bart's ?
    Stand in front of the first ingredient.
    While you have an ingredient in front of you :
       Put the ingredient in the bowl.
       Move to the next ingredient.
On 21.04.2025 17:36, bart wrote:
On 21/04/2025 14:51, Waldek Hebisch wrote:
[ sqlite3.c code ]
You have to analyse it first. The kind of loop this expresses is:
No. That's just *your* _transcription_ of the original code
p = startvalue()
while (p) {
<body>
p = nextvalue()
}
Notice how I chose to express it: it reflects how I would describe it in
English:
You are *not* describing _the task_, you are merely translating the
_chosen commands_ of the procedural language [that you have chosen]
* Set P to Start Value
* While P isn't Null:
* Execute the body
* Set P to Next Value
Whatever programming language and constructs you use, this is not
more than a rephrasing the used language constructs (as opposed to
the task).
(It's not surprising; you can observe that folks that are familiar
only with imperative languages tend to this sort of fallacy.)
So, how would /you/ describe it in English? (Or in any language if like,
as the ordering is more important.)
Iterate over the elements and sequentially perform <the task> on
the elements. Or a bit more formally
iterate (elements, task)
in some functional programming language. Or any other variant on
any abstraction level that is appropriate.
On 22/04/2025 19:16, David Brown wrote:
On 22/04/2025 09:54, Janis Papanagnou wrote:
On 21.04.2025 17:36, bart wrote:
On 21/04/2025 14:51, Waldek Hebisch wrote:
[ sqlite3.c code ]
You have to analyse it first. The kind of loop this expresses is:
No. That's just *your* _transcription_ of the original code
   p = startvalue()
   while (p) {
      <body>
      p = nextvalue()
   }
Notice how I chose to express it: it reflects how I would describe
it in
English:
You are *not* describing _the task_, you are merely translating the
_chosen commands_ of the procedural language [that you have chosen]
   * Set P to Start Value
   * While P isn't Null:
     * Execute the body
     * Set P to Next Value
Whatever programming language and constructs you use, this is not
more than a rephrasing the used language constructs (as opposed to
the task).
(It's not surprising; you can observe that folks that are familiar
only with imperative languages tend to this sort of fallacy.)
So, how would /you/ describe it in English? (Or in any language if
like,
as the ordering is more important.)
Iterate over the elements and sequentially perform <the task> on
the elements. Or a bit more formally
  iterate (elements, task)
in some functional programming language. Or any other variant on
any abstraction level that is appropriate.
Alternatively, you might describe it as "Perform the task on each
element" :
     task(e) for e in elements
Sometimes you want to emphasise moving through the elements, sometimes
you want to emphasise what you are doing with them - but either way is
fine.
You may or may not include an order in the iteration, depending on
what you actually want to do here (I have no idea of the purpose of
the original code).
I would suggest Bart thinks this through using a real-world task.
Consider making a cake by mixing the ingredients you have laid out on
your kitchen top. Would you use Janis' formulation?
     Go through the ingredients, putting each in the bowl.
Or mine?
     Put in the bowl each of the ingredients.
Or Bart's ?
     Stand in front of the first ingredient.
     While you have an ingredient in front of you :
        Put the ingredient in the bowl.
        Move to the next ingredient.
Well C's would be:
  For (first ingredient; ingredient; next ingredient)
On 2025-04-22, bart <bc@freeuk.com> wrote:
Or would you automatically say it was reasonable no matter how many
levels there were, just to disagee with me and to side with a fellow C
programmer?
You're trying to say that C is a bad language because you can have
7 or more layers of macro expansion, but you're not considering how
bizarre and crippling restriction it would be to put a cap on it.
but you're not considering how
bizarre and crippling restriction it would be to put a cap on it.
On 22/04/2025 20:54, bart wrote:
Well C's would be:
   For (first ingredient; ingredient; next ingredient)
The point was that you claimed your loop matches how you would describe things in English.
BTW here is that macro definition from Lua:
#define op_arith(L,iop,fop) { \
TValue *v1 = vRB(i); \
TValue *v2 = vRC(i); \
op_arith_aux(L, v1, v2, iop, fop); }
It calls yet more macros, and those in turn call others; here are a few
for vRB:
#define vRB(i) s2v(RB(i))
#define s2v(o) (&(o)->val)
#define RB(i) (base+GETARG_B(i))
#define GETARG_A(i) getarg(i, POS_A, SIZE_A)
#define getarg(i,pos,size) (cast_int(((i)>>(pos)) & MASK1(size,0)))
#define cast_int(i) cast(int, (i))
#define MASK1(n,p) ((~((~(Instruction)0)<<(n)))<<(p))
#define cast(t, exp) ((t)(exp))
So what language would say this program is written in?
I would expect that Lua has more than one artithemetic
operation, so sharing code saves coding effort (actually, most
of tag mainipulations probably can be shared by all Lua instructions).
Any reason to uses *seven* levels of macro invocation? That is all in
one branch.
Do you think that's too many? Perhaps only 3 levels would have sufficed.
Or would you automatically say it was reasonable no matter how many
levels there were, just to disagee with me and to side with a fellow C programmer?
On 22/04/2025 20:26, Kaz Kylheku wrote:
On 2025-04-22, bart <bc@freeuk.com> wrote:
Or would you automatically say it was reasonable no matter how many
levels there were, just to disagee with me and to side with a fellow C
programmer?
You're trying to say that C is a bad language because you can have
7 or more layers of macro expansion, but you're not considering how
bizarre and crippling restriction it would be to put a cap on it.
Here, I'm trying to determine if this is the poster's honest, objective opinion, or whether they'd always going to be defend such code no matter what.
but you're not considering how
bizarre and crippling restriction it would be to put a cap on it.
There WILL be a cap. Some deep nesting might be justified in special
cases, for example some recursive macro that builds a string a character
at a time.
But this was not such a case; it was simply decided to make it work
using macros instead of functions.
scott@slp53.sl.home (Scott Lurndal) writes:
[...]
And even then, his complaints would never have gained traction;
the basic syntax of C hasn't changed for forty years, particularly
with respect to the syntax related to the 'for' keyword.
That's not *quite* true. The syntax of the for loop changed
significantly in C99, allowing for a declaration as the first clause
(a feature borrowed from C++).
bart <bc@freeuk.com> writes:
On 22/04/2025 02:22, Keith Thompson wrote:[...]
For example, in my post to which you replied, I discussed at
some length how I like to split for loops if they're too long.
You snipped that discussion *and then* insinuated that I don't care
about complex loops written on one line. Please stop doing things
like that.
Well I care about it too, and I would write such things more sensibly
as well. But so what? Most code I see tends to be badly
formatted. For-headers are not split up unless they're long enough to
overflow the line.
It's something about the culture behind 'for' that encourages poor
coding style.
Did you miss my point, or did you deliberately ignore it?
You snipped part of my text in which I discussed how I like to split
long for loops. You then insinuated that I personally don't care
about complex loops written on one line, after I clearly demonstrated
that I do, and you hid the evidence.
I'm asking you to stop doing that kind of thing.
I'm making a point, not about technical disagreements, but about you, deliberately or otherwise, misrepresenting what I've written.
On 2025-04-22, bart <bc@freeuk.com> wrote:
On 22/04/2025 20:26, Kaz Kylheku wrote:
On 2025-04-22, bart <bc@freeuk.com> wrote:
Or would you automatically say it was reasonable no matter how many
levels there were, just to disagee with me and to side with a fellow C >>>> programmer?
You're trying to say that C is a bad language because you can have
7 or more layers of macro expansion, but you're not considering how
bizarre and crippling restriction it would be to put a cap on it.
Here, I'm trying to determine if this is the poster's honest, objective
opinion, or whether they'd always going to be defend such code no matter
what.
I identified some macros in there that look gratuitous; you just snipped
that part.
but you're not considering how
bizarre and crippling restriction it would be to put a cap on it.
There WILL be a cap. Some deep nesting might be justified in special
cases, for example some recursive macro that builds a string a character
at a time.
But this was not such a case; it was simply decided to make it work
using macros instead of functions.
If you're going to criticize the macros, then hold the code constant.
If it is a given requirement that some code is to be written or
generated, and we have some macros which do that, are those macros a
"good" or "bad" solution?
If you're going to criticize the macros, then hold the code constant.
If it is a given requirement that some code is to be written or
generated, and we have some macros which do that, are those macros a
"good" or "bad" solution?
Why use macros at all?
They might be a big deal in your own language, but in my experience,
largely in C, they seem to cause nothing but grief.
Here, it produces neither faster code nor smaller.
On 2025-04-22, bart <bc@freeuk.com> wrote:
BTW here is that macro definition from Lua:
#define op_arith(L,iop,fop) { \
TValue *v1 = vRB(i); \
TValue *v2 = vRC(i); \
op_arith_aux(L, v1, v2, iop, fop); }
I'm assuming that op_arith is called numerous times to define handling
for various VM opcodes.
It *reliably* writes a bunch of code that would otherwise require a lot
of error-prone copy and paste.
This is language extension; the LUA maintainers have written
themselves an op_arith language feature.
The generated codee is large and complex, but at least you can see it.
If that language feature were wired into the compiler, it would only
be harder to work with. All that morass of code would be hidden
inside, generated into some intermediate representation (that you
can perhaps intercept and get in some readable form, in a
language/notation you will have to learn).
It calls yet more macros, and those in turn call others; here are a few
for vRB:
#define vRB(i) s2v(RB(i))
#define s2v(o) (&(o)->val)
#define RB(i) (base+GETARG_B(i))
#define GETARG_A(i) getarg(i, POS_A, SIZE_A)
#define getarg(i,pos,size) (cast_int(((i)>>(pos)) & MASK1(size,0)))
#define cast_int(i) cast(int, (i))
#define MASK1(n,p) ((~((~(Instruction)0)<<(n)))<<(p))
#define cast(t, exp) ((t)(exp))
Some of these look like low value. For instance cast_int(x) saves no characters over cast(int,x), and ony one character over cast(int, x),
and that is longer than just (int)(x), not to mention (int)x
situations where you can omit parentheses.
On 2025-04-22, bart <bc@freeuk.com> wrote:
to a nice use of macros, like what I was talking about.If you're going to criticize the macros, then hold the code constant.
If it is a given requirement that some code is to be written or
generated, and we have some macros which do that, are those macros a
"good" or "bad" solution?
The fact that Lua has some grotty macros in its VM doesn't speak
You comitted a slippery slope type fallacy, or similar, by bringing
that up. "Macros /can/ be layered to 10 layers deeper and make the logic inscrutable, therefore all macros are a bad idea, including a simple, single-expansion iteration macro for sqlite hashes."
Here, it produces neither faster code nor smaller.
That's neither here nor there; all that matters is whether it's a good
way to produce the code that the author wanted.
Additionally, it may be possible to replace the implementation of the
macro such that it generates some other code that is faster, and such
that it remains compatible; all the calls to the macro remain valid, and
so new code for handling all the opcodes is generated.
The macro is called like this:
op_arith(L, l_addi, luai_numadd);
that's pretty abstract; chances are it could be preserved if
the implementation strategy were substantially altered.
Why use macros at all?
You need to repeat some verbose coding pattern without copy-and-paste mistakes. If that coding pattern has a bug or has to be revised for any reason, you want to do it in one place.
You need to control the evaluation of an argument expression, rather
than just receive its value.
You need to set up some code that declares variables or interacts
with declared identifiers in scope.
They might be a big deal in your own language, but in my experience,
largely in C, they seem to cause nothing but grief.
But:
for_sqlite_hash(p, hash) { ... }
has only one simple level of expansion; saves typing, no grief.
IMV, macros generally are a bad idea. They are especially bad with how
they are implemented in C:
* Their implementation is complex and poorly defined.
* Compiler error messages can get weird and confusing
* They cause problems for tools such as smart editors
* Macros don't obey normal scope rules.
* Macro solutions tend to be ugly and tacky (see X-macros)
* Macro expansion can do too much:
#define length 200
#define width 100
struct {char* str; int length;} s;
s.length = 0; // oops!
On Wed, 23 Apr 2025 11:15:01 +0100
bart <bc@freeuk.com> wibbled:
IMV, macros generally are a bad idea. They are especially bad with how
they are implemented in C:
C macros do essentially 4 different things:
1) Provide a way to pass compilation data directly into the code via compiler
switches and #ifdef
2) Allow conditional compilation for different OS's
3) Allow repeating blocks of code to be compacted into a single macro when
having a function instead would be more complicated and/or inefficient. 4) An alternative to inline functions. Probably the least useful.
* Their implementation is complex and poorly defined.
Maybe to you.
* Macros don't obey normal scope rules.
They're not supposed to.
So what? You'd get a compilation error.
On 22/04/2025 20:11, David Brown wrote:
On 22/04/2025 20:54, bart wrote:
Well C's would be:
   For (first ingredient; ingredient; next ingredient)
The point was that you claimed your loop matches how you would
describe things in English.
And it does exactly that. I get why people decide to go up several
levels to try score some points, but we're not talking about AI here
where we are trying to extract the exact algorithm that might have been
in the mind of the programmer.
On 22/04/2025 20:26, Kaz Kylheku wrote:
On 2025-04-22, bart <bc@freeuk.com> wrote:
Or would you automatically say it was reasonable no matter how many
levels there were, just to disagee with me and to side with a fellow C
programmer?
You're trying to say that C is a bad language because you can have
7 or more layers of macro expansion, but you're not considering how
bizarre and crippling restriction it would be to put a cap on it.
Here, I'm trying to determine if this is the poster's honest, objective opinion, or whether they'd always going to be defend such code no matter what.
14 levels of macros? No problem; I'm sure there was a good reason!
I did ask at one point whether anybody could link to some truly terrible
C code; nobody has so far.
I just want to find out the threshold at which some will actually agree
that macros have been abused or their use is over the top and unjustified.
There doesn't seem to be any limit - or not any that they want to admit
to me.
but you're not considering how
bizarre and crippling restriction it would be to put a cap on it.
There WILL be a cap.
Some deep nesting might be justified in special
cases, for example some recursive macro that builds a string a character
at a time.
But this was not such a case; it was simply decided to make it work
using macros instead of functions.
As for being crippling: I've written all sorts of language apps,
including interpreters like this, without ever using more than one
level, and for several decades, zero levels.
A language like C provides all these odd-ball features, people are going
to use them.
On 23/04/2025 11:58, Muttley@DastardlyHQ.org wrote:
On Wed, 23 Apr 2025 11:15:01 +0100
bart <bc@freeuk.com> wibbled:
IMV, macros generally are a bad idea. They are especially bad with how
they are implemented in C:
C macros do essentially 4 different things:
1) Provide a way to pass compilation data directly into the code via compiler
switches and #ifdef
2) Allow conditional compilation for different OS's
That does not need expandable macros.
3) Allow repeating blocks of code to be compacted into a single macro when >> having a function instead would be more complicated and/or inefficient. >> 4) An alternative to inline functions. Probably the least useful.
* Their implementation is complex and poorly defined.
Maybe to you.
Have you ever implemented a C preprocessor?
* Macros don't obey normal scope rules.
They're not supposed to.
So, is that good or bad? (Let me guess: it's good that it works like that!)
So what? You'd get a compilation error.
If you're lucky. Or you get a silent, undetectable bug, since any
alphanumeric token: identifier, type, or reserved word, gets expanded,
no matter what the context.
David Brown <david.brown@hesbynett.no> writes:
On 22/04/2025 02:12, bart wrote:[...]
(Only Keith cautiously welcome the idea of such a feature, while MS
said he would vote against it, and JP said they would have proposed
it on April 1st.)
I don't recall reading Keith saying any such thing. He said he would
be willing to nit-pick a proposal for a new "for-loop" syntax - not
that he would welcome it. Perhaps he just thinks he would enjoy
nit-picking such a paper. As for using a feature if it were added to
C, I know I probably would do so in my own code - that does not imply
that I think such a feature is needed, or that I have any trouble
using C's current syntax for simple loops. (I find C++'s alternative
for-loop syntax nicer for iterating over containers, but that is not
as easily translatable into C.)
Here's what I wrote:
"""
Again, I would not object to adding a new kind of for loop,
similar to what you would prefer, and visually distinct from the
existing for loop, in a new version of the C standard. But that's
not likely to happen because there doesn't seem to be much demand
for it (for reasons that I know make you angry), and I don't care
enough to write a proposal. If someone else does write a proposal,
I'll be glad to help out by nitpicking it.
"""
I'll accept that "cautiously accept" is close enough to "would not
object".
[...]
I guess I am the exception - I've never needed any of these. But for
your information, C23 has a _Lengthof operator
C23 does not have _Lengthof. It's proposed for C2y.
On Wed, 23 Apr 2025 14:50:22 +0100
bart <bc@freeuk.com>:
On 23/04/2025 11:58, Muttley@DastardlyHQ.org wrote:
On Wed, 23 Apr 2025 11:15:01 +0100
bart <bc@freeuk.com>:
* Their implementation is complex and poorly defined.
Maybe to you.
Have you ever implemented a C preprocessor?
No, have you?
On 22/04/2025 22:03, bart wrote:
Too few levels of functions and/or macros (there is no semantic
difference between macros and functions in this matter)
I did ask at one point whether anybody could link to some truly
terrible C code; nobody has so far.
There are probably two reasons for that. One is that everyone, except perhaps you, understands that this is very subjective. I've seen plenty
of code posted in this group, written by regulars whose programming
skill and knowledge I highly respect, and yet which I would classify as "truly terrible" and reject it immediately if it were presented to me
for code review in my work. The other reason is that nobody else is particularly interested in seeing bad code, or going out of their way to
look for it.
I just want to find out the threshold at which some will actually
agree that macros have been abused or their use is over the top and
unjustified.
Why?
We are all going to have different first-glance opinions on different
bits of code.
 But to form a solid, justifiable opinion I would want to
see a lot more of the code in question.
but you're not considering how
bizarre and crippling restriction it would be to put a cap on it.
There WILL be a cap.
No.
Very few things in this world are black-or-white.
Some deep nesting might be justified in special cases, for example
some recursive macro that builds a string a character at a time.
But this was not such a case; it was simply decided to make it work
using macros instead of functions.
How do you know that?
As for being crippling: I've written all sorts of language apps,
including interpreters like this, without ever using more than one
level, and for several decades, zero levels.
And how do you know that is a good choice, or would be better in this case?
A language like C provides all these odd-ball features, people are
going to use them.
Macros are hardly odd-ball.
On 23/04/2025 00:02, Keith Thompson wrote:
C23 does not have _Lengthof. It's proposed for C2y.
Sorry, yes. The pdf I was viewing was the latest draft for post-C23
rather than C23. My mistake.
On 23/04/2025 16:31, David Brown wrote:
On 22/04/2025 22:03, bart wrote:
Too few levels of functions and/or macros (there is no semantic
difference between macros and functions in this matter)
There is a great deal of difference. Functions tend to be well-formed in their inputs and outputs.
Macros take some abitrary blocks of syntax and return another arbitrary
block of syntax:
#define INDEX(a, b, y) a y b
INDEX(a, i, [) ];
On 2025-04-23, bart <bc@freeuk.com> wrote:
On 23/04/2025 16:31, David Brown wrote:
On 22/04/2025 22:03, bart wrote:
Too few levels of functions and/or macros (there is no semantic
difference between macros and functions in this matter)
There is a great deal of difference. Functions tend to be well-formed in
their inputs and outputs.
Macros take some abitrary blocks of syntax and return another arbitrary
block of syntax:
#define INDEX(a, b, y) a y b
INDEX(a, i, [) ];
While that's terrible, I've never seen anything like it in the wild.
Macros which open a brace, relying on the invocation of a paired
macro to close it, are reasonably common.
The macro we had been discussing whereby the for (..) part of
a loop is also an example of something which generates a syntactic
fragment, which must be completed by something which follows
the macro, namely a statement.
Speaking purely in terms of formal syntqx, this is exactly what is going
on in your example with the completing closing bracket. In that
situation, it gives us a useful technique whose use is clear.
On 2025-04-23, bart <bc@freeuk.com> wrote:
On 23/04/2025 16:31, David Brown wrote:
On 22/04/2025 22:03, bart wrote:
Too few levels of functions and/or macros (there is no semantic
difference between macros and functions in this matter)
There is a great deal of difference. Functions tend to be well-formed in
their inputs and outputs.
Macros take some abitrary blocks of syntax and return another arbitrary
block of syntax:
#define INDEX(a, b, y) a y b
INDEX(a, i, [) ];
While that's terrible, I've never seen anything like it in the wild.
On 23/04/2025 17:00, Muttley@DastardlyHQ.org wrote:
On Wed, 23 Apr 2025 14:50:22 +0100
bart <bc@freeuk.com>:
On 23/04/2025 11:58, Muttley@DastardlyHQ.org wrote:
On Wed, 23 Apr 2025 11:15:01 +0100
bart <bc@freeuk.com>:
* Their implementation is complex and poorly defined.
Maybe to you.
Have you ever implemented a C preprocessor?
No, have you?
Yes.
On Wed, 23 Apr 2025 17:39:18 +0100
bart <bc@freeuk.com> wibbled:
On 23/04/2025 17:00, Muttley@DastardlyHQ.org wrote:
On Wed, 23 Apr 2025 14:50:22 +0100
bart <bc@freeuk.com>:
On 23/04/2025 11:58, Muttley@DastardlyHQ.org wrote:
On Wed, 23 Apr 2025 11:15:01 +0100
bart <bc@freeuk.com>:
* Their implementation is complex and poorly defined.
Maybe to you.
Have you ever implemented a C preprocessor?
No, have you?
Yes.
Well thats nice.
On Wed, 23 Apr 2025 18:43:33 -0000 (UTC)
Kaz Kylheku <643-408-1753@kylheku.com> wibbled:
On 2025-04-23, bart <bc@freeuk.com> wrote:
On 23/04/2025 16:31, David Brown wrote:
On 22/04/2025 22:03, bart wrote:
Too few levels of functions and/or macros (there is no semantic
difference between macros and functions in this matter)
There is a great deal of difference. Functions tend to be well-formed in >>> their inputs and outputs.
Macros take some abitrary blocks of syntax and return another arbitrary
block of syntax:
#define INDEX(a, b, y) a y b
INDEX(a, i, [) ];
While that's terrible, I've never seen anything like it in the wild.
He loves coming up with unrealistic code examples that no decent programmer would ever write then points and says look how bad macros must be. Using that approach you can easily come up with highly contorted code that no one can read
as the Obfuscated C contest proves.
On 24/04/2025 08:40, Muttley@DastardlyHQ.org wrote:
On Wed, 23 Apr 2025 18:43:33 -0000 (UTC)
Kaz Kylheku <643-408-1753@kylheku.com> wibbled:
On 2025-04-23, bart <bc@freeuk.com> wrote:
On 23/04/2025 16:31, David Brown wrote:
On 22/04/2025 22:03, bart wrote:
Too few levels of functions and/or macros (there is no semantic
difference between macros and functions in this matter)
There is a great deal of difference. Functions tend to be well-formed in >>>> their inputs and outputs.
Macros take some abitrary blocks of syntax and return another arbitrary >>>> block of syntax:
#define INDEX(a, b, y) a y b
INDEX(a, i, [) ];
While that's terrible, I've never seen anything like it in the wild.
He loves coming up with unrealistic code examples that no decent programmer >> would ever write then points and says look how bad macros must be. Using that
approach you can easily come up with highly contorted code that no one can >read
as the Obfuscated C contest proves.
And you do like totally ignoring the context. This was an example of how >macros work compared with how functions work.
Functions return a value of some type;
macros yield a bunch of lexical
tokens.
On Thu, 24 Apr 2025 09:26:23 +0100
bart <bc@freeuk.com> wibbled:
On 24/04/2025 08:40, Muttley@DastardlyHQ.org wrote:
On Wed, 23 Apr 2025 18:43:33 -0000 (UTC)
Kaz Kylheku <643-408-1753@kylheku.com> wibbled:
On 2025-04-23, bart <bc@freeuk.com> wrote:
On 23/04/2025 16:31, David Brown wrote:
On 22/04/2025 22:03, bart wrote:
Too few levels of functions and/or macros (there is no semantic
difference between macros and functions in this matter)
There is a great deal of difference. Functions tend to be well-formed in >>>>> their inputs and outputs.
Macros take some abitrary blocks of syntax and return another arbitrary >>>>> block of syntax:
#define INDEX(a, b, y) a y b
INDEX(a, i, [) ];
While that's terrible, I've never seen anything like it in the wild.
He loves coming up with unrealistic code examples that no decent programmer >>> would ever write then points and says look how bad macros must be. Using that
approach you can easily come up with highly contorted code that no one can >> read
as the Obfuscated C contest proves.
And you do like totally ignoring the context. This was an example of how
macros work compared with how functions work.
You think anyone in this group needed to be told? Macros arn't supposed to work the same way as functions or there'd be no point having them!
Too few levels of functions and/or macros (there is no semantic
difference between macros and functions in this matter)
Not always. Heard of void?
On 23/04/2025 19:43, Kaz Kylheku wrote:
On 2025-04-23, bart <bc@freeuk.com> wrote:
On 23/04/2025 16:31, David Brown wrote:
On 22/04/2025 22:03, bart wrote:
Too few levels of functions and/or macros (there is no semantic
difference between macros and functions in this matter)
There is a great deal of difference. Functions tend to be well-formed in >>> their inputs and outputs.
Macros take some abitrary blocks of syntax and return another arbitrary
block of syntax:
     #define INDEX(a, b, y) a y b
     INDEX(a, i, [) ];
While that's terrible, I've never seen anything like it in the wild.
Macros which open a brace, relying on the invocation of a paired
macro to close it, are reasonably common.
So you /have/ seen things like that.
There are entire libraries built
solely with the C processor, some even implement languages; they will
use every trick they can.
My example was only for illustrating my claim that macros work with
blobs of syntax, that may be malformed and unbalanced.
They can also work at the sub-token level:
   #define O(x) 0##x
   printf("%d\n", 100);            # 100
   printf("%d\n", O(100));         # 64
The macro we had been discussing whereby the for (..) part of
a loop is also an example of something which generates a syntactic
fragment, which must be completed by something which follows
the macro, namely a statement.
Speaking purely in terms of formal syntqx, this is exactly what is going
on in your example with the completing closing bracket. In that
situation, it gives us a useful technique whose use is clear.
Such libraries as I mentioned for embedded languages or syntax wrapping
are clever, but usually impractical, unwieldy and inefficient.
If you want a new language, do it properly!
Or use a more appropriate
base language.
On 23/04/2025 16:31, David Brown wrote:
On 22/04/2025 22:03, bart wrote:
Too few levels of functions and/or macros (there is no semantic
difference between macros and functions in this matter)
There is a great deal of difference. Functions tend to be well-formed in their inputs and outputs.
Macros take some abitrary blocks of syntax and return another arbitrary
block of syntax:
   #define INDEX(a, b, y) a y b
   INDEX(a, i, [) ];
I did ask at one point whether anybody could link to some truly
terrible C code; nobody has so far.
There are probably two reasons for that. One is that everyone, except
perhaps you, understands that this is very subjective. I've seen
plenty of code posted in this group, written by regulars whose
programming skill and knowledge I highly respect, and yet which I
would classify as "truly terrible" and reject it immediately if it
were presented to me for code review in my work. The other reason is
that nobody else is particularly interested in seeing bad code, or
going out of their way to look for it.
I just want to find out the threshold at which some will actually
agree that macros have been abused or their use is over the top and
unjustified.
Why?
We are all going to have different first-glance opinions on different
bits of code.
What's your initial opinion of this 500-line example:
 https://github.com/sal55/langs/blob/master/lisp.c
Here's one of the functions from it:
 lval rest(lval *h, lval *g) { lval *f = h-1; lval r = 0; for (; f>=g; f--)
   r = cons(h, *f, r); return r; }
Here's what my visualisation tool produces:
   global function rest(ref i32 h, g)i64 =
       ref i32 f
       i32 r
       f := h-1
       r := 0
       while f >= g do
           r := cons(h,f^,r)
           f--
       od
       return r
   end
Yes, you might apply a C formatter too, and keep it in C, it won't fix
that for-loop though.
 But to form a solid, justifiable opinion I would want to see a lot
more of the code in question.
Suppose this is a class of program which you have extensive experience
of writing, but you see extra layers of complications that you'd never
needed in yours, and the other program doesn't work any better either.
In fact it is poorer, and looks worse.
Would you still be as tolerant?
but you're not considering how
bizarre and crippling restriction it would be to put a cap on it.
There WILL be a cap.
No.
OK. I haven't been able to find a limit for nested, non-recursive
macros. And I don't know how to set up a test for recursive macros (I'm
not even sure they exist in C).
(I can do that my language, and that needs a cap since there are no conditional elements that would stop recursion. So this fails:
  macro M = M + 1
But the same in C works, sort of, in that the second M here:
  #define M M + 1
is not expanded.)
Very few things in this world are black-or-white.
Some deep nesting might be justified in special cases, for example
some recursive macro that builds a string a character at a time.
But this was not such a case; it was simply decided to make it work
using macros instead of functions.
How do you know that?
By the 500 such macro definitions, and the EXTENSIVE use of such macro invocations instead of functions, in a class of application I'm very
familiar with.
As for being crippling: I've written all sorts of language apps,
including interpreters like this, without ever using more than one
level, and for several decades, zero levels.
And how do you know that is a good choice, or would be better in this
case?
Near 40 years' experience of implementing interpreters? On some very resource-limited systems too. Ones that have always run rings around
other products, until more recently when people are trying harder.
But those efforts are now centred on JIT-based products. This Lua
product however is not JIT-based. (There is a separate LuaJIT project.)
(My latest interpreter does use macros. But there are only 25 across the project, which is for a bigger and richer language than Lua, and for an interpreter that runs faster. None of those are nested.
That product can be transpiled to C, and then it uses 0 C macros.)
A language like C provides all these odd-ball features, people are
going to use them.
Macros are hardly odd-ball.
C ones are.
On 23/04/2025 19:43, bart wrote:
By the 500 such macro definitions, and the EXTENSIVE use of such macro
invocations instead of functions, in a class of application I'm very
familiar with.
So you don't know. You guessed.
You don't have any idea /why/ the developers choose to use macros like
this - you simply assume that because you have written an interpreter
with little use of macros,
What your experience tells you is that is that it is /possible/ to
implement a little virtual machine for a simple language without using C macros.
It tells you nothing about whether or not it is a good idea,
because you have not tried it out for comparison. It tells you nothing about how best to implement Lua in C, because that is a very different language from your languages.
 It tells you nothing about the best
structure for a core project written by multiple people, with extensions written by hundreds or thousands of people, and with many orders of
magnitude more end users - because you just have a one-person language.
And to cap it all, you (AFAIUI) don't even use C to implement any of
this stuff - you use your own languages. (Those may well be a better
choice than C for implementing virtual machines.)
The way you could /know/ the best way to implement a Lua virtual machine
in C would be to investigate if the Lua developers, or any other groups,
have tried to implement the Lua VM in different ways and seen how well
they work. Or you could find books or articles that discuss different
ways of implementing VMs.
There are perhaps a half-dozen basic ways of implementing language
virtual machines. There are a very large number of interpreted or byte- coded languages. You have experience of implementing a couple of simple languages, for personal use, using either a strangely restricted subset
of C or one of your own languages.
On 24/04/2025 11:52, Muttley@DastardlyHQ.org wrote:
You think anyone in this group needed to be told? Macros arn't supposed to >> work the same way as functions or there'd be no point having them!
I was replying to this which implies the opposite:
DB:
Too few levels of functions and/or macros (there is no semantic
difference between macros and functions in this matter)
MD:
Not always. Heard of void?
I /knew/ you seize upon that! Then I might also say: "You think anyone
in this group needed to be told?".
FWIW, in my language, and a few others, 'functions' ALWAYS return values.
On 23/04/2025 22:49, bart wrote:
Such libraries as I mentioned for embedded languages or syntax
wrapping are clever, but usually impractical, unwieldy and inefficient.
And as usual, I question your basis for making such wild general claims.
If you want a new language, do it properly!
Let's compare alternatives here. If I want to make a little bit of
embedded language, with C macros I have :
    + The implementation is in a language I already know
    + I can probably implement it in a few hundred lines of code
    + It will work with any C compiler, any target, and any C code
    + It mixes freely with other C code
    - Debugging it can be a bit of a pain
    - Complicated things can get a bit ugly with macro tricks
    - C macros are not Turing complete (no loops, recursion, etc.)
Doing it "properly" means :
    + The language can support anything I want it to
    - I have to design a whole language
    - I have to document a whole language
    - I have to implement a whole language and all the tools
    - It only works for the targets I bother supporting
    - No one else can use it without all the tools
    - It has to be a complete language on its own
    - Integration with other code is a PITA at best
    - The implementation in C probably uses lots of macros...
The suggestion that making your own language and tools instead of a
macro library is clearly absurd.
Or use a more appropriate base language.
That is sometimes a much better option.
On 24/04/2025 13:25, David Brown wrote:
On 23/04/2025 19:43, bart wrote:
By the 500 such macro definitions, and the EXTENSIVE use of such macro
invocations instead of functions, in a class of application I'm very
familiar with.
So you don't know. You guessed.
Why are you automatically sticking up for THEM and not ME?
On 24/04/2025 14:12, David Brown wrote:
On 23/04/2025 22:49, bart wrote:
Such libraries as I mentioned for embedded languages or syntax
wrapping are clever, but usually impractical, unwieldy and inefficient.
And as usual, I question your basis for making such wild general claims.
If you want a new language, do it properly!
Let's compare alternatives here. If I want to make a little bit of
embedded language, with C macros I have :
     + The implementation is in a language I already know
No. It is in the C preprocessor language. It is quite different from C
and requires special skills.
(But if someone is up to it, perhaps they can write 'make' in it!)
     + I can probably implement it in a few hundred lines of code
One Brainfuck interpreter I looked at (not sure if one of those at the
link below), involved 5000 lines of header code. But they can usually be directly implemented in any normal language in about 50 lines.
     + It will work with any C compiler, any target, and any C code
Some will need a particular compiler or version because they rely on
some very specific behaviour.
     + It mixes freely with other C code
Not necessarily. Look at the 'datatype99' link here for example:
 https://github.com/hirrolot/awesome-c-preprocessor
That provides a new syntax for type definitions. While it can co-exist
with normal C code, it will be poorly matched.
It will also be incredibly confusing for someone looking at a source
file with a .c extension.
     - Debugging it can be a bit of a pain
     - Complicated things can get a bit ugly with macro tricks
     - C macros are not Turing complete (no loops, recursion, etc.)
Doing it "properly" means :
     + The language can support anything I want it to
     - I have to design a whole language
     - I have to document a whole language
You will need docs anyway.
     - I have to implement a whole language and all the tools
     - It only works for the targets I bother supporting
     - No one else can use it without all the tools
     - It has to be a complete language on its own
     - Integration with other code is a PITA at best
That is going to be a problem anyway if you have this mysterious-looking syntax in the middle of a C file.
     - The implementation in C probably uses lots of macros...
The suggestion that making your own language and tools instead of a
macro library is clearly absurd.
Who said devising a language was easy? But a CPP-based one will be poor.
Here's a comment from the Ferdi265/BF interpreter at that link:
"The preprocessor brainfuck interpreter is very very inefficient and
will use around 16 GIGABYTES of memory and 15 to 20 minutes of
processing time while running hello.bf."
If all you're thinking of is a library of functions and macros, then
fine, but that is not exactly a language.
Or use a more appropriate base language.
That is sometimes a much better option.
While not my thing, I was thinking of something like Racket.
On 23/04/2025 19:43, bart wrote:
On 23/04/2025 16:31, David Brown wrote:
[...]
We are all going to have different first-glance opinions on different
bits of code.
What's your initial opinion of this 500-line example:
[...]
You missed the bit where I said I am not interested in examining bad
code. Nor am I interested in Lisp, so I have no interest in looking at
that.
Here's one of the functions from it:
lval rest(lval *h, lval *g) { lval *f = h-1; lval r = 0; for (;
=g; f--)r = cons(h, *f, r); return r; }
Presumably that makes sense to the people who wrote the code. But it
makes no sense to me.
That is /not/ because of some syntax or
formatting issue, or the missing clause in the "for" loop - it is
because I don't know the types, the functions (or macros) used, the
purpose of this function, the way memory and resource management is
handled in this code, the meaning of the single-letter identifiers, or anything else about it.
How the code is formatted is a drop in the ocean in the effort needed to understand the code, what it does, and where it fits with everything
else in the code base.
Here's what my visualisation tool produces:
global function rest(ref i32 h, g)i64 =
ref i32 f
i32 r
f := h-1
r := 0
while f >= g do
r := cons(h,f^,r)
f--
od
return r
end
Yes, you might apply a C formatter too, and keep it in C, it won't fix
that for-loop though.
The for-loop is basically irrelevant for understanding the code.
I fully agree that your "visualisation" here - or a re-factoring in C to
use a "while" loop rather than a "for" loop - makes the code easier to
read and makes it more obvious what the mechanics of the function are.
And if I were writing the same function myself, I'd use a while loop
rather than a for loop.
So don't misunderstand me here - I am not a fan
of the way that function is written. But I can't judge the format
without a lot more context, and such judgement would be highly
subjective. And the formatting is a very minor aspect of understanding
the code.
I'm trying to stand up for myself as I'M AT THE END OF MY FUCKING TETHER >HERE.
ALL YOU PEOPLE WANT TO DO IS JUST TRASH EVERYTHING I'VE DONE.
EVERY SINGLE THING I SAY IS WRONG. NOTHING I SAY IS EVER RIGHT. WHATEVER
IT IS I SAY, YOU SAY THE OPPOSITE THING.
MY SELF-ESTEEM HAS NEVER BEEN LOWER - YOU HAVE DESTROYED ME.
AND WHEN I TRY QUESTION IT - THEN *YOU* ACCUSE ME OF BEING >SELF-CENTRED!!!!!!!
FUCK YOU, CUNT.
AND FUCKING TROLLS, ALL OF YOU.
On 24/04/2025 16:28, bart wrote:
On 24/04/2025 14:12, David Brown wrote:
On 23/04/2025 22:49, bart wrote:
Such libraries as I mentioned for embedded languages or syntaxAnd as usual, I question your basis for making such wild general claims. >>>
wrapping are clever, but usually impractical, unwieldy and inefficient. >>>
If you want a new language, do it properly!
Let's compare alternatives here. If I want to make a little bit of
embedded language, with C macros I have :
     + The implementation is in a language I already know
No. It is in the C preprocessor language. It is quite different from C
and requires special skills.
Sometimes you talk a lot of bollocks.
Some will need a particular compiler or version because they rely on
some very specific behaviour.
There is almost nothing compiler-specific about pre-processor
directives. I only know of two very minor extensions that gcc supports,
for marginally nicer variadic macro support. Of course the result of
the macro expansion might be compiler-specific, just like any other C
code you write without macros.
It will also be incredibly confusing for someone looking at a source
file with a .c extension.
I am not personally a fan of things like these macro packages - I'd
rather just use C++. But it's a matter of choice. Even if you could
argue that packages like "datatype99" are objectively bad in some sense
(and you can't argue that), it would only be an argument against that
use of macros, not C macros themselves or other use of them.
If your best argument against C macros is that someone wrote a Brainfuck interpreter with them and it is inefficient, then I think you should
retire from the discussion.
On 24/04/2025 16:21, David Brown wrote:
Then look at the kind of impenetrable macros that are needed to do
ambitious things like emulate a new kind of syntax within a C file. That
bart <bc@freeuk.com> writes:
On 24/04/2025 13:25, David Brown wrote:
On 23/04/2025 19:43, bart wrote:
By the 500 such macro definitions, and the EXTENSIVE use of such macro >>>> invocations instead of functions, in a class of application I'm very
familiar with.
So you don't know. You guessed.
Why are you automatically sticking up for THEM and not ME?
Wow! How self-centered can you get?
On 16.04.2025 11:45, Rosario19 wrote:
On Tue, 15 Apr 2025 09:25:40 +0200, Janis Papanagnou wrote:
On 15.04.2025 06:57, Rosario19 wrote:
On 13.04.2025 18:39, bart wrote:
[...]
for(let i = 1; i <= 36; i++) {
C for loop is great, but all can be workarounded with goto label
Sure. Or all done with Turing machines. - But why would one want to.
because one not has the for loop, because is need more flexibility in
what code has to make, because one find so easy goto label, that it is
easier of the loop for, even if i think in 80% of cases for loop is
less chars and easier of the loop that use goto label
(Note my question above was rhetorical. - Turing machine programs is
not something you should consider as scale for what we usually do in >programming.)
Of course, if all you have is an assembler language then "all" you
have are jumps.
(Note: again an accentuated formulation of the point,
but I'm confident you understand what I'm trying to say.)
If, for common loop conditions, it's easier for someone to use gotos
than to use typical loop constructs then I suggest that this person
should not apply for a programmers' job.
The "number of characters" in a syntactical construct is IMO not the
most relevant or primary factor. But abstractions in languages often
coincide with much terser formulations. And abstractions is what aids
in programming non-trivial systems. I would abstain from gotos, but
because of the "number of characters" to type or to spare.
Janis
NOTHING I SAY IS EVER RIGHT.
for loop is easy because propose always the same type of loop, the initialization, the condition, the increment etc
all ok but sometimes
one need to exit the loop before the main condition of exit,
and go down or up in the code lines going out of that loop,
for these cases it is better for me, not use forloop but the right
if()s goto labels...
Especially if you have backward 'goto's it's IMO worth to have
a second look on your iteration construct.
bart <bc@freeuk.com> writes:
[...]
I'm trying to stand up for myself as I'M AT THE END OF MY FUCKING[...]
TETHER HERE.
ALL YOU PEOPLE WANT TO DO IS JUST TRASH EVERYTHING I'VE DONE.
EVERY SINGLE THING I SAY IS WRONG. NOTHING I SAY IS EVER
RIGHT. WHATEVER IT IS I SAY, YOU SAY THE OPPOSITE THING.
MY SELF-ESTEEM HAS NEVER BEEN LOWER - YOU HAVE DESTROYED ME.
Bart, if this is sincere, please consider stepping away from
comp.lang.c for a while. I have no motivation for this post other
than concern for your well-being.
On 2025-04-24, bart wrote:
NOTHING I SAY IS EVER RIGHT.
Well, /that/ isn't; you're blindly discounting all the times
someone has agreed with you.
On Fri, 25 Apr 2025 07:46:21 +0200, Janis Papanagnou wrote:
Especially if you have backward 'goto's it's IMO worth to have
a second look on your iteration construct.
loops have always at last one backward 'goto'
On 24/04/2025 16:21, David Brown wrote:
On 24/04/2025 16:28, bart wrote:
On 24/04/2025 14:12, David Brown wrote:
On 23/04/2025 22:49, bart wrote:
Such libraries as I mentioned for embedded languages or syntax
wrapping are clever, but usually impractical, unwieldy and
inefficient.
And as usual, I question your basis for making such wild general
claims.
If you want a new language, do it properly!
Let's compare alternatives here. If I want to make a little bit of
embedded language, with C macros I have :
     + The implementation is in a language I already know
No. It is in the C preprocessor language. It is quite different from
C and requires special skills.
Sometimes you talk a lot of bollocks.
Take a look at any ordinary C code of normal functions, statements etc.
Then look at the kind of impenetrable macros that are needed to do
ambitious things like emulate a new kind of syntax within a C file. That
will be a long list of #defines.
Can you honestly say that the person writing the former, and the one
writing the latter, are coding in the same language?
And can you honestly say that writing those complicated collections of
macros requires no extra skill?
If your answers are Yes then /you/ are the one talking bollocks.
(Maybe you've forgotten that Preprocessing is a C compilation stage
which performs a transformation from C with macro invocations, to pure C
with those invocations expanded.
To repeat, what I call a different language is the set of incantations
that form the body of each #define. They do not exist in the pure C
version.)
Some will need a particular compiler or version because they rely on
some very specific behaviour.
There is almost nothing compiler-specific about pre-processor
directives. I only know of two very minor extensions that gcc
supports, for marginally nicer variadic macro support. Of course the
result of the macro expansion might be compiler-specific, just like
any other C code you write without macros.
C compilers used to vary quite a bit in how macro expansions were done.
Now there is less variance, maybe because they're sharing the one implementation that got it just right.
It will also be incredibly confusing for someone looking at a source
file with a .c extension.
I am not personally a fan of things like these macro packages - I'd
rather just use C++. But it's a matter of choice. Even if you could
argue that packages like "datatype99" are objectively bad in some
sense (and you can't argue that), it would only be an argument against
that use of macros, not C macros themselves or other use of them.
You're trying to avoid agreeing with me, that C files containing
constructs that emulate different syntax or different language elements,
are going to be confusing.
Remember I claimed such things to be 'impractical, unwieldy and
inefficient' -'usually'.
If your best argument against C macros is that someone wrote a
Brainfuck interpreter with them and it is inefficient, then I think
you should retire from the discussion.
You seem to advocating using C macros to make an embedded language,
given that you say:
"The suggestion that making your own language and tools instead of a
macro library is clearly absurd."
I gave an example of a tiny language needing 100 times as much source
code to implement using C macros, as in a normal language, and another version of it that was hopelessy slow and inefficient.
And that's for a language which is normally as simple to implement, if
not use, as they come.
So, do you have an actual example of an embedded language that is
implemented more reasonably using macros?
By embedded 'language' I don't mean a library of function or macro definitions that you just call or invoked, as happens in any C program.
On Fri, 25 Apr 2025 02:56:44 -0000 (UTC), Kaz Kylheku wrote:
On 2025-04-24, bart wrote:
NOTHING I SAY IS EVER RIGHT.
Well, /that/ isn't; you're blindly discounting all the times
someone has agreed with you.
here we are all wrong in something
On 24/04/2025 13:25, David Brown wrote:
On 23/04/2025 19:43, bart wrote:
By the 500 such macro definitions, and the EXTENSIVE use of such
macro invocations instead of functions, in a class of application I'm
very familiar with.
So you don't know. You guessed.
Why are you automatically sticking up for THEM and not ME?
What qualifications and what experience would somebody need for David
Brown to give credence to their opinion?
I'm sure there are lots projects you can look at and know straight off
that its uses of macros or whatever is over the top.
Suppose you reported that here, but I suggested that you don't know
that; you're only guessing?
You don't have any idea /why/ the developers choose to use macros like
this - you simply assume that because you have written an interpreter
with little use of macros,
I've written a million lines of code with little use of macros.
They are prevalent in C simply because the language has them and people
will use them. Even if you ignore the ones that only exist people
because don't like to use enums for named constants.
What your experience tells you is that is that it is /possible/ to
implement a little virtual machine for a simple language without using
C macros.
It tells you nothing about whether or not it is a good idea, because
you have not tried it out for comparison. It tells you nothing about
how best to implement Lua in C, because that is a very different
language from your languages.
The example I gave was from ADD; my language has ADD too! Except that
Lua's ADD works between Ints and Floats (Floats only before 5.1?); mine
works between Ints, Floats, Strings, Bignums and Sets.
So, how different is it really?
 It tells you nothing about the best structure for a core project
written by multiple people, with extensions written by hundreds or
thousands of people, and with many orders of magnitude more end users
- because you just have a one-person language.
You forgot the bit where I said I've been doing this since the 1980s, on
a few languages of my own, both dynamic and statically typed. And I've
looked at lots of others including their implementations.
I've also got my C compiler to build and run some of those (Lua and
Seed7 among them).
I even created a C interpreter.
I've experimented with half a dozen kinds of bytecode dispatcher, and
several methods of type dispatching, and tried out AST tree-walkers.
Some of my interpreters were embedded, and run within commercial
products, and used by other people to create their own add-on programs.
And to cap it all, you (AFAIUI) don't even use C to implement any of
this stuff - you use your own languages. (Those may well be a better
choice than C for implementing virtual machines.)
I did try C at one point. That didn't use macros either.
The way you could /know/ the best way to implement a Lua virtual
machine in C would be to investigate if the Lua developers, or any
other groups, have tried to implement the Lua VM in different ways and
seen how well they work. Or you could find books or articles that
discuss different ways of implementing VMs.
Maybe /I/ should write a book or article instead!
There are perhaps a half-dozen basic ways of implementing language
virtual machines. There are a very large number of interpreted or
byte- coded languages. You have experience of implementing a couple
of simple languages, for personal use, using either a strangely
restricted subset of C or one of your own languages.
How many interpreters do you think the developers of Lua have worked on?
(And how many of those developers also devised and programmed its implemenation language?)
You describe my languages as 'simple'; how much more complex do you
think Lua is?
I find it disappointing that you don't want to acknowledge that I might
know something of this field, that I have extensive experience of it,
and that my opinions might be valid.
More so that you seem to know little about Lua, but decide to give them
the benefit of a doubt anyway.
/Maybe/ they had good reasons for using so many macros, but /maybe/ so
do I for having so few!
So why are you automatically assuming they must be right, and I must be wrong?
Further, this is part of a survey of interpreters running a recursive Fibonacci benchmark:
So I know fuck-all about writing interpreters, they are all toys, my languages are toys, and my opinion counts for nothing?
On Tue, 15 Apr 2025 11:30:24 +0100
bart <bc@freeuk.com> wrote:
Let me ask you this: what exactly is the point of the 'while'
statement in C? Since it can always be trivially be written as:
for (;cond;)
It seems to that most use cases (initialise, check exit condition,
change something that affects the letter), would suit 'for' better.
But since 'for' then becomes overloaded, there ought to be a
dedicated feature for simple iteration. So it seems the solution is
as a I suggested above.
I suspect that 'while' loop is here in C because Dennis Ritchie wanted
'do .. while() ' and thought that if the keyword is here anyway than
why not reuse it?
In the hindsight, probably a mistake.
On 15/04/2025 13:34, Michael S wrote:
<snip>
I suspect that 'while' loop is here in C because Dennis Ritchie wanted
'do .. while() ' and thought that if the keyword is here anyway than
why not reuse it?
In the hindsight, probably a mistake.
In hindsight:
$ find . -name \*.c | xargs cat | wc -l
126343
$ find . -name \*.c | xargs grep -w while | wc -l
556
$ find . -name \*.c | xargs grep -w for | wc -l
1258
So although I use for() about twice as much as I use while(), I still
find while a better option one time in three. That's useful enough to
make it worth keeping in the toolbox.
Michael S <already5chosen@yahoo.com> writes:
On Tue, 15 Apr 2025 11:30:24 +0100
bart <bc@freeuk.com> wrote:
Let me ask you this: what exactly is the point of the 'while'
statement in C? Since it can always be trivially be written as:
for (;cond;)
It seems to that most use cases (initialise, check exit condition,
change something that affects the letter), would suit 'for' better.
But since 'for' then becomes overloaded, there ought to be a
dedicated feature for simple iteration. So it seems the solution
is as a I suggested above.
I suspect that 'while' loop is here in C because Dennis Ritchie
wanted 'do .. while() ' and thought that if the keyword is here
anyway than why not reuse it?
According to K&R, all of the basic control structures in C -- if,
while, for, do, and switch (and listed in that order) -- were
provided in BCPL, though not using the same syntax as in C,.
In the hindsight, probably a mistake.
I admit I don't understand this reaction.
bart <bc@freeuk.com> writes:
On 18/04/2025 19:10, James Kuyper wrote:
On 16.04.2025 13:01, bart wrote:
...
Unlike C's for, which is just a gimmick where you bundle three
potentially unrelated expressions and hope for the best.
If all you can do is "hope for the best", you're doing it wrong.
It's your job to ensure that they are not arbitrary unrelated
expressions, but correctly related expressions, and that's no
different from your responsibility for all of the other
expressions that make up your program.
If you find that problematic, you shouldn't be programming in
any language, but certainly not in C.
I see it didn't take you long to get to the personal insult. What
is it with this group?
It's not an insult, it is a simple fact.
Michael S <already5chosen@yahoo.com> writes:
On Wed, 16 Apr 2025 12:32:13 +0100
bart <bc@freeuk.com> wrote:
But never, mind, C's for-loop will still be the most superior to
everybody here. I'd have an easier time arguing about religion!
Who exactly said that it is superior? Surely not me.
I think, most posters here would agree with my stance that C for() is
non-ideal. esp. for writer, but good enough.
I disagree with that statement, [...]
bart <bc@freeuk.com> writes:
[...]
I'm trying to stand up for myself as I'M AT THE END OF MY FUCKING
TETHER HERE.
ALL YOU PEOPLE WANT TO DO IS JUST TRASH EVERYTHING I'VE DONE.
EVERY SINGLE THING I SAY IS WRONG. NOTHING I SAY IS EVER
RIGHT. WHATEVER IT IS I SAY, YOU SAY THE OPPOSITE THING.
MY SELF-ESTEEM HAS NEVER BEEN LOWER - YOU HAVE DESTROYED ME.
[...]
Bart, if this is sincere, please consider stepping away from
comp.lang.c for a while. I have no motivation for this post other
than concern for your well-being.
On Sun, 04 May 2025 07:31:11 -0700^^^^^^^^^
Tim Rentsch <tr.17687@z991.linuxsc.com> wrote:
Michael S <already5chosen@yahoo.com> writes:
On Tue, 15 Apr 2025 11:30:24 +0100
bart <bc@freeuk.com> wrote:
Let me ask you this: what exactly is the point of the 'while'
statement in C? Since it can always be trivially be written as:
for (;cond;)
It seems to that most use cases (initialise, check exit
condition, change something that affects the letter), would suit
'for' better.
But since 'for' then becomes overloaded, there ought to be a
dedicated feature for simple iteration. So it seems the solution
is as a I suggested above.
I suspect that 'while' loop is here in C because Dennis Ritchie
wanted 'do .. while() ' and thought that if the keyword is here
anyway than why not reuse it?
According to K&R, all of the basic control structures in C -- if,
while, for, do, and switch (and listed in that order) -- were
provided in BCPL, though not using the same syntax as in C,.
In the hindsight, probably a mistake.
I admit I don't understand this reaction.
I don't like reuse of keyboards. Neither of 'while' nor of 'break' nor
of 'static' (even more so in C++) nor use of 'long' modifier both for^^
integer and for floating-point types.
Double meaning of 'while' adds unnecessary mental load for a code
reader. Not a lot of it, but still unnecessary.
Also having just one form of loop with pre-condition strikes me as
more elegant. Since elegance is strongly subjective, I have no logical arguments in support of me feelings.
On Wed, 16 Apr 2025 14:09:44 GMT
scott@slp53.sl.home (Scott Lurndal) wrote:
bart <bc@freeuk.com> writes:
On 15/04/2025 20:07, Scott Lurndal wrote:
bart <bc@freeuk.com> writes:
On 15/04/2025 14:19, Kaz Kylheku wrote:
Thats's fine. But it means a real 'for' loop doesn't exist in C;
you have to emulate it using that 3-way construct, which is naff,
and also error prone.
Real for loops _are_ a three-way construct.
135 FOR I=1 TO 10 STEP 2 [BASIC]
for(i = 1; i < 11; i += 2) [C/C++]
do 1 = 1, 10, 2 [FORTRAN]
Any step other than 1 is unusual. So Basic and Fortran would
typically be:
for i = 1 to 10 # 6 tokens; Basic
do i = 1, 10 # 6 tokens; Fortran
for i = 1, 10 # 6 tokens; Lua
for i to 10 do # 5 tokens; Mine (using default start)
to 10 do # 3 tokens; Mine (when index is not needed)
Let's look at that C again:
for (int i = 1; i < 11; i += 1) # 15 tokens; C
for(i = 1; i++ <= 10;)
I'd reject this code during review.
Hopefully, you too.
Michael S <already5chosen@yahoo.com> writes:
On Wed, 16 Apr 2025 14:09:44 GMT
scott@slp53.sl.home (Scott Lurndal) wrote:
bart <bc@freeuk.com> writes:
On 15/04/2025 20:07, Scott Lurndal wrote:
Let's look at that C again:
for (int i = 1; i < 11; i += 1) # 15 tokens; C
for(i = 1; i++ <= 10;)
I'd reject this code during review.
Hopefully, you too.
I'm curious to know the basis for your reaction. What about the
code would prompt your judgment to reject it? Is it just a
specific reaction, or does it represent some more general pattern
(and if so then what more general pattern)?
On Tue, 06 May 2025 05:59:20 -0700
Tim Rentsch <tr.17687@z991.linuxsc.com> wrote:
Michael S <already5chosen@yahoo.com> writes:
On Wed, 16 Apr 2025 14:09:44 GMT
scott@slp53.sl.home (Scott Lurndal) wrote:
bart <bc@freeuk.com> writes:
On 15/04/2025 20:07, Scott Lurndal wrote:
Let's look at that C again:
for (int i = 1; i < 11; i += 1) # 15 tokens; C
for(i = 1; i++ <= 10;)
I'd reject this code during review.
Hopefully, you too.
I'm curious to know the basis for your reaction. What about the
code would prompt your judgment to reject it? Is it just a
specific reaction, or does it represent some more general pattern
(and if so then what more general pattern)?
First and foremost it was tongue-in-cheek reaction to the article that
Scott posted 2-3 minutes after the article that I was reacting to.
At the next level, it is true that if I do detailed code review (which
I almost never do) I would indeed reject this code. The first
and sufficient reason is that the code looks superficially similar to
very common idiom, but significantly differs in behavior. An additional reason is that post-increment operator is generally harder to reason
about than almost any other C operator, esp. so when used as part of comparison, so, IMHO, readable code should limit use of post-increment
to very common idioms (or altogether avoid it). The third reason,
related to the first two is that behavior of this loop is way too
surprising.
On Tue, 06 May 2025 05:59:20 -0700
Tim Rentsch <tr.17687@z991.linuxsc.com> wrote:
Michael S <already5chosen@yahoo.com> writes:
On Wed, 16 Apr 2025 14:09:44 GMT
scott@slp53.sl.home (Scott Lurndal) wrote:
bart <bc@freeuk.com> writes:
On 15/04/2025 20:07, Scott Lurndal wrote:
Let's look at that C again:
for (int i = 1; i < 11; i += 1) # 15 tokens; C
for(i = 1; i++ <= 10;)
I'd reject this code during review.
Hopefully, you too.
I'm curious to know the basis for your reaction. What about the
code would prompt your judgment to reject it? Is it just a
specific reaction, or does it represent some more general pattern
(and if so then what more general pattern)?
First and foremost it was tongue-in-cheek reaction to the article that
Scott posted 2-3 minutes after the article that I was reacting to.
At the next level, it is true that if I do detailed code review (which
I almost never do) I would indeed reject this code. The first
and sufficient reason is that the code looks superficially similar to
very common idiom, but significantly differs in behavior. An additional >reason is that post-increment operator is generally harder to reason
about than almost any other C operator, esp. so when used as part of >comparison, so, IMHO, readable code should limit use of post-increment
to very common idioms (or altogether avoid it). The third reason,
related to the first two is that behavior of this loop is way too
surprising.
On Sun, 04 May 2025 07:31:11 -0700
Tim Rentsch <tr.17687@z991.linuxsc.com> wrote:
Michael S <already5chosen@yahoo.com> writes:
On Tue, 15 Apr 2025 11:30:24 +0100
bart <bc@freeuk.com> wrote:
Let me ask you this: what exactly is the point of the 'while'
statement in C? Since it can always be trivially be written as:
for (;cond;)
It seems to that most use cases (initialise, check exit condition,
change something that affects the letter), would suit 'for' better.
But since 'for' then becomes overloaded, there ought to be a
dedicated feature for simple iteration. So it seems the solution
is as a I suggested above.
I suspect that 'while' loop is here in C because Dennis Ritchie
wanted 'do .. while() ' and thought that if the keyword is here
anyway than why not reuse it?
According to K&R, all of the basic control structures in C -- if,
while, for, do, and switch (and listed in that order) -- were
provided in BCPL, though not using the same syntax as in C,.
In the hindsight, probably a mistake.
I admit I don't understand this reaction.
I don't like reuse of [keywords]. Neither of 'while' nor of 'break' nor
of 'static' (even more so in C++) nor use of 'long' modifier both for
integer and for floating-point types.
Double meaning of 'while' adds unnecessary mental load for a code
reader. Not a lot of it, but still unnecessary.
Also having just one form of loop with pre-condition strikes me as more elegant. Since elegance is strongly subjective, I have no logical
arguments in support of me feelings.
never necessary). Also it isn't easy to think of a good substitute
word that might be given for this use of 'static', so maybe the
On Sat, 10 May 2025 06:43:38 -0700
Tim Rentsch <tr.17687@z991.linuxsc.com> gabbled:
never necessary). Also it isn't easy to think of a good substitute
word that might be given for this use of 'static', so maybe the
Isn't it?
Where "static" means local to a module the words "local","module","limit" >spring to mind which are far closer to the intended meaning. Reusing "static" >seems somewhat perverse IMO.
Michael S <already5chosen@yahoo.com> writes:
On Sun, 04 May 2025 07:31:11 -0700
Tim Rentsch <tr.17687@z991.linuxsc.com> wrote:
Also having just one form of loop with pre-condition strikes me as
more elegant. Since elegance is strongly subjective, I have no
logical arguments in support of me feelings.
In a recent posting I gave some statistics about the three different
kinds of looping controls (while,for,do/while). do/while loops were
almost 20% of all loops, and more than a quarter of the two kinds of
loops other than for(). Besides being a more pragmatic choice, I
think having the three kinds of loops be distinct in the language is
a better choice, because how we think of the different kinds of loop
is different, and it's helpful to have those differences be readily
apparent in the program, rather than needing to reconstruct them by
looking at code around the loop control fragment.
On Sat, 10 May 2025 06:43:38 -0700
Tim Rentsch <tr.17687@z991.linuxsc.com> gabbled:
never necessary). Also it isn't easy to think of a good substitute
word that might be given for this use of 'static', so maybe the
Isn't it?
Where "static" means local to a module the words
"local","module","limit" spring to mind which are far closer to
the intended meaning. Reusing "static" seems somewhat perverse
IMO.
Muttley@dastardlyhq.com writes:
On Sat, 10 May 2025 06:43:38 -0700
Tim Rentsch <tr.17687@z991.linuxsc.com> gabbled:
never necessary). Also it isn't easy to think of a good substitute
word that might be given for this use of 'static', so maybe the
Isn't it?
Where "static" means local to a module the words "local","module","limit" >>spring to mind which are far closer to the intended meaning. Reusing "static" >>seems somewhat perverse IMO.
'local', 'module', 'limit' are common words used as identifiers in five >decades of C code. Using them as keywords in a newer version of C would
not be desirable. Whereas the new flavor of 'static' won't break any >existing code, and provides a concrete benefit.
Muttley@dastardlyhq.com writes:
On Sat, 10 May 2025 06:43:38 -0700
Tim Rentsch <tr.17687@z991.linuxsc.com> gabbled:
never necessary). Also it isn't easy to think of a good substitute
word that might be given for this use of 'static', so maybe the
Isn't it?
Where "static" means local to a module the words
"local","module","limit" spring to mind which are far closer to
the intended meaning. Reusing "static" seems somewhat perverse
IMO.
The use I'm talking about here may be illustrated as follows:
double
average( double values[ static 10 ] ){
double total = 0;
for( int i = 0; i < 10; i++ ){
total += values[i];
}
return total / 10;
}
What word would you suggest to be used in place of 'static'
there?
On Sat, 10 May 2025 14:29:50 -0700
Tim Rentsch <tr.17687@z991.linuxsc.com> gabbled:
Muttley@dastardlyhq.com writes:
On Sat, 10 May 2025 06:43:38 -0700
Tim Rentsch <tr.17687@z991.linuxsc.com> gabbled:
never necessary). Also it isn't easy to think of a good substitute
word that might be given for this use of 'static', so maybe the
Isn't it?
Where "static" means local to a module the words
"local","module","limit" spring to mind which are far closer to
the intended meaning. Reusing "static" seems somewhat perverse
IMO.
The use I'm talking about here may be illustrated as follows:
double
average( double values[ static 10 ] ){
double total = 0;
for( int i = 0; i < 10; i++ ){
total += values[i];
}
return total / 10;
}
What word would you suggest to be used in place of 'static'
there?
If I knew what the hell it was supposed to do I'd tell you.
double average(double values[static 4]) {
double total = 0.0;
for (int i = 0; i < 3; i++) {
total += values[i];
}
return total / 3;
}
Without the "static 4", the compiler can only assume that it can access
up to 3 doubles from the "value" array. But with "static 4", it knows
it is safe to read "values[3]" even though the code never needs to do
On Sun, 11 May 2025 12:02:25 +0200
David Brown <david.brown@hesbynett.no> gabbled:
double average(double values[static 4]) {
    double total = 0.0;
    for (int i = 0; i < 3; i++) {
       total += values[i];
    }
    return total / 3;
}
Without the "static 4", the compiler can only assume that it can
access up to 3 doubles from the "value" array. But with "static 4",
it knows it is safe to read "values[3]" even though the code never
needs to do
Not sure I follow. C doesn't care if its safe or not, it'll just try and
read them anyway and if it can't and there's a memory leak or crash,
well, tough luck mate. So I don't see why it would make a difference to
the resulting
assembler.
On Sun, 11 May 2025 12:02:25 +0200
David Brown <david.brown@hesbynett.no> gabbled:
double average(double values[static 4]) {
double total = 0.0;
for (int i = 0; i < 3; i++) {
total += values[i];
}
return total / 3;
}
Without the "static 4", the compiler can only assume that it can access
up to 3 doubles from the "value" array. But with "static 4", it knows
it is safe to read "values[3]" even though the code never needs to do
Not sure I follow. C doesn't care if its safe or not, it'll just try and
read them anyway and if it can't and there's a memory leak or crash, well, >tough luck mate. So I don't see why it would make a difference to the resulting
assembler.
On 11/05/2025 10:21, Muttley@dastardlyhq.com wrote:...
On Sat, 10 May 2025 14:29:50 -0700
Tim Rentsch <tr.17687@z991.linuxsc.com> gabbled:
The use I'm talking about here may be illustrated as follows:
double
average( double values[ static 10 ] ){
double total = 0;
for( int i = 0; i < 10; i++ ){
total += values[i];
}
return total / 10;
}
What word would you suggest to be used in place of 'static'
there?
If I knew what the hell it was supposed to do I'd tell you.
Using "static" inside array parameters is, IME, extremely rare. It was
added in C99, and tells the compiler that whenever "average" is called,
the "values" parameter points to an array of at least 10 doubles. It
does not affect the signature of the function or compatibility with any
other declarations, and is AFAIK rarely checked by compilers.
In an example like the one above, it is completely useless for
compilation - it tells the compiler nothing that it does not already
know. An optimising compiler will see that you are accessing values[0]
to values[9], and if it can get better results through vectorising, prefetching, etc., then it will do so. (You can argue that the "static
10" is still useful as an indicator to human readers, but I am not
convinced of that.)
James Kuyper <jameskuyper@alumni.caltech.edu> writes:
[...]
It's main potential usefulness is not in the definition of the function,
but in calls to the function. If the calls occur in a different
translation unit from the definition, the compiler does not have the
needed information.
It does if the visible declaration has the same information.
On Sat, 10 May 2025 06:43:38 -0700
Tim Rentsch <tr.17687@z991.linuxsc.com> wrote:
Michael S <already5chosen@yahoo.com> writes:
On Sun, 04 May 2025 07:31:11 -0700
Tim Rentsch <tr.17687@z991.linuxsc.com> wrote:
[...]
Also having just one form of loop with pre-condition strikes me as
more elegant. Since elegance is strongly subjective, I have no
logical arguments in support of me feelings.
In a recent posting I gave some statistics about the three different
kinds of looping controls (while,for,do/while). do/while loops were
almost 20% of all loops, and more than a quarter of the two kinds of
loops other than for(). Besides being a more pragmatic choice, I
think having the three kinds of loops be distinct in the language is
a better choice, because how we think of the different kinds of loop
is different, and it's helpful to have those differences be readily
apparent in the program, rather than needing to reconstruct them by
looking at code around the loop control fragment.
That sounds like misunderstanding.
I didn't suggest one loop construct. I suggested two constructs: for()
for loops with pre-condition and do-while for loops with post-condition.
On Sat, 10 May 2025 14:29:50 -0700
Tim Rentsch <tr.17687@z991.linuxsc.com> gabbled:
Muttley@dastardlyhq.com writes:
On Sat, 10 May 2025 06:43:38 -0700
Tim Rentsch <tr.17687@z991.linuxsc.com> gabbled:
never necessary). Also it isn't easy to think of a good substitute
word that might be given for this use of 'static', so maybe the
Isn't it?
Where "static" means local to a module the words
"local","module","limit" spring to mind which are far closer to
the intended meaning. Reusing "static" seems somewhat perverse
IMO.
The use I'm talking about here may be illustrated as follows:
double
average( double values[ static 10 ] ){
double total = 0;
for( int i = 0; i < 10; i++ ){
total += values[i];
}
return total / 10;
}
What word would you suggest to be used in place of 'static'
there?
If I knew what the hell it was supposed to do I'd tell you.
James Kuyper <jameskuyper@alumni.caltech.edu> writes:
[...]
It's main potential usefulness is not in the definition of the
function, but in calls to the function. If the calls occur in
a different translation unit from the definition, the compiler
does not have the needed information.
It does if the visible declaration has the same information.
Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
James Kuyper <jameskuyper@alumni.caltech.edu> writes:
[...]
It's main potential usefulness is not in the definition of the
function, but in calls to the function. If the calls occur in
a different translation unit from the definition, the compiler
does not have the needed information.
It does if the visible declaration has the same information.
Like 'restrict', parameter array length information, specified by
way of 'static', is ignored outside of function definitions. As
was intended (with 'restrict' also).
Furthermore, and also like 'restrict', there is no general
way to verify at compile time that the stipulated condition
holds.
Considering the above, it's better to observe the status quo, andI was not suggesting any change to the status quo, only explaining why
leave any diagnostics up to the discretion of the implementation,
rather than try to retrofit an incompatible change that would
make an infringement be a constraint violation that can't be
checked anyway.
On Tue, 06 May 2025 05:59:20 -0700
Tim Rentsch <tr.17687@z991.linuxsc.com> wrote:
Michael S <already5chosen@yahoo.com> writes:
On Wed, 16 Apr 2025 14:09:44 GMT
scott@slp53.sl.home (Scott Lurndal) wrote:
bart <bc@freeuk.com> writes:
On 15/04/2025 20:07, Scott Lurndal wrote:
Let's look at that C again:
for (int i = 1; i < 11; i += 1) # 15 tokens; C
for(i = 1; i++ <= 10;)
I'd reject this code during review.
Hopefully, you too.
I'm curious to know the basis for your reaction. What about the
code would prompt your judgment to reject it? Is it just a
specific reaction, or does it represent some more general pattern
(and if so then what more general pattern)?
First and foremost it was tongue-in-cheek reaction to the article
that Scott posted 2-3 minutes after the article that I was
reacting to.
At the next level, it is true that if I do detailed code review
(which I almost never do) I would indeed reject this code. The
first and sufficient reason is that the code looks superficially
similar to very common idiom, but significantly differs in
behavior. An additional reason is that post-increment operator is
generally harder to reason about than almost any other C operator,
esp. so when used as part of comparison, so, IMHO, readable code
should limit use of post-increment to very common idioms (or
altogether avoid it). The third reason, related to the first two
is that behavior of this loop is way too surprising.
Muttley@dastardlyhq.com writes:
On Sat, 10 May 2025 14:29:50 -0700
Tim Rentsch <tr.17687@z991.linuxsc.com> gabbled:
Muttley@dastardlyhq.com writes:
On Sat, 10 May 2025 06:43:38 -0700
Tim Rentsch <tr.17687@z991.linuxsc.com> gabbled:
never necessary). Also it isn't easy to think of a good substitute
word that might be given for this use of 'static', so maybe the
Isn't it?
Where "static" means local to a module the words
"local","module","limit" spring to mind which are far closer to
the intended meaning. Reusing "static" seems somewhat perverse
IMO.
The use I'm talking about here may be illustrated as follows:
double
average( double values[ static 10 ] ){
double total = 0;
for( int i = 0; i < 10; i++ ){
total += values[i];
}
return total / 10;
}
What word would you suggest to be used in place of 'static'
there?
If I knew what the hell it was supposed to do I'd tell you.
When you find out please post your suggested replacement.
Michael S <already5chosen@yahoo.com> writes:
On Sat, 10 May 2025 06:43:38 -0700
Tim Rentsch <tr.17687@z991.linuxsc.com> wrote:
Michael S <already5chosen@yahoo.com> writes:
On Sun, 04 May 2025 07:31:11 -0700
Tim Rentsch <tr.17687@z991.linuxsc.com> wrote:
[...]
Also having just one form of loop with pre-condition strikes me as
more elegant. Since elegance is strongly subjective, I have no
logical arguments in support of me feelings.
In a recent posting I gave some statistics about the three
different kinds of looping controls (while,for,do/while).
do/while loops were almost 20% of all loops, and more than a
quarter of the two kinds of loops other than for(). Besides being
a more pragmatic choice, I think having the three kinds of loops
be distinct in the language is a better choice, because how we
think of the different kinds of loop is different, and it's
helpful to have those differences be readily apparent in the
program, rather than needing to reconstruct them by looking at
code around the loop control fragment.
That sounds like misunderstanding.
I didn't suggest one loop construct. I suggested two constructs:
for() for loops with pre-condition and do-while for loops with post-condition.
Yes, I did misunderstand you. The alternative meaning didn't occur
to me. I see it now.
The same posting I mentioned in the previous response gave while()
loops as occurring more than for() and do/while() combined. I
conceptualize for() loops quite differently than while() loops, and
vice versa. By analogy to the only-two-kinds-of-loops suggestion,
the language could forego switch() and provide only if()/else.
To
me, neither of those what-might-be-called-simplifications seems like
a good trade. Have you asked other people to see what their
reactions are?
Incidentally, a language change to C was once proposed, so that
a do/while could also have an expression after the while().
Note that this change is backwards compatible with C as it is,
because a simple semicolon after while() is an empty statement.
Adopting this proposal would mean that the language would allow,
for example
do {
// this part will always be done at least once
// and once more for each time 'condition' is true
} while( condition ){
// this part will be done zero or more times
}
allowing a convenient way of writing "loop-and-a-half" type
loops. Do you have any reaction to this idea? Suppose
the just plain while() style were eliminated, so ordinary
while() loops would instead be written as
do ; while( condition ){
// body of while loop
}
Is that better or worse than having a separate while() statement?
On Sun, 11 May 2025 17:59:31 -0700
Tim Rentsch <tr.17687@z991.linuxsc.com> wibbled:
Muttley@dastardlyhq.com writes:
If I knew what the hell it was supposed to do I'd tell you.
When you find out please post your suggested replacement.
average( double values[ pointless_keyword 10 ] ){
Tim Rentsch <tr.17687@z991.linuxsc.com> writes:
Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
Tim Rentsch <tr.17687@z991.linuxsc.com> writes:
Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
James Kuyper <jameskuyper@alumni.caltech.edu> writes:
[...]
It's main potential usefulness is not in the definition of the
function, but in calls to the function. If the calls occur in
a different translation unit from the definition, the compiler
does not have the needed information.
It does if the visible declaration has the same information.
Like 'restrict', parameter array length information, specified by
way of 'static', is ignored outside of function definitions. As
was intended (with 'restrict' also).
I think that by "is ignored", you mean that compilers are not
required to pay attention to it. [...]
I mean it has no effect on program semantics.
It affects whether a program has defined or undefined behavior.
(Yes, a conforming compiler could ignore it.)
Furthermore, and also like 'restrict', there is no general
way to verify at compile time that the stipulated condition
holds.
Right, but that doesn't prevent implementations from issuing
useful warnings when it can determine that the stipulated
condition does not hold.
True, but in many cases they can't, because that information
is not part of the function's type and so often it is not
present.
Why would it not be present?
If you mean that someone might use `[static N]` in a function's
definition but not in a declaration, sure, but I would always
make sure they match as closely as possible. [...]
Considering the above, it's better to observe the status quo, and
leave any diagnostics up to the discretion of the implementation,
rather than try to retrofit an incompatible change that would
make an infringement be a constraint violation that can't be
checked anyway.
Observing the status quo is better than what, exactly?
Better than than trying to retrofit an incompatible change that
would make an infringement be a constraint violation that can't
be checked anyway.
And as you seem to have agreed, nobody suggested that. Were we
supposed to guess what it's better than? Or did I miss something?
It strikes me that you have a rather self-centered view of the
world.
The vast majority of all C code is written by people other
than yourself.
Tim Rentsch <tr.17687@z991.linuxsc.com> writes:
Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
James Kuyper <jameskuyper@alumni.caltech.edu> writes:
[...]
It's main potential usefulness is not in the definition of the
function, but in calls to the function. If the calls occur in
a different translation unit from the definition, the compiler
does not have the needed information.
It does if the visible declaration has the same information.
Like 'restrict', parameter array length information, specified by
way of 'static', is ignored outside of function definitions. As
was intended (with 'restrict' also).
I think that by "is ignored", you mean that compilers are not
required to pay attention to it. [...]
Furthermore, and also like 'restrict', there is no general
way to verify at compile time that the stipulated condition
holds.
Right, but that doesn't prevent implementations from issuing
useful warnings when it can determine that the stipulated
condition does not hold.
Considering the above, it's better to observe the status quo, and
leave any diagnostics up to the discretion of the implementation,
rather than try to retrofit an incompatible change that would
make an infringement be a constraint violation that can't be
checked anyway.
Observing the status quo is better than what, exactly?
The status quo is that the "[static N]" syntax has been in the
language since C99, and programmers and implementations are free
to take advantage of it. I don't recall anyone in this thread
proposing a change to that.
Tim Rentsch <tr.17687@z991.linuxsc.com> writes:
Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
James Kuyper <jameskuyper@alumni.caltech.edu> writes:
[...]
It's main potential usefulness is not in the definition of the
function, but in calls to the function. If the calls occur in
a different translation unit from the definition, the compiler
does not have the needed information.
It does if the visible declaration has the same information.
Like 'restrict', parameter array length information, specified by
way of 'static', is ignored outside of function definitions. As
was intended (with 'restrict' also).
? What "static" does when applied to an array parameter's length is to
render the behavior undefined if the function is called with a pointer
that points to an array that is shorter than the specified length. Are
you saying that it has no such effect except for inside the function definition? I'm not sure what that would even mean - is the behavior undefined only for recursive calls to the function?
Furthermore, and also like 'restrict', there is no general
way to verify at compile time that the stipulated condition
holds.
As I already mentioned, if such verification could have been
generally possible, it should have been a constraint violation.
Because it is not possible in general, but only in certain cases,
making the behavior undefined is the best that can be done. Doing
so encourages implementations to generate a diagnostic when those
cases do come up.
Considering the above, it's better to observe the status quo, and
leave any diagnostics up to the discretion of the implementation,
rather than try to retrofit an incompatible change that would
make an infringement be a constraint violation that can't be
checked anyway.
I was not suggesting any change to the status quo, [...]
On 5/11/25 06:02, David Brown wrote:
On 11/05/2025 10:21, Muttley@dastardlyhq.com wrote:...
On Sat, 10 May 2025 14:29:50 -0700
Tim Rentsch <tr.17687@z991.linuxsc.com> gabbled:
The use I'm talking about here may be illustrated as follows:
double
average( double values[ static 10 ] ){
double total = 0;
for( int i = 0; i < 10; i++ ){
total += values[i];
}
return total / 10;
}
What word would you suggest to be used in place of 'static'
there?
If I knew what the hell it was supposed to do I'd tell you.
Using "static" inside array parameters is, IME, extremely rare. It was
added in C99, and tells the compiler that whenever "average" is called,
the "values" parameter points to an array of at least 10 doubles. It
More precisely, it makes it undefined behavior for values to point to an array of less than 10 doubles.
does not affect the signature of the function or compatibility with any
other declarations, and is AFAIK rarely checked by compilers.
I'd have preferred it if violating that requirement was a constraint violation, but it can't be, because there are many cases where a
compiler cannot be sure how long the array is that a pointer points at.
However, the fact that the behavior is undefined justifies a compiler reacting to the case when it can be sure that the requirement will be violated.
That's the main reason I like this feature, and dislike
compilers that fail to take advantage of that opportunity. I never
considered it to be about efficiency, though there are cases where it
can result in more efficient code.
In an example like the one above, it is completely useless for
compilation - it tells the compiler nothing that it does not already
know. An optimising compiler will see that you are accessing values[0]
to values[9], and if it can get better results through vectorising,
prefetching, etc., then it will do so. (You can argue that the "static
10" is still useful as an indicator to human readers, but I am not
convinced of that.)
It's main potential usefulness is not in the definition of the function,
but in calls to the function. If the calls occur in a different
translation unit from the definition, the compiler does not have the
needed information.
Tim Rentsch <tr.17687@z991.linuxsc.com> writes:
[...]
It isn't just that checking the condition cannot be done in general.[...]
To be reliable the parameter length information would need to be
part of the function's type. That has implications for type
compatibility and also for the types of pointers-to-function. And
it would mean that removing a 'static' array length specification on
a function definition would necessitate also changing the functions
declarations, plus any affected pointers-to-function. Not worth it,
even if in theory it were doable.
In my opinion, keeping a function's definition and declarations
consistent is absolutely worth it, even if the language might not
require it.
Tim Rentsch <tr.17687@z991.linuxsc.com> writes:
Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
Tim Rentsch <tr.17687@z991.linuxsc.com> writes:
Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
James Kuyper <jameskuyper@alumni.caltech.edu> writes:
[...]
It's main potential usefulness is not in the definition of the
function, but in calls to the function. If the calls occur in
a different translation unit from the definition, the compiler
does not have the needed information.
It does if the visible declaration has the same information.
Like 'restrict', parameter array length information, specified by
way of 'static', is ignored outside of function definitions. As
was intended (with 'restrict' also).
I think that by "is ignored", you mean that compilers are not
required to pay attention to it. [...]
I mean it has no effect on program semantics.
It affects whether a program has defined or undefined behavior.
(Yes, a conforming compiler could ignore it.)
It isn't just that checking the condition cannot be done in general.
To be reliable the parameter length information would need to be
part of the function's type.
James Kuyper <jameskuyper@alumni.caltech.edu> writes:
Tim Rentsch <tr.17687@z991.linuxsc.com> writes:
Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
James Kuyper <jameskuyper@alumni.caltech.edu> writes:
[...]
It's main potential usefulness is not in the definition of the
function, but in calls to the function. If the calls occur in
a different translation unit from the definition, the compiler
does not have the needed information.
It does if the visible declaration has the same information.
Like 'restrict', parameter array length information, specified by
way of 'static', is ignored outside of function definitions. As
was intended (with 'restrict' also).
? What "static" does when applied to an array parameter's length is to
render the behavior undefined if the function is called with a pointer
that points to an array that is shorter than the specified length. Are
you saying that it has no such effect except for inside the function
definition? I'm not sure what that would even mean - is the behavior
undefined only for recursive calls to the function?
What I mean is that such uses of 'static' have no effect when
used in declarations that are not part of a definition.
On 12/05/2025 15:19, Tim Rentsch wrote:
It strikes me that you have a rather self-centered view of the
world.
So do you. So do I. So does everybody else. Your eyes are at the
precise centre of your observable universe.
Richard Heathfield <rjh@cpax.org.uk> writes:
On 12/05/2025 15:19, Tim Rentsch wrote:
It strikes me that you have a rather self-centered view of the
world.
So do you. So do I. So does everybody else. Your eyes are at the
precise centre of your observable universe.
I think you have misunderstood me.
(At some later time I may have more to say about your other
comments, but it seemed important to respond to this part
more promptly.)
Tim Rentsch <tr.17687@z991.linuxsc.com> writes:
[...]
It isn't just that checking the condition cannot be done in general.
To be reliable the parameter length information would need to be
part of the function's type.
The problem is much deeper than that. The same pointer can point
to different arrays, or different positions in the same array,
during different passes through the same line of code. Some of
those would violate this rule, others would not. I don't see how
violating such a rule could ever be made a constraint violation.
[...]
David Brown <david.brown@hesbynett.no> writes:
[...]
I am not convinced that it is actually useful as an error checking[...]
mechanism in many situations. In a lot of code, you simply don't have
a compile-time checkable array sizes - you have a pointer, and a
run-time variable for the size. When you are calling your function
with a "static" array size, the compiler does not have any way to
check your pointer for correctness.
Using [static N] in an array parameter declaration also requires the
caller to pass a non-NULL pointer. If I want to tell the compiler
that an argument must not be a NULL pointer, I can write:
void func(int arg[static 1]);
func(NULL) then has undefined behavior, and a compiler is likely
to warn about it. (Of course some UB will be missed if the compiler
can't detect it.)
[...]
Incidentally, a language change to C was once proposed, so that
a do/while could also have an expression after the while().
Note that this change is backwards compatible with C as it is,
because a simple semicolon after while() is an empty statement.
Adopting this proposal would mean that the language would allow,
for example
do {
// this part will always be done at least once
// and once more for each time 'condition' is true
} while( condition ){
// this part will be done zero or more times
}
allowing a convenient way of writing "loop-and-a-half" type
loops. [...]
On 12/05/2025 15:19, Tim Rentsch wrote:
It strikes me that you have a rather self-centered view of the
world.
So do you. So do I. So does everybody else. Your eyes are at the
precise centre of your observable universe.
The vast majority of all C code is written by people other
than yourself.
And the vast majority of those people are entitled to express their
opinion, just as you are entitled to express yours and Keith is
entitled to express his.
I hope I'm wrong, but it strikes me that you're trying to pick a fight
with Keith. No doubt you have your reasons, but I would have expected
better from a man of your calibre.
On 12/05/2025 15:19, Tim Rentsch wrote:
[...]
[...]
I hope I'm wrong, but it strikes me that you're trying to pick a fight
with Keith. No doubt you have your reasons, but I would have expected
better from a man of your calibre.
Hey! You do you. What would I know? And it really is none of my
business, which is precisely why, back in the day, grown-ups who wanted
to lock horns would take it to email.
On 11/05/2025 23:43, James Kuyper wrote:[...]
More precisely, it makes it undefined behavior for values to point to an
array of less than 10 doubles.
The wording of the C standard (C11, as that's what I have open at the
moment) is :
"""
If the keyword static also appears within the [ and ] of the array type derivation, then for each call to the function, the value of the corresponding actual argument shall provide access to the first element
of an array with at least as many elements as specified by the size expression.
"""
[...]
Janis Papanagnou <janis_papanagnou+ng@hotmail.com> writes:
On 12.05.2025 17:08, David Brown wrote:
On 11/05/2025 23:43, James Kuyper wrote:
[...]
More precisely, it makes it undefined behavior for values to point to an >>>> array of less than 10 doubles.
The wording of the C standard (C11, as that's what I have open at the
moment) is :
"""
If the keyword static also appears within the [ and ] of the array type
derivation, then for each call to the function, the value of the
corresponding actual argument shall provide access to the first element
of an array with at least as many elements as specified by the size
expression.
"""
Oh! - This is somewhat surprising to me. - When I first read about
the "arr[static N]" I assumed that it would be possible to pass any
sub-array, say "&arr[8]" (or "arr+8") as long as it's large enough
to still have N elements from the starting point, but the quote says
explicitly "access to the _first_ element of an array" (which I'd
interpret as "&arr[0]" (or "arr"). - Unless I misinterpreted that;
what would be the reason for that?
My personal interpretation is that this:
void func(int arr[static 5]) {
}
int main(void) {
int arr[10];
func(arr+5); // OK
// func(arr+6); // UB
}
is valid, because, for example, the last 5 elements of a 10-element
array object can be treated as a 5-element array object. gcc seems
to agree, based on the fact that it warns about func(arr+6) but
not about func(arr+5).
This is a fundamental part of my mental model of C, but in a few
minutes of searching I wasn't able to find explicit wording in the
standard that supports it.
Tim Rentsch <tr.17687@z991.linuxsc.com> writes:
Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
[...]
My personal interpretation is that this:
void func(int arr[static 5]) {
}
int main(void) {
int arr[10];
func(arr+5); // OK
// func(arr+6); // UB
}
is valid, because, for example, the last 5 elements of a 10-element
array object can be treated as a 5-element array object. gcc seems
to agree, based on the fact that it warns about func(arr+6) but
not about func(arr+5).
This is a fundamental part of my mental model of C, but in a few
minutes of searching I wasn't able to find explicit wording in the
standard that supports it.
In N1570, 6.7.6.3 p7.
Did you mean to imply that that paragraph supports (or refutes) my
statement? [...]
"""
A declaration of a parameter as ??array of _type_?? shall
be adjusted to ??qualified pointer to _type_??, where the
type qualifiers (if any) are those specified within the [ and ]
of the array type derivation. If the keyword static also appears
within the [ and ] of the array type derivation, then for each call
to the function, the value of the corresponding actual argument
shall provide access to the first element of an array with at least
as many elements as specified by the size expression.
"""
The question is whether, for example, the last 5 elements of a
10-element array object can be treated as a 5-element array object.
If someone can cite wording in the standard that answers that
question, I'd appreciate it. (I'll be happier if the answer is yes.)
Looking into this a bit more, I realize that the question doesn't
matter if there's no "static" keyword between the [ and ]. In that
case, the parameter is of pointer type, and the description of
pointer arithmetic (N1570 6.5.6p8) explicitly allows the pointer
to point to the i-th element of an array object. The wording for
[static N] is the only place I've seen (so far) that specifically
refers to the *first* element of an array object, raising the
question of whether a subobject of an array object is itself an
array object.
This might just be some slightly sloppy wording that was
introduced in C99 and never corrected.
For example, given this code:
```
void without_static(int arr[]) {
(void)arr[4];
}
void with_static(int arr[static 5]) {
(void)arr[4];
}
int main(void) {
int arr[10] = { 0 };
without_static(arr+5);
with_static(arr+5);
}
```
there's no problem with the call `without_static(arr+5)`, but the
call `with_static(arr+5)` has defined behavior if and only if the
last 5 elements of a 10-element array object can be treated as a
5-element array object.
Tim Rentsch <tr.17687@z991.linuxsc.com> writes:...
Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
Tim Rentsch <tr.17687@z991.linuxsc.com> writes:
Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:[...]
My personal interpretation is that this:
void func(int arr[static 5]) {
}
int main(void) {
int arr[10];
func(arr+5); // OK
// func(arr+6); // UB
}
is valid, because, for example, the last 5 elements of a 10-element
array object can be treated as a 5-element array object. gcc seems
to agree, based on the fact that it warns about func(arr+6) but
not about func(arr+5).
This is a fundamental part of my mental model of C, but in a few
minutes of searching I wasn't able to find explicit wording in the
standard that supports it.
In N1570, 6.7.6.3 p7.
Did you mean to imply that that paragraph supports (or refutes) my
statement? [...]
No. I posted the reference to say that the cited paragraph supports
the conclusion that 'func(arr+6)' is undefined behavior.
To me it seems obvious that 6.7.6.3 p7 is meant to cover the
case of 'func(arr+6)' as being undefined behavior.
But that's not the question I was addressing. My question is whether func(arr+5) has defined behavior, based on whether or not a+5 points to
the *first element* of an array.
is valid, because, for example, the last 5 elements of a 10-element
array object can be treated as a 5-element array object. gcc seems
to agree, based on the fact that it warns about func(arr+6) but
not about func(arr+5).
This is a fundamental part of my mental model of C, but in a few
minutes of searching I wasn't able to find explicit wording in the
standard that supports it.
Tim Rentsch <tr.17687@z991.linuxsc.com> writes:
Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
Tim Rentsch <tr.17687@z991.linuxsc.com> writes:
Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
[...]
My personal interpretation is that this:
void func(int arr[static 5]) {
}
int main(void) {
int arr[10];
func(arr+5); // OK
// func(arr+6); // UB
}
is valid, because, for example, the last 5 elements of a 10-element
array object can be treated as a 5-element array object. gcc seems
to agree, based on the fact that it warns about func(arr+6) but
not about func(arr+5).
This is a fundamental part of my mental model of C, but in a few
minutes of searching I wasn't able to find explicit wording in the
standard that supports it.
In N1570, 6.7.6.3 p7.
Did you mean to imply that that paragraph supports (or refutes) my
statement? [...]
No. I posted the reference to say that the cited paragraph supports
the conclusion that 'func(arr+6)' is undefined behavior.
I wish you had said so in the first place. Of course func(arr+6) has undefined behavior. Did anyone in this thread say or imply otherwise?
"""
A declaration of a parameter as ??array of _type_?? shall
be adjusted to ??qualified pointer to _type_??, where the
type qualifiers (if any) are those specified within the [ and ]
of the array type derivation. If the keyword static also appears
within the [ and ] of the array type derivation, then for each call
to the function, the value of the corresponding actual argument
shall provide access to the first element of an array with at least
as many elements as specified by the size expression.
"""
The question is whether, for example, the last 5 elements of a
10-element array object can be treated as a 5-element array object.
If someone can cite wording in the standard that answers that
question, I'd appreciate it. (I'll be happier if the answer is yes.)
To me it seems obvious that 6.7.6.3 p7 is meant to cover the
case of 'func(arr+6)' as being undefined behavior.
But that's not the question I was addressing. My question is whether func(arr+5) has defined behavior, based on whether or not a+5 points to
the *first element* of an array.
Note that 6.7.6.3 p7 doesn't say "array object", it says just
"array". I believe the choice of wording is neither an accident nor
an oversight.
Then please explain what you see as the difference. Wording in the
standard to support the distinction would be welcome.
Given `int arr[10];`, do the last 5 elements of arr constitute an
"array"? Do they constitute an "array object"? And the same
questions for arr as a whole.
Looking into this a bit more, I realize that the question doesn't
matter if there's no "static" keyword between the [ and ]. In that
case, the parameter is of pointer type, and the description of
pointer arithmetic (N1570 6.5.6p8) explicitly allows the pointer
to point to the i-th element of an array object. The wording for
[static N] is the only place I've seen (so far) that specifically
refers to the *first* element of an array object, raising the
question of whether a subobject of an array object is itself an
array object.
Again, not an array object, just an array.
This might just be some slightly sloppy wording that was
introduced in C99 and never corrected.
I draw the opposite conclusion. The wording of 6.7.6.3 p7 was
carefully chosen so that it would cover cases like 'func(arr+6)'.
But func(arr+5) is the case I was wondering about. (That's why I
commented out the func(arr+6) call.)
On 5/13/25 22:37, Keith Thompson wrote:
Tim Rentsch <tr.17687@z991.linuxsc.com> writes:
Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
Tim Rentsch <tr.17687@z991.linuxsc.com> writes:
Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
[...]
My personal interpretation is that this:
void func(int arr[static 5]) {
}
int main(void) {
int arr[10];
func(arr+5); // OK
// func(arr+6); // UB
}
is valid, because, for example, the last 5 elements of a 10-element >>>>>> array object can be treated as a 5-element array object. gcc seems >>>>>> to agree, based on the fact that it warns about func(arr+6) but
not about func(arr+5).
This is a fundamental part of my mental model of C, but in a few
minutes of searching I wasn't able to find explicit wording in the >>>>>> standard that supports it.
In N1570, 6.7.6.3 p7.
Did you mean to imply that that paragraph supports (or refutes) my
statement? [...]
No. I posted the reference to say that the cited paragraph supports
the conclusion that 'func(arr+6)' is undefined behavior.
...
To me it seems obvious that 6.7.6.3 p7 is meant to cover the
case of 'func(arr+6)' as being undefined behavior.
But that's not the question I was addressing. My question is whether
func(arr+5) has defined behavior, based on whether or not a+5 points to
the *first element* of an array.
Tim - [...]
Kaz Kylheku <643-408-1753@kylheku.com> writes:
[...]
Progarms that manipulate strings using standard library functions often
take advantage of the above. Strings are defined as null-terminated
arrays; but it is very common for strings to be arrays that are displaced
within larger arrays.
To be pedantic, a string is defined as "a contiguous sequence of
characters terminated by and including the first null character".
The word "array" is not used. It does seem fairly obvious that
the contiguous sequence will be stored in an array (array object?),
but the standard doesn't quite say so.
Janis Papanagnou <janis_papanagnou+ng@hotmail.com> writes:
On 12.05.2025 17:08, David Brown wrote:
On 11/05/2025 23:43, James Kuyper wrote:[...]
More precisely, it makes it undefined behavior for values to point to an >>>> array of less than 10 doubles.
The wording of the C standard (C11, as that's what I have open at the
moment) is :
"""
If the keyword static also appears within the [ and ] of the array type
derivation, then for each call to the function, the value of the
corresponding actual argument shall provide access to the first element
of an array with at least as many elements as specified by the size
expression.
"""
Oh! - This is somewhat surprising to me. - When I first read about
the "arr[static N]" I assumed that it would be possible to pass any
sub-array, say "&arr[8]" (or "arr+8") as long as it's large enough
to still have N elements from the starting point, but the quote says
explicitly "access to the _first_ element of an array" (which I'd
interpret as "&arr[0]" (or "arr"). - Unless I misinterpreted that;
what would be the reason for that?
My personal interpretation is that this:
void func(int arr[static 5]) {
}
int main(void) {
int arr[10];
func(arr+5); // OK
// func(arr+6); // UB
}
is valid, because, for example, the last 5 elements of a 10-element
array object can be treated as a 5-element array object. gcc seems
to agree, based on the fact that it warns about func(arr+6) but
not about func(arr+5).
This is a fundamental part of my mental model of C, but in a few
minutes of searching I wasn't able to find explicit wording in the
standard that supports it.
[...]
I believe we can reassure Janis that your example code will be
considered valid by any real-world compiler.
But it would be nice to
find some combination of standard paragraphs that guarantee it.
On Sun, 11 May 2025 17:30:14 -0700
Tim Rentsch <tr.17687@z991.linuxsc.com> wrote:
Michael S <already5chosen@yahoo.com> writes:
[suggestion to keep for() and eliminate while()]
[a mention was made of statistics given in another posting
that reported a ratio of while()/for() of roughly 2.3 to 1.]
[upthread there was a different posting, from Richard
Heathfield, that gave statistics reflecting a ratio
of for()/while() of approximately 3 to 1, IIRC]
[...] while() does not help anything relatively to for().
My interpretation matches yours. I can't find any indication in the
standard of a definition of what an "array" actually means
Tim Rentsch <tr.17687@z991.linuxsc.com> writes:
Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
[...]
If you mean that someone might use `[static N]` in a function's
definition but not in a declaration, sure, but I would always
make sure they match as closely as possible. [...]
It strikes me that you have a rather self-centered view of the
world. The vast majority of all C code is written by people other
than yourself.
[...]
Tim, I am not interested in exchanging personal insults with you.
(Yes, the quoted text above comes across as in insult, however you
intended it.)
If the only way to avoid that is to filter out your posts, I will,
with some regret, do that. I would miss out on your technical
contributions, but I'm willing to pay that price to avoid your
hostility.
On 13/05/2025 06:42, Tim Rentsch wrote:
Richard Heathfield <rjh@cpax.org.uk> writes:
On 12/05/2025 15:19, Tim Rentsch wrote:
It strikes me that you have a rather self-centered view of the
world.
So do you. So do I. So does everybody else. Your eyes are at the
precise centre of your observable universe.
I think you have misunderstood me.
I hope so, because the alternative is unpalatable.
(At some later time I may have more to say about your other
comments, but it seemed important to respond to this part
more promptly.)
I think you chose the least important part to respond to, so maybe you
have misunderstood me, too.
I was trying to defend an honourable man from what seemed to me to be
an unfair and unnecessarily unkind observation. The paragraph you
quote above acknowledges that in some literal sense your accusation of self-centredness is accurate, and so implies by omission that the more metaphorical sense that I think you intended is /not/ accurate.
A fine distinction, some might say. But that's what the other
paragraphs were for.
On 5/14/25 07:00, David Brown wrote:
...
My interpretation matches yours. I can't find any indication in the
standard of a definition of what an "array" actually means
This is a problem with all of the derived types (6.2.5p25). There are definitions of the terms "array type", "structure type:, "union type", "function type", and "pointer type", but no definitions of the things
that those types are types of. My interpretation is that for each of
those object types, "X" is short-hand for "an object of X type". I
haven't figured out suitable wording to define what a function is,
since it isn't an object. The best I've come up with is "a thing of
function type".