• Re: Loops (was Re: do { quit; } else { })

    From bart@21:1/5 to James Kuyper on Sun Apr 20 16:53:27 2025
    On 20/04/2025 16:25, James Kuyper wrote:
    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.

    Yes it is. That doesn't means I'm incapable of writing such loops. But I
    prefer to use a syntax which involves less typing, has less opportunity
    for typos, and which anyone can instantly grok.




    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.

    And yet, you are unwilling to accept my criticisms of it, to the extent
    that you'd rather accuse me of being unable to program rather than admit
    I might be at least partly right.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Michael S on Sun Apr 20 16:25:14 2025
    On 20/04/2025 15:34, Michael S wrote:
    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.

    I don't quite get your point. So, given a generic form like:

    for x in A

    then some languages, mainly newer ones, will iterate through the values
    of A if it is not a range. If it is a range, then those values are the interpolated elements of the range.

    In other words, in something like:

    for x in 1..10

    x will take on the values 1, 2, ... up to 10 (depending on whether the
    range is closed or open in the language).

    Isn't this exactly what some of us are trying to do? So why is it a joke
    to desire such a nifty form of loop, and why are we losers to do so?

    There is nothing to stop any of those languages from also having a
    feature that does what C-for does, but they choose not to.

    However there are also a great number that do have a C-style 'for',
    often as the primary means of iteration, and there it usually looks
    completely out of place. (For example, JavaScript.)

    This was my opening observation in this sub-thread.



    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.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Michael S@21:1/5 to bart on Sun Apr 20 19:01:25 2025
    On Sun, 20 Apr 2025 16:25:14 +0100
    bart <bc@freeuk.com> wrote:


    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'.


    I never knew either BCPL or B, but from what I am reading, B is more
    primitive of the two. B was a simplified BCPL. BCPL itself was
    simplified CPL. CPL was so complicated that it took approximately 10
    years from definition to 1st implementation.

    Clearly the author of both (Ken Thompson) had no great love for
    'for'-loops; real ones.



    Ken Thompson was not an author of C. No doubts that Thompson
    influenced it, but still C is Ritchie's language.
    You can probably say that Dennis Ritchie was an author of both
    languages, even if he joined Thompson on later stages of design and implementation of B.

    The second language of Ken Thompson is Go which he co-authored with
    Robert Griesemer and Rob Pike almost 40 years after B.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Janis Papanagnou@21:1/5 to bart on Sun Apr 20 17:55:57 2025
    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.


    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.

    Janis

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Janis Papanagnou@21:1/5 to bart on Sun Apr 20 18:10:59 2025
    On 20.04.2025 17:25, bart wrote:

    [...], and why are we losers to do so?

    Not "we".

    (Your arguments are based on your preconditioned brain.
    Try to overcome that.)

    Janis

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Janis Papanagnou@21:1/5 to Michael S on Sun Apr 20 18:07:18 2025
    On 20.04.2025 16:34, Michael S wrote:
    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?

    The Unix standard shell has no counted loops, but it has indexed
    arrays, and a 'for' loop that effectively processes just lists.

    Kornshell, in its 1988 version the base of POSIX shell, invented
    counted loops in its 1993 version but (while adopted by e.g. Bash)
    this did not enter the Unix standard. Kornshell also supported
    associative arrays. The Unix shells are a big grown conglomerate
    of ancient crude syntax. Some shells try to "orthogonalize" their
    feature set, but Unix shells are still no appropriate measure for
    a sensible language design.


    Not all, just many.

    Janis

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Janis Papanagnou on Sun Apr 20 17:28:48 2025
    On 20/04/2025 16:55, Janis Papanagnou wrote:
    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.

    Huh?????

    You CLEARLY suggested such a proposal would be an April Fool's joke.

    You CLEARLY chose to name the keyword 'for_losers'.

    I didn't make either of those up; YOU did! Proof:

    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])'.

    So what am I missing? What DID you mean if it wasn't that?

    Or is it even worth my asking if you're just going to give me a runaround?




    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"?

    That fact that you are asking about it. Optional syntax is hardly novel
    in any language.


    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.

    Would it kill you to just answer Yes or No for once?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Janis Papanagnou@21:1/5 to bart on Sun Apr 20 18:46:32 2025
    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.

    (Why do you repeatedly show us your frustration about the "C"
    language in every post and complain about completely *unrelated*
    things to what was said.)


    Show me a for-loop that cannot be expressed with existing features. Many
    are likely to be clearer!

    (There have been sufficient examples posted.)

    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.
    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.[*]

    [*] 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.

    Either into one over-long line,
    or spilling over multiple lines; both should fail a code review.

    (I doubt you've ever been engaged in professional code reviews.)

    [...]

    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.

    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.
    (Unless you have no "design principles" and just featurities as a
    trigger for evolution of your language.) - Anyway, I don't care.

    Janis

    [...]

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Janis Papanagnou on Sun Apr 20 20:51:26 2025
    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. Only YOU are allowed to call adding one extra
    loop type 'overloading' of the language. But *I* am not allowed to call
    the plethora of integer types and support macros 'overloading' of the
    same language.


    Show me a for-loop that cannot be expressed with existing features. Many
    are likely to be clearer!

    (There have been sufficient examples posted.)

    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.


    [*] 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 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.


    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.

    But they do! Try looking at hundreds of thousands of lines of open
    source projects like I do.

    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 usually agreed to be better coding style. The same applies to
    complex type declarations which everyone tells me should use typedefs to
    break them up.

    But when 'for' is involved, it seems all common sense goes out the Window.

    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.

    Here I would go further and use a short loop index:

    m = 0;
    while (m < 12 && yearDay >= yearDays[leapYear][m]) ++m;

    This one is a simple iteration:

    for (character = 0; character <= 255; character++) {

    At least I can see it. However this is simply crying out to be written as:

    for (ch in 0..255)

    You don't get that? You'd rather keep it long-winded because you can do linked-lists too?!

    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'.

    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.

    Huh? I have very ordinary 'for' statements that are similar to dozens of
    other languages.

    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.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Keith Thompson on Mon Apr 21 00:29:52 2025
    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:

    p = sqliteHashFirst(&pSchema->trigHash);
    while (p != NULL)
    {
    sqlite3DeleteTrigger(db, (Trigger*)sqliteHashData(p));
    p = sqliteHashNext(p) )
    }

    The same number of lines, but now:

    * The one-time initialisatioin is isolated and out of the way

    * The execution order is more natural: the increment follows the body
    as it does at runtime.

    * It's clearly a while loop which repeats until some pointer is NULL

    (Note there can be minor differences in behaviour with statements like 'continue', or variable scope when the 'for' declares its variables.)


    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!)


    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?

    By me. One use-case was porting code from C, but I didn't do much of
    that either.

    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!

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kenny McCormack@21:1/5 to Keith.S.Thompson+u@gmail.com on Sun Apr 20 23:19:37 2025
    In article <87ldruv65j.fsf@nosuchdomain.example.com>,
    Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:
    ...
    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.

    I'm with Bart on this one. I think "for" loops that aren't simple
    iterations (i.e., do this thing 10 times), are harder to understand than
    the equivalent "while" loop. I.e., this:

    p = someComplexFunctionCall(this,that,the,other,thing);
    while (p) {
    do stuff;
    p = getNewValue(p);
    }

    is a lot easier to understand at first glance than the equivalent:

    for (p = someComplexFunctionCall(this,that,the,other,thing); p; p = getNewValue(p))
    do stuff;

    But, having said that, I'll always write the "for" version, because I like writing compact (some would say "cryptic") code. Job security, and all that...

    --
    Meatball Ron wants to replace the phrase "climate change" with the phrase "energy dominance" in policy discussions.

    Yeah, like that makes a lot of sense...

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Waldek Hebisch@21:1/5 to bart on Mon Apr 21 03:07:20 2025
    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:

    for(int i = 0; i < B; ++i)

    or maybe a slightly more general form:

    for(int i = A; i < B; ++i)

    This restricition is easily checkable in mechanical way, checking
    it is a very small addition to C parser. You can modify your C
    compiler so that is will flag any "non simple" 'for' loop as an
    error. So, this is really non-issue for anybody who wants to
    use C, but is bothered by errors that you mention.

    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. And even then do not
    think that you will "win" the argument. Ada folks had strong
    arguments, namely there was a company having mixed codebase,
    partially in C, partially in Ada, with both parts of comparable
    size. They had various statistics inluding defects, showing
    that Ada was about twice time as productive as C, that is they
    were able to get the same functionality with significantly
    smaller (IIRC more than two times smaller) number of defects
    and at lower developement cost. Popularity of Ada should
    tell you how C programmers reacted to this data.

    Note: one can question validity of the Ada versus C data, in
    particular if it generalizes to other settings. However, this
    data is the best comparative data that I am aware of and I have
    little hope to see better comparison. There are also data
    about actual failures in C code. Here top issue was
    confusion between '=' and '==' in conditionals (this probably
    motivated warnings in gcc and clang about using naked assignment
    in comparisons). Guessing or hand picked examples are
    not a substitute for serious research. In particular, some
    potential issues seem to be no problem in practice, that
    is did not lead to known failure in a largish collection
    of C programs.

    BTW: In modern software number of defects due to issues that
    you discuss seem to be much lower than in the past, partially
    due to warnings from C compilers. OTOH memory errors seem to
    dominate and AFAICS you have nothing better than C have
    concering memory safety. In fact, one of most effective
    ways to improve memory safety of C programs is to use
    accessor macros or functions to check every array access.
    But this has acceptable cost only when one uses highly
    optimizing compiler.


    --
    Waldek Hebisch

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to bart on Mon Apr 21 03:16:06 2025
    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!
    }

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Kaz Kylheku on Mon Apr 21 12:57:13 2025
    On 21/04/2025 04:16, Kaz Kylheku wrote:
    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.

    I mentioned this. I would not call it an advantage, since it depends on
    the behaviour you want. It's just different.

    - The loop controls are not gathered in one place.

    The loop controls being .... the last line of the loop body?

    WHILE doesn't have the concept of a 'blob' that is a formal increment or
    step expression. Maybe that assignment to 'p' in the above example
    occurs in multiple places within the body.

    My own 'while' has such an optional step expression. It was intended to
    show how C could have implemented linked list traversal, since that's
    the go-to example of how flexible its 'for' is.

    I played with two kinds of syntax:

    while cond do
    body
    step
    incr # 'incr' represents anything
    end

    while cond, incr do
    body
    end

    When the experiment was over, I decided to keep that second syntax, and
    it is sometimes used. But I can't use it when the factors that will
    affect the next 'cond' are complex.

    Currently, from 20-30% of while loops use that form, almost exclusively
    for linked list traversal.

    It doesn't affect the normal while-loops. It doesn't impinge on normal
    for loops either.

    (My version of 'continue' will not skip the step part, but it is also
    very rarely used.)

    - 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!

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From James Kuyper@21:1/5 to bart on Mon Apr 21 07:34:11 2025
    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.

    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.

    It's been a while since I've written basic, but looking at that syntax I believe it would be equally feasible to write

    for i=start_year to months_per_year

    Can BASIC protect you from misusing it's for statement by putting in the
    wrong expressions for the start and end of the loop? Then why would
    expect C's loop construct to do any better?

    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.

    How can I respond by agreeing with an incorrect assessment? And the
    insults are the consequence of your endlessly successful search to find
    new ways to misunderstand C.

    The fact is that people make typos (obviously, not you or JP or SL or

    I make typos all the time. I love language features that let me catch my
    typos. That's one reason why I like languages that provide features like
    "for each employee in employee_list", but I see little advantage to a
    for statement limited in the fashion you desire. It's easily emulated by
    the more flexible for-statement that also allows me to do many other
    things as well.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Keith Thompson on Mon Apr 21 12:26:53 2025
    On 21/04/2025 03:08, Keith Thompson wrote:
    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.

    I used 'wonderful' to summarise what people arguing against me have said
    about it. Am I not allowed to use an adjective unless it's one that
    somebody else has already used? If so, how come they're allowed to use
    it first?

    You're being silly here.


    Multi-purpose? Yes, absolutely.

    You hit the nail on the head. Take this previously posted example:

    for (character = 0; character <= 255; character++) {

    Such syntax for a trivial iteration seems to be tolerated; why? Because
    it can also be adapted to traversing linked lists? That's just irrational!

    Indispensible? No, I don't believe anyone has made that claim, and it
    would be inaccurate.

    Well, any control flow can be implemented with goto. But if you suggest
    to people that they lose those bizarre capabilities of 'for' they will
    be aghast. How on earth will they express all those weird and wonderful constructs.

    Just read the thread if you don't believe me!

    Superior to an ordinary 'for'? Certainly a lot of people think it is, because of its flexibility.

    Yes, some people have said some of these. Are you suggesting that
    everyone needs to have said all of those things before I can make such a remark?

    I think it's quite clear what the attitude of most here is.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to James Kuyper on Mon Apr 21 13:26:17 2025
    On 21/04/2025 12:34, James Kuyper wrote:
    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.

    You don't understand the issue. OK, that's fine. Because it is possible
    to make typos at any point in the source code, then it doesn't matter if
    the language provides unnecessary extra opportunities for those errors.

    All those languages with strict type systems to allow more compile-time error-checking are pointless, since nothing stops you writing 'a = 0'
    instead of 'a = 1'.

    But there is also the human aspect; which of these for-loops have typos
    (assume that 'i' and 'n' are the right spellings):

    for (i=0; i<n; ++n)
    for (i=0; i<n; --i)
    for (i=0; i<=n; ++i)
    for (i=0; i<n; ++i)

    That fact is, you can't tell. It depends on intention, which is unclear /because/ of the flexibility of the construct. That second example looks
    dodgy, but the compiler can't pick that up either.

    In that case, I have to ask, why even bother with such a construct. Just
    have:

    i = 0;
    L1:
    if (i<n) goto L2;
    ...
    ++i;
    goto L1
    L2:

    This is even more flexible.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Waldek Hebisch on Mon Apr 21 13:46:21 2025
    On 21/04/2025 04:07, Waldek Hebisch wrote:
    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:

    What about when you're working with other people's code? Every for-loop
    you encounter is a little puzzle that you have to decode to find out its category! (There are about 4 categories plus 'free-style'.)

    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 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.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Waldek Hebisch@21:1/5 to bart on Mon Apr 21 13:51:04 2025
    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.

    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.

    --
    Waldek Hebisch

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Waldek Hebisch on Mon Apr 21 16:36:01 2025
    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.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to bart on Mon Apr 21 18:43:40 2025
    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".

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Kaz Kylheku on Mon Apr 21 20:57:13 2025
    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!)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to bart on Mon Apr 21 20:25:58 2025
    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.

    There is some sense in upper case for preprocessor constants.

    Those that have short names can easily clash with variables.

    Preprocessor constants are often under-typed.

    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?

    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); then
    execute stmt if p is not not null, and step to the next entry
    using sqliteHashNext(p), assigned back to p:

    #define for_sqlite_hash(P, H) \
    for ((P) = sqliteHashFirst(H); P; (P) = sqliteHashNext(P))

    I'm taking it for granted that sqliteHashFirst, which came from your
    post, initiates an iteration, etc. I intuited that without needing it explained.

    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?

    You're also claiming that "for X in A, B" (or what have you) is something wonderful compared to "for (X = A; X <= B; X++)".

    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 don't know. I
    might guess from its name that it is something to do with loops.

    You can guess from its name that it has something to do with loops
    over sqlite hashes.

    Just like I guessed from sqliteHashBegin that it has something to
    with initiating an iteration over a hash, using a pointer-typed
    cursor.

    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 added a local variable binding extension to GNU Awk (in an Enhanced GNU Awk fork).

    I gave it this syntax;

    let (x = 0, y = 1, z) { ... body ...}

    guess why! This works with macros. This doesn't:

    { let x = 0, y = 1, z; ... body ...}

    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.

    It's not easy to add new things by changing the language, when the language is widely deployed, and implemented by multiple vendors.

    Even the users who follow a single implementation might balk;
    "Sorry, it has to work with the language that is packaged for Ubuntu 20".

    It's a lot, lot better if a program can encode it sown language extension which works with all major implementations going back 20 years in their versions.

    A macro solution would anyway be poor in terms of nice syntax, error reporting and so on.

    Sure; but that can often be beaten by Works Everywhere.

    (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!)

    In a clean room language, you have an opportunity to make an excellent
    macro system which has answers for error reporting, identifier hygiene, multiple evaluation ...

    The weaknesses in C macros are a strawman against macros as such.

    Yet, I will take C macros over no macros.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Waldek Hebisch@21:1/5 to bart on Mon Apr 21 21:06:01 2025
    bart <bc@freeuk.com> wrote:
    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.)

    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.

    BTW: I do not know how Sqlite folks implemented 'sqliteHashNext',
    but I would expect semi-random order of elements. What matters
    is that 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.

    In short, this is separation of concerns.

    You seem be picking and choosing which concerns are in need of separation!

    Yes.

    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?

    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. Putting A just before
    'while' loop is not too bad, as it is still pretty close to the
    loop and B. But when using 'while' C potentially could be quite
    far from A and B.

    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'.

    No, this subtly different. I do not have problem with writing 'break',
    as it is mandatory in vast majority of C loops. You may think about
    it as mandatory sacrifice to gods of C. When you forget it you will
    be punished and after punishment you will remember to put it. Yes,
    it is silly but there is a lot of mandatory sillines in real life,
    one just adapts and goes on. The difference compared to 'for' is
    that there are easy alternatives to falltrough by default.

    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. If you do not mind some compiler complexity you
    can do better (I consider 'do ch = 0, 255' to be rather bad, IMO
    something like 'for ch in 0..255' or 'for ch := 0 to 255' is better).
    But IMO main reason that C won competition for system language was
    that C compiler could be quite simple while generating efficient
    code and allowing powerful constructs.

    IMO there are now good reasons to create a different system
    language. But C loops are tiny part of issues and alone (or
    even combined with other issues that you mentioned like 'break'-s
    in 'switch'-es and declaration syntax) not enough.

    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.

    Note that this is C group. If you find new way of writing C code
    which is more convenient and nicer than existing ways, then people
    here may be interested. Or if you propose a compatible extention.

    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. However, "better" includes factors as
    availability of optimizing compilers and libraries. And
    availabiity of skill: language which has interesting features
    that you do not understand is not good for you.

    --
    Waldek Hebisch

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Janis Papanagnou@21:1/5 to Keith Thompson on Tue Apr 22 00:14:44 2025
    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

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to bart on Mon Apr 21 22:16:54 2025
    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.

    (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.

    It has superior flexibility. Flexible is not a synonym for superior; flexibility doesn't make something unequivocally superior, unless we can
    hold "all else equal". We can almost never do that. Here, the flexible
    thing is more verbose, and there are challenges implementing a simple
    range iteration that can go (for instance) all the way to INT_MAX,
    whereas the less primitive loop can get that right and completely hide
    it from the programmer.

    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.

    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

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Janis Papanagnou@21:1/5 to Janis Papanagnou on Tue Apr 22 00:19:50 2025
    On 22.04.2025 00:14, Janis Papanagnou wrote:
    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.

    Ah, I forgot; but Bash seems to support comma-subexpressions in
    loops (as opposed to Ksh). - So this is even more valid (given
    all the inconsistencies and differences across shells and "C"):

    (As previously said, Unix shell in general and Bash specifically
    is not a good comparison WRT "C" loops.)

    Janis


    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Waldek Hebisch on Mon Apr 21 23:54:21 2025
    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.

    This is the original C again:

    for(p=sqliteHashFirst(&pSchema->trigHash); p; p=sqliteHashNext(p)){
    sqlite3DeleteTrigger(db, (Trigger*)sqliteHashData(p));
    }

    This is the version that my visualisation tool produced (where C source
    is visualised in my syntax); posted earlier but it's worth posting again:

    p := pSchema^.trigHash.first
    while p do
    sqlite3DeleteTrigger(db, ref Trigger(p^.data))
    p := p^.next
    od

    While the C version looks intimidating, this looks much cleaner and
    simpler (expanding those macros helped).

    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:

    for(p=pDel; p && (p->pLeft||p->pRight); p=(p->pLeft ? p->pLeft : p->pRight)){

    for (yoffset = coef->MCU_vert_offset; yoffset <
    coef->MCU_rows_per_iMCU_row; yoffset++) {

    for (bi = 0; bi < num_blocks; bi++, start_col +=
    compptr->DCT_h_scaled_size) {....}

    I /think/ the last two are simple iterations!

    Notice that that last bit of the last example is nothing to do with the mechanics of the iteration; it's to do with the body.

    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've seen plenty of for-loops that start with 'for (;...)'; that doesn't
    seem to worry anyone! Which is not surprising; nobody seems to care
    anything here.

    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.

    How complicated was a 1950s Fortran compiler? A 1964 BASIC interpreter?
    My own 1981 compiler implemented in 8KB? All supported such loops.

    There is no excuse. This is a Pascal program with such a loop:

    program loop(output);
    var c:integer;
    begin
    for c := 0 to 255 do
    writeln(c);
    end.

    I can run it with a toy Pascal compiler that is 22KB (of x64 code).

    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.

    And yet, even within the limitations of C, people favour writing
    appalling code, when it could be much cleaner. So (1) they don't care;
    and (2) try to justify that bad code as being better.

    Here's that visualisation example above, converted into C:

    p = pSchema->trigHash.first;
    while (p) {
    sqlite3DeleteTrigger(db, ref Trigger(p->data));
    p = p->next;
    }

    Compare with the original C at the top.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to bart on Mon Apr 21 23:46:40 2025
    bart <bc@freeuk.com> writes:
    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:

    Doesn't matter, nobody will ever look at the expansion. It's
    actually a good thing, since it found a bug in your compiler.


    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.


    Obviously the entire world-wide programming community
    disagrees with your opinions.

    Here's a very large C project that heavily uses
    a for-based macro.

    $ grep for_each kernel/*.c |wc -l
    284
    $ grep for_each kernel/*.c |head
    kernel/acct.c: for_each_vma(vmi, vma)
    kernel/audit_tree.c: list_for_each_entry_rcu(p, list, hash) { kernel/audit_tree.c: list_for_each_entry(owner, &new->trees, same_root) kernel/audit_tree.c: list_for_each_entry_safe(rule, next, &tree->rules, rlist) {
    kernel/audit_tree.c: list_for_each_entry(node, &tree->chunks, list) {
    kernel/audit_tree.c: list_for_each_entry(tree, &tree_list, list) { kernel/audit_tree.c: list_for_each_entry(node, &tree->chunks, list) kernel/audit_tree.c: list_for_each_entry(node, &tree->chunks, list)
    kernel/audit_watch.c: list_for_each_entry_safe(owatch, nextw, &parent->watches, wlist) {
    kernel/audit_watch.c: list_for_each_entry_safe(r, nextr, &owatch->rules, rlist) {

    $ grep for_each */*.c |wc -l
    1599
    $ grep for_each */*/*.c | wc -l
    12015
    $ grep for_each */*/*/*.c | wc -l
    9077

    include/linux/list.h:

    /**
    * list_prev_entry_circular - get the prev element in list
    * @pos: the type * to cursor.
    * @head: the list head to take the element from.
    * @member: the name of the list_head within the struct.
    *
    * Wraparound if pos is the first element (return the last element).
    * Note, that list is expected to be not empty.
    */
    #define list_prev_entry_circular(pos, head, member) \
    (list_is_first(&(pos)->member, head) ? \
    list_last_entry(head, typeof(*(pos)), member) : list_prev_entry(pos, member))

    /**
    * list_for_each - iterate over a list
    * @pos: the &struct list_head to use as a loop cursor.
    * @head: the head for your list.
    */
    #define list_for_each(pos, head) \
    for (pos = (head)->next; !list_is_head(pos, (head)); pos = pos->next)

    /**
    * list_for_each_rcu - Iterate over a list in an RCU-safe fashion
    * @pos: the &struct list_head to use as a loop cursor.
    * @head: the head for your list.
    */
    #define list_for_each_rcu(pos, head) \
    for (pos = rcu_dereference((head)->next); \
    !list_is_head(pos, (head)); \
    pos = rcu_dereference(pos->next))

    /**
    * list_for_each_continue - continue iteration over a list
    * @pos: the &struct list_head to use as a loop cursor.
    * @head: the head for your list.
    *
    * Continue to iterate over a list, continuing after the current position.
    */
    #define list_for_each_continue(pos, head) \
    for (pos = pos->next; !list_is_head(pos, (head)); pos = pos->next)

    ....

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Kaz Kylheku on Tue Apr 22 00:33:48 2025
    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 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.

    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
    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))))?((
    n2)=((lua_Number)(((((v2)->value_).i)))),1):0))){pc++;{TValue*io=((&(ra)->val));((io)->value_).n=(((n1)+(n2)));((io)->tt_=(((3)|((1)<<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.)

    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);

    This sounds a very specific type of macro, just to replace a simple
    loop. And it is simple; those identifiers make it look scary. This is
    the loop:

    p = a;
    while (p) {
    stmt;
    p = next(); // which turns out to be p = p->next
    }


    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.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Kaz Kylheku on Tue Apr 22 01:12:46 2025
    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.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Waldek Hebisch@21:1/5 to bart on Tue Apr 22 01:06:53 2025
    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:
    - 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 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.

    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.

    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.

    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.

    Your abuse is another person "taking advantage of available technology".
    One can write code in C so that rather dumb compiler will generate
    reasobly fast program. But such code is harder to write and
    error prone. Instead, one can write higher level code and
    depend on optimizer to get good efficiency.

    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
    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))))?
    ((n2)=((lua_Number)(((((v2)->value_).i)))),1):0))){pc++;{TValue*io=((&(ra)->val));((io)->value_).n=(((n1)+(n2)));((io)->tt_=(((3)|((1)<<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).

    In many cases one can use functions instead of macros, but sometimes
    macros depend on variable capture, so can not be replaced by functions. Similarly, gotos (to label outside of a macro) and case labels
    do not work well in inline functions. And in the past some compilers
    were rather shy with inlining, which could significantly reduce speed.

    --
    Waldek Hebisch

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Keith Thompson on Tue Apr 22 01:26:39 2025
    On 22/04/2025 00:12, Keith Thompson wrote:
    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?

    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.

    *You* find it much cleaner and simpler. I don't. What makes you right
    and everyone else wrong?

    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.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Waldek Hebisch on Tue Apr 22 02:21:32 2025
    On 21/04/2025 22:06, Waldek Hebisch wrote:
    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.

    You're guessing based on the identifiers chosen. But that lump of code
    has this shape:

    for(A; B; C)

    which is equivalent to: A; while (B) { body; C}

    However, you can see that A and C are assignments to the same variable,
    so you can reasonably refine to what I wrote above. But perhaps the
    English should have been 'While P is True' to avoid the assumption it is
    a pointer.

    You can't go beyond that however /from the shape of the code/.

    Remember, C-for is 'flexible'. That means it can literally be anything,
    but the general pattern of any such loop can be expressed with 'while'.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Waldek Hebisch on Tue Apr 22 02:24:22 2025
    On 22/04/2025 02:06, Waldek Hebisch wrote:
    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:
    - 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 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.

    I think it was 20 years ago that I read somewhere that the MS Visual
    Studio codebase was 1.5 million files. IIRC, it took 11 hours to build, non-optimised, and 60 hours optimised.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to bart on Tue Apr 22 03:31:39 2025
    On 2025-04-22, bart <bc@freeuk.com> wrote:
    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.

    If a streamlined loop were added to C, you'd be among the last coders
    to use it.

    (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++.

    But C++ is becoming more and more of a monstrosity specifically by
    acquiring numerous features that /cannot/ be achieved with its existing language-building features. (Sure, the language-building ones too,
    on the library side.)

    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.

    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.

    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.

    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.

    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.

    C macros definitely hinder all kinds of tooling.

    Why add a new core feature, when some tacky, half-assed macro can be
    used? Or, sometimes, a typedef.

    But, tacky as the macro is, it only pollutes the program where it is
    used, not the entire language.

    A badly designed macro getting into a program is less harmful than a
    badly designed feature getting into the language. (Where we recognize
    that such a feature could be a macro.)

    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]).

    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?

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Janis Papanagnou@21:1/5 to bart on Tue Apr 22 09:14:22 2025
    On 20.04.2025 21:51, bart wrote:
    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. [...]

    What rules? You're again making up things. - Instead of arguing on
    the topic you switched to something else that you dislike about "C".


    In C? I don't recall any examples in C that could be written without 'for'.

    (We've obviously been speaking about different things.)


    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).

    You find (and can make up) arbitrary examples of badly written code.

    How is that 'for' loop with all in one line worse than the respective
    'while' loop if stupidly written in one line.

    If you have complex expressions you format it accordingly. Moreover,
    you choose appropriate names for your variables and functions.


    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.

    You are talking nonsense. Again; how is that better than a 'while'
    loop where everything is on one line. - Are you deliberately playing
    the stupid?

    To keep things together means that you have the controlling elements
    together. To not have parts of the controlling elements at the very
    end of the block that the control construct controls.

    (From the other posts I see that the folks here understand that; so
    why are you playing the stupid?)

    [...]

    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.

    (I didn't say that. - Stop such misinterpretations and imputations!)

    [...]

    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? [...]

    (I haven't said that. - Stop it!)

    You're
    saying that:

    ONE; TWO; THREE

    is always better than:

    ONE;
    TWO;
    THREE;

    (No I haven't said that. - Stop it!)

    [...]

    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.

    You can simply (and obviously) format it appropriately...

    for (monthIdx = 0;
    monthIdx < 12 && yearDay >= yearDays[leapYear][monthIdx];
    monthIdx++)
    ;

    So the first step is to turn it into a while loop:

    Why? It's completely unnecessary to _rewrite_ that. A _reformat_ is
    not only simpler (and safer to do), it's the straightforward way.

    And if it still appears too complicated a style for your perception
    (or liking) you can also keep the 'for' loop just iterating over the
    'monthIdx' and put the exit condition explicitly in the controlled
    block with a 'break' if you prefer that.

    for (monthIdx = 0; monthIdx < 12; monthIdx++)
    if (yearDay < yearDays[leapYear][monthIdx])
    break;

    Both presented forms are IMO much better structured (and most easily recognizable) than your code following here...


    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.

    (Above "the original" was the 'for' loop.)

    [...]

    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'.

    You don't like it. Fine. (I don't like a lot about "C".) Who cares?

    Janis

    [...]

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Janis Papanagnou@21:1/5 to bart on Tue Apr 22 09:54:36 2025
    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.

    [...]

    However I see that nobody in this group has any objective interest in language ergonomics and aesthetics.

    Nonsense.

    They more interested in defending 'their' language.

    Nonsense.

    Janis

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Janis Papanagnou@21:1/5 to Keith Thompson on Tue Apr 22 10:12:19 2025
    On 22.04.2025 00:39, Keith Thompson wrote:
    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

    This is the historic and standard form in all POSIX shells.

    and
    for (( EXPR1 ; EXPR2 ; EXPR3 )) [;] do COMMANDS ; done

    I'm referring to the second form, as I'm sure you know.

    Yes, of course.

    I see that ksh has something very similar.

    Ksh invented it, along with the "arithmetic command" ((...)), as
    addition to the (standard) arithmetic substitution $((...)) .

    I have no idea which shell
    added the second form earlier, but both are clearly derived from C,
    directly or indirectly.

    Yes. - In the sense of having this three-part separation.

    [...]

    [...]
    [...] 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

    Indeed. Because of the three-part separation of building blocks
    you can do similar things as in "C" (modulo FP in Bash).

    It's something you cannot do, e.g., with Pascal- or "bart-loops".

    Janis

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Janis Papanagnou@21:1/5 to bart on Tue Apr 22 09:32:32 2025
    On 21.04.2025 01:29, bart wrote:
    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

    (There's obviously a lot you haven't seen.)

    (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:

    You still have the controlling elements in one place with 'for'.

    [...]

    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.

    Yes, that's a commonplace. (But "one place" doesn't mean "stuffed" or
    "all in one line".)

    However, I have
    seen actual examples like that, in for-headers that that use
    comma-separated expressions.

    (You can see and find arbitrary code, better or worse.)



    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.

    Yes, and that's his point; it's not "C".

    If I want abstractions I don't (unless I have to) use "C". If I want to
    have clean concepts, say iterating over an indexed array, I can use in
    "C"

    for (i=0; i<N; i++)
    arr[i] = fun (arr[i]);

    which is clean enough for me. But I might prefer (with other languages)
    to not care about the bounds in the first place, and use

    for (i := lwb(arr) to upb(arr)) # not "C"
    arr[i] := fun (arr[i]);

    if I'd want to avoid errors with the bounds. But the real thing I'd want
    is a functional approach where I don't need indices at all

    iterate (arr, fun); # not "C"


    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!)

    There's a lot I'd like to have that I don't find in the "C" language.

    If I choose to use (or have to use) "C" I use the options I have to best
    solve my tasks.

    [...]

    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!

    No one said that. - You should stop your regular misrepresentations and
    also your misinterpretations! - STOP IT, stupid!

    Janis

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Janis Papanagnou@21:1/5 to Scott Lurndal on Tue Apr 22 10:32:20 2025
    On 22.04.2025 01:46, Scott Lurndal wrote:
    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.

    (Again exaggerating and imputations. *sigh* - Abandon all hope, ye
    who enter here.)

    Obviously the entire world-wide programming community
    disagrees with your opinions.

    Well, I certainly disagree with the (so well known) exaggerations of
    bart, but I have a similar principle here; I avoid macros if I can.
    I like languages where every construct is under "direct control" of
    the compiler with a single abstraction principle (as opposed to
    two-step interpretations). It's all not worth a discussion, I think,
    and I certainly see and acknowledge "valid" applications of macros.
    Just saying that I understand his reluctance here.

    Janis

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Janis Papanagnou@21:1/5 to bart on Tue Apr 22 10:19:15 2025
    On 22.04.2025 00:54, bart wrote:
    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?

    No one suggested (or forces you) to do that.

    All within the same parentheses?

    It makes sense in programming languages to have a syntactic frame
    for things that belong together.

    Putting multiple things on the same line is usually frowned upon.

    There's of course exceptions but basically yes.

    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.

    Janis

    [...]

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Janis Papanagnou@21:1/5 to Waldek Hebisch on Tue Apr 22 10:47:25 2025
    On 22.04.2025 03:06, Waldek Hebisch wrote:
    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.

    I've been already in the 1990's. (A huge years-long reorganization
    project of a telecommunication company. - A lot of companies and
    people were involved. It was, at least on our part, a C++ project.)

    Janis

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Janis Papanagnou@21:1/5 to bart on Tue Apr 22 10:40:55 2025
    On 22.04.2025 02:26, bart wrote:
    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.

    You have no fantasy about sensible code patterns but when it goes
    to imputations and misrepresentations your fantasy is limitless.


    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.

    Take your meds and recognize that the world is different than how
    your fantasy deludes you.

    And stop making such nonsensical claims like "nearly always" without
    any evidence.

    Janis

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Waldek Hebisch on Tue Apr 22 11:28:56 2025
    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.


    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))))
    ?((n2)=((lua_Number)(((((v2)->value_).i)))),1):0))){pc++;{TValue*io=((&(ra)->val));((io)->value_).n=(((n1)+(n2)));((io)->tt_=(((3)|((1)<<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?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Keith Thompson on Tue Apr 22 11:43:09 2025
    On 22/04/2025 02:22, Keith Thompson wrote:
    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:
    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?

    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?

    At least the keyword would now be 'appropriate'.

    But that doesn't answer my question. You'd prefer "while", but would
    using "while" rather than "for" resolve your objections?

    If a separate 'for' didn't exist, then there'd be mostly the same
    problems, but it would be slightly less annoying: at least there is no
    pretence that linked-list traversal is anything to with iterating from A
    to B.

    But ideally 'for' would exist and be used only for simple iterations,
    even if it was the same for(a;b;c) style as now. Then when you see
    'for....', you will know instantly what you're looking at.

    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.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Janis Papanagnou on Tue Apr 22 12:39:30 2025
    On 22/04/2025 09:19, Janis Papanagnou wrote:
    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.

    They were real examples. The first from SQLite3, the next two from LIBJPEG.

    But thanks anyway, you've just agreed that those are ludicrous.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Kaz Kylheku on Tue Apr 22 12:33:14 2025
    On 22/04/2025 04:31, Kaz Kylheku wrote:
    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.

    I don't actually write much raw C. When I generate C, especially if I
    want someone else to compile the code, then it needs to be conservative.

    But most of my complaints have been do with other people's code. They're
    the ones that need to use those features!

    In any case, for-loops account for 1% of what I find painful about C.

    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.


    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.

    Most of my replies haven't been about the feature itself: it's about
    people's attitudes to it, defending appalling coding styles, pretending
    the primitive C feature is superior to anything else, and generally
    refuting every point I try to make.

    And when they run out of arguments, they start personal insults.

    No; it just has to be defined at the top of a file which needs it.

    Either it's in the language or it isn't. It is unacceptable to me to
    require headers for fundamental language features.

    All of a sudden, something has to be in a document you can't be
    bothered to read in order to be useful?

    Do you think I haven't played around with such macros? Here's some from
    2014:

    #define PUTS puts
    #define PRINTF printf
    #define TO(i,x) for (i=x; i; --i)
    #define FOR(i,a,b) for (i=a; i<=b; ++i)
    #define FORD(i,a,b) for (i=a; i>=b; --i)
    #define UPB(a) (sizeof(a)/sizeof(a[0]))
    #define UPB0(a) (sizeof(a)/sizeof(a[0])-1)
    #define LEN(a) (sizeof(a)/sizeof(a[0]))

    But if writing a code fragment to post in a forum, for example, you
    can't use any of this stuff. People arren't going to download some
    separate header for these macros, and I don't want them plastered all
    over my example. It would also be silly ate to use that FOR macro for
    that /one/ for-loop in my code.

    This stuff does need to be standardised to be useful.

    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. Example:

    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.


    I think C's macros are the main thing that have hindered its development.

    C macros definitely hinder all kinds of tooling.

    Just C syntax does that by itself! My editor uses Ctrl-PageUp/Down to
    step between functions. But it doesn't work for C source code: you need
    half a C compiler to figure out where each function starts.

    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.

    After nearly 50 years of using alternative languages? Probably not. By
    the time it became standard, I'd be dead.

    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?

    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?

    However those undergraduates probably moved on to use a mainstream
    language, while I've successfully used mine to do 99% of my coding over decades.

    (Actually, I've looked at countless amateur language projects over on
    Reddit. Most of them would be hopeless as systems languages. And half of
    them look like C!)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to bart on Tue Apr 22 14:36:52 2025
    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.

    In a Lisp system, all standard functions, variables, macros and
    everything else are present in the image and can just be used.

    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?

    The most actively developed dialect of C is C++.

    C is kind of a still backwaters in comparison.

    C++ has acquired all sorts of features and continues to do so.
    Including in the area of iteration.

    This is valid C++, as of C++11, which was 14 years ago:

    int a[] = {0, 1, 2, 3, 4, 5};

    for (int n : a) {
    // ...
    }

    C tends not to take on this kind of stuff.

    But remembrer, you don't like C++ because it's big and full of cruft
    that would take you years to understand, not to mention me.

    _ You don't like gatekeeping against features, like in the case of C standardization.

    - You don't like more open gate, like C++, which leads to a Borg-like assimilation of cruft over years and decades.

    You want the gate to be open only for you, where you get to decide on
    some dozen features (or what have you) which are then frozen, so that
    nobody else gets to add anything that you would have to learn.

    It's as if you should be developing your own language and not bothering
    wth any other, and perhaps using C as a code generation back end,
    whereby you are grateful that at least it's not such a moving target, in
    spite of its warts.

    I'm the same way! I'm against the floodgates being open, yet I don't
    absolutely zero progress either, like Common Lisp (ratified by ANSI
    in 1994, and nothing since then). Make your own Lisp; then you can
    experiment, without other cooks spoiling the sauce.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to Kaz Kylheku on Tue Apr 22 16:10:31 2025
    Kaz Kylheku <643-408-1753@kylheku.com> writes:
    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.

    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.

    Offering suggestions, rather than chronic complaining would have
    been a better track, but still wouldn't have likely resulted in
    changes that would satisfy bart.


    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.

    Which precludes user overrides of the standard library at compile
    time or run-time.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to bart on Tue Apr 22 17:53:20 2025
    On 22/04/2025 02:12, bart wrote:
    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.

    It is very rare that rants, exaggerations, and highly subjective
    opinions change anyone's opinions. You have presented no evidence or
    rational argument for your own viewpoint - merely an opinion that you
    don't like C's for-loop syntax.

    It is not news to any C programmer that C's for-loop syntax is different
    to that of a number of other languages - and that like a lot of C
    syntax, its flexibility has its advantages and disadvantages.

    But if you want to show that C's for-loop syntax is /bad/, not just say
    that you don't like it (which is a subjective opinion, and as such you
    are free to hold whatever views you like), you have to demonstrate that
    there are clear and significant issues with it. You have to show that
    it is a problem for C programmers. It is not enough to show that it
    takes more keystrokes or tokens than some alternatives, or that there
    are people other than yourself who dislike it. It is not enough to show
    that some C programmers make macros for their own convenience, or that
    most for-loops in C are simple counters that have a shorter syntax in
    many languages.

    If you want to demonstrate that C's for-loop syntax is objectively a bad design, you need to show that it is a source of real-world errors or
    real-world lack of productivity. That means you need to point to errors
    in the way for-loops are written being behind a statistically
    significant fraction of CVE entries, or research papers or surveys
    showing that it is something people regularly get wrong in C
    programming. Or you can point to studies showing that using a language
    similar to C, but with a different loop syntax, is measurably more
    productive. (Ironically for you, the nearest here would be the C++
    syntax for for-loops over containers.)

    I don't believe you will be able to provide any such reference. If you
    could, then I am sure at least some people here would accept that C's for-loops are a dangerous and therefore poor choice of syntax - though
    personal preference is still personal preference.

    Until then, your posts are as convincing as someone claiming strawberry
    is a terrible flavour for ice cream.

    Had you picked another aspect of C, such as the ease of writing code
    with buffer overflows, you could provide objective proof that the C way
    of handling arrays and strings is a lot more dangerous than modern C++,
    Python, or even old BASIC in that respect.



    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.

    I don't see anyone disagreeing with you about the claim that no one has
    changed their opinion as a result of your rants. But I regularly see
    you making claims about what other people have said or meant, without justification.


    (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.)



    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++.

    C++ has, as noted above, an alternative for-loop syntax that is a lot
    simpler for some uses - "for (auto x : vec) { ... }" iterates over a
    vector, C++ array, or other container.


    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.


    You can, if fact, simply add such a macro if that is something you want
    to do. If you find it useful, use it. That is part of the beauty of
    /not/ putting this kind of thing into the core language - it is
    completely optional, and people can make their own choices.

    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.


    I think you are wrong.

    And I think you haven't the slightest shred of evidence to support your opinion. I, on the other hand, have at least some justification - the C standards committee have written that they intentionally change the C
    language slowly, valuing stability of the language and compatibility
    with existing code over new features and language changes.

    Why add a new core feature, when some tacky, half-assed macro can be
    used? Or, sometimes, a typedef.


    A good guideline for language design is that you /never/ change core
    features if you can implement the feature you want as part of the
    language library. For established languages, the main motivation for
    changing the language is to provide better support for features in the
    standard library - to make them more efficient, or significantly easier
    to use.

    You live in a very different world from serious, mainstream languages, libraries and programming. In your world, it's as easy to add a
    function to the core language as it is to add it to the user application
    code, so you have a very different balance. Other language designers,
    language implementers, and language users do not work that way.

    At one time, every other app I looked at defined its own ARRAYLEN macro,
    or MIN/MAX, or STREQ.


    I guess I am the exception - I've never needed any of these. But for
    your information, C23 has a _Lengthof operator as well as features
    making it easier to make a better min or max macro, and C++ has all of
    these features defined in its standard library. I assume, however, that
    you feel C++ is evil for implementing min and max as library template
    functions that are easy to use and do not conflict with programmer's
    choices to use those same identifiers themselves. You would rather fill
    the core language with an increasingly large number of junk functions
    that cause more and more compatibility issues with user code.

    There is no need to add a formal lengthof() operator when every user can create their own variation of ARRAYLEN in a few seconds.


    Correct. And most people don't create their own ARRAYLEN - why do so
    when you can write the same expression clearly and simply in a few
    seconds when you need it? And why even do that, when you almost always
    know the length of the array you are dealing with in the first place?

    Result: most people still end up writing
    sizeof(myarray)/sizeof(myarray[0]).

    I think most people write "n", or "buffer_size", or whatever expression
    they already have for the array in question.

    In the meantime, I'm been able to
    write myarray.len for 43 years, and also:

       print a, b       # who cares about format codes

    Convenient print formatting is one area where I agree C is poor.


    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


    I personally don't see "max" or "min" operations coming up very often in
    my code. And when I do, it is often not by itself :

    if (x[i] > max) {
    max = x[i];
    max_index = i;
    show_new_max_value(max);
    }

    A built-in "max" function or operator would mean I needed a different identifier, and it would not be of any use in such code anyway.

    It is, at most, a trivial thing.

    Oh, and I already have Unless:

       unless a then b end
       return 0 unless cond

    It is not a macro.

    It is not good either - IMHO. You end up with time flowing forwards in
    some bits of your code, and backwards in other bits, which makes it significantly harder to follow.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Scott Lurndal on Tue Apr 22 17:27:28 2025
    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.

    Or it can be left out and the library imported explicitly like this:

    import msyswin

    Only one such line is needed for everything, and it is only needed in
    the lead module of any project. But normally not that even that is required.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to bart on Tue Apr 22 17:48:17 2025
    bart <bc@freeuk.com> writes:
    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.

    I find it useful that APIs are distributed amongst many, many
    header files. The windows "all-in-one" header is fundamentally
    broken, as would be any implementation that followed that paradigm.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to bart on Tue Apr 22 19:19:47 2025
    On 22/04/2025 12:28, bart wrote:
    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.


    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)))
    )?((n2)=((lua_Number)(((((v2)->value_).i)))),1):0))){pc++;{TValue*io=((&(ra)->val));((io)->value_).n=(((n1)+(n2)));((io)->tt_=(((3)|((1)<<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?



    I don't see any serious problem with this. It's not the way /I/ would
    code things, but I have different aims for my code than the Lua folks
    have for theirs. I don't know what all these macros do, or what all the abbreviations are - but that's because I don't know anything about the internals of the Lua byte-code interpreter and virtual machine.

    What I /do/ see here is that everything is built up with small parts
    combined and layered - that's how you do structured programming. It's a
    bit excessive IMHO in how much it is split up (perhaps the programmers
    are used to Forth), but that's a matter of taste.

    It doesn't matter that it is macros rather than functions - except that
    macros will probably give more efficient end results than functions
    would on weaker compilers that can't handle inlining well. (The
    compiler still needs to do good expression optimisation for efficient
    results.) Lua is intended to be portable to a wide range of different compilers of different capabilities.

    The Lua programmers never intend humans to read the expansion of the
    macros - that is completely unnecessary. Get each macro right
    individually, and the final result is correct - again, that's the way structured programming is done.

    The cryptic names do mean it would take more effort to learn to
    understand what's going on here for people involved in maintaining the
    code, but that only applies to a very few people. It doesn't even apply
    to most C compiler implementers - they just need to read the C standards
    and write a compiler that follows the C rules, and it should have no
    problem compiling the Lua virtual machine.


    Instead of thinking of this as some kind of abuse of C macros or incomprehensible expanded expressions, consider it more as a
    domain-specific language written in C macros with the intention of
    making it easier to write the higher level parts of the Lua virtual
    machine. You are not expected to be able to read the expanded C code
    any more than the average C programmer is expected to be able to read generating binary images.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to David Brown on Tue Apr 22 19:54:01 2025
    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)

    We /are/ still about expressing a loop in C, yes? At least mine directly translates to C:

    x = first_ingredient(); // C needs some state
    while (x != empty) {
    put_ingredient();
    x = next_ingredient();
    }

    I'd be interested in how your 'Put in the bowl each of the ingredients' translates into:

    FOR(W; X; Y) {Z;}

    There is also nothing about this syntax that directly indicates the
    middle term X is the terminating condition; it is just expression #2 of 3.

    It doesn't read as English; not in this form. Whereas FOR I IN A TO B is already most of the way there, as is WHILE (CONDITION).

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Janis Papanagnou on Tue Apr 22 20:16:13 2025
    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.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to bart on Tue Apr 22 21:11:54 2025
    On 22/04/2025 20:54, bart wrote:
    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)

    The point was that you claimed your loop matches how you would describe
    things in English. That was nonsense. Your loop - whether it is in
    your language or your preference for C loops - is no closer to a normal
    English language description than C's for-loops are.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Kaz Kylheku on Tue Apr 22 21:03:21 2025
    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.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to David Brown on Tue Apr 22 20:43:45 2025
    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.

    This:
    a=b;
    while (c) {
    d;
    e;
    }

    can alway be directly translated to English, a line at a time:

    Assign b to a; While c: (Do d, Do e)

    The only liberty taken is turning ") {}" into ": ()".

    Now try that with the equivalent "for (a=b; c; e;) {d;}. There is no
    direct mapping.

    But if you want do it your way, what algorithm ss being expressed here:

    for(a; b; c) {d}

    You can't do it; there isn't enough information, or you'd have to
    analyse it more deeply. However you CAN describe the outline of what is
    being executed, and you CAN port that to a different language, just about.

    Here it is in Python (it may need the details tweaked):

    a
    while b:
    d
    c

    And guess what - it's a WHILE loop!

    Whichever way you want to slice it, FOR(A;B;C) is a glorified WHILE
    loop. And a lot of the time it is much better suited as such.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to bart on Tue Apr 22 19:26:19 2025
    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.

    I've only defined cast macros for the purpose of switching between
    C casts and C++ casts. If that's not going on here, it's just
    gratuitous macrology.

    The switching I'm talking about is this:

    #ifdef __cplusplus
    #define strip_qual(TYPE, EXPR) (const_cast<TYPE>(EXPR))
    #define convert(TYPE, EXPR) (static_cast<TYPE>(EXPR))
    #define coerce(TYPE, EXPR) (reinterpret_cast<TYPE>(EXPR))
    #else
    #define strip_qual(TYPE, EXPR) ((TYPE) (EXPR))
    #define convert(TYPE, EXPR) ((TYPE) (EXPR))
    #define coerce(TYPE, EXPR) ((TYPE) (EXPR))
    #endif

    The purpose is to gain access to the finer-grained C++ casts when
    the code is compiled as C++, but fall back on the C casts when it
    is compiled as C. These macros *do* something, so they are needed.

    I don't want to have #ifdef __cplusplus all over the place and write
    the same expressions twice with different casts.

    So what language would say this program is written in?

    Obviously C. C has preprocessing and so, the above is what C sometimes
    looks like.

    It's obviously not written in the C-without-preprocessing subset of C.

    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.

    It allows the code to be built up in small chunks that are easy
    to understand (when looking at the macro definitions).

    If all else is equal, such as the sizes of the code mountains, one layer
    of macro to generate the mountain of code is almost always going to be objectively worse than a macro defined in several hierarchical levels
    that offer some meaningful "chunking" to someone reading the code.

    The definition will likely be more condensed, due to certain lower level
    macros are used more than once in the higher level ones.

    Do you think that's too many? Perhaps only 3 levels would have sufficed.

    Low-value macros that only add levels of expansion without payoff should
    be avoided. If there are 7 levels, but 4 of them are some low-value
    syntactic sugar, a case can be made for reducing.

    If we go by the chunking concept in cognitive psychology, that people
    do well with an abstraction layer that has about 7 chunks of content,
    in 7 layers of 7 chunks, we get a hierarchy with 800,000 items at the
    leaf level, which is an improbable morass.

    In 7 layers of macros, some of the layers are going to have low
    branching factors, just providing some minor notations, sometimes
    of little value.

    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.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to bart on Tue Apr 22 21:40:28 2025
    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 think some other code shoud be written or generated, and not
    the code produced by those macros, or equivalent to it, then that
    doesn't speak to those macros.

    It looks like that Lua VM is doing way too much work just to dispatch
    an opcode, but if we take that crud as a given, the layers of macros may
    be a bad way to obtain it, out of all the alternatives. A somewhat
    cleaned up version of those macros would likely be better.

    I don't think Lua is very good; I would never choose it for anything, or basically go anywhere near it. I'm not even slightly surprised the code
    is grotty.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to Keith Thompson on Tue Apr 22 22:39:25 2025
    Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
    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++).

    Yes, that's true. It's more a logical extension, however, than a change IMO. Your point is taken.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Keith Thompson on Tue Apr 22 23:52:05 2025
    On 22/04/2025 22:15, Keith Thompson wrote:
    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.


    I think my comments above stand.

    Of course, everybody here (me included) will claim their own code is
    cleanly laid out; it's everybody else's that is the problem.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Kaz Kylheku on Wed Apr 23 00:43:23 2025
    On 22/04/2025 22:40, Kaz Kylheku wrote:
    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?

    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.

    It makes debugging, porting, or anything requiring deep understanding, a nightmare.

    This project uses approx 500 macros that look like functions.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to bart on Wed Apr 23 00:01:09 2025
    On 2025-04-22, bart <bc@freeuk.com> wrote:
    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?

    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.

    The fact that Lua has some grotty macros in its VM doesn't speak
    to a nice use of macros, like what I was talking about.

    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.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Kaz Kylheku on Wed Apr 23 09:01:20 2025
    On 22/04/2025 21:26, Kaz Kylheku wrote:
    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.


    That's a good way to describe it.

    The generated codee is large and complex, but at least you can see it.

    Yes, but that is only needed by the people writing and debugging the
    macros involved.


    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.

    It is certainly split up more than I would have written, but there are
    perhaps good reasons for the style. I haven't seen any more of the code
    than what Bart has quoted here - perhaps there are other situations in
    which "cast_int" is being passed as a parameter in another macro. Or
    maybe there are historical reasons - "cast_int" came first, then there
    were other "cast_X" macros added later, then a generic "cast" macro and "cast_int" was redefined in terms of the generic one. Maybe there are development or test versions of some of these macros where the plain
    casts are replaced by runtime checks before the conversion, and what we
    see here is just the release versions without the checks.

    Or maybe they were just written by someone who likes to divide things up
    a bit more, or they were mixed from different sources or authors with
    minimal changes (that might explain the different capitalisation styles).

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Kaz Kylheku on Wed Apr 23 11:15:01 2025
    On 23/04/2025 01:01, Kaz Kylheku wrote:
    On 2025-04-22, bart <bc@freeuk.com> wrote:
    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
    to a nice use of macros, like what I was talking about.

    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.

    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.

    * It is a language within a language: a quite different one that can be
    very challenging to code in, and even more challenging trying to
    understand or debug it

    * They make hard to port software or to convert APIs if they are full of
    C macros (GTK2 headers use 4000 macros)

    * Compiler error messages can get weird and confusing

    * In C, macros can generate random, unbalanced bits of syntax

    * They cause problems for tools such as smart editors

    * Macros don't obey normal scope rules.

    * Macros tend to get abused or over-used, and people try to be too clever

    * 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!

    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.

    What's the problem with using a function here?

    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.

    Most languages don't have C-style macros; how do they manage?

    Some may not have macros at all; I think D, Java, Zig, Go.

    I admit there are some use-cases that cannot easily be achieved by other
    means. For that reason I experimented with macros in my own two
    languages a few years ago. But those have a much smaller scope than C
    macros, while also being more integrated:

    * A macro, which can take parameters, can only be an alias for an
    expression term, not random syntax

    * Macro expansion takes place at a late stage, so it cannot define new
    symbol names

    * Macro expansion only occurs for top-level names, so the M in M.A.B is expanded, but not in A.M.B.

    * Macro names have normal scope rules

    * Macro names can be imported and exported like any other named entity

    Even with such a limited scheme, it can cover your examples, except for abstracting loops. But, given that the loop body in this case is only a
    single function call, that can easily be handled by a function.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Muttley@DastardlyHQ.org@21:1/5 to All on Wed Apr 23 10:58:12 2025
    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.

    * Compiler error messages can get weird and confusing

    If you think C errors are confusing try C++ with template errors.

    * They cause problems for tools such as smart editors

    Any "smart" editor that can't cope with macros isn't smart.

    * Macros don't obey normal scope rules.

    They're not supposed to.

    * Macro solutions tend to be ugly and tacky (see X-macros)

    So can any code.

    * Macro expansion can do too much:

    #define length 200
    #define width 100

    struct {char* str; int length;} s;
    s.length = 0; // oops!

    So what? You'd get a compilation error.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Muttley@DastardlyHQ.org on Wed Apr 23 14:50:22 2025
    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.

    (Let me guess again: that's still fine!)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to bart on Wed Apr 23 17:41:39 2025
    On 22/04/2025 21:43, bart wrote:
    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.


    I think it was Djikstra who said that one of the most important skills
    for a programmer is a mastery of your mother tongue. You seem to be
    failing here.

    No one is doubting your ability to translate some code directly into
    English words. What you are failing to do, however, is write a
    sensible, normal English language description of the task for the code,
    and then translate that into code. You can't expect people to take you seriously when you say a C for-loop does not match a normal English
    description when your own looping alternatives are equally bad matches.

    Let me put it simply for you:

    It is true that no one uses C for-loops in normal English.

    It is equally true that no one uses while-loops in normal English.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to bart on Wed Apr 23 17:31:29 2025
    On 22/04/2025 22:03, bart 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.

    14 levels of macros? No problem; I'm sure there was a good reason!

    If there is good reason for 14 levels of macros, then fine.

    If there are not /good/ reasons for multiple levels, but the result is
    correct code that compiles efficiently (on reasonable compilers with
    reasonable options), then maybe it is appropriate to leave it untouched.

    Too few levels of functions and/or macros (there is no semantic
    difference between macros and functions in this matter) makes code hard
    to write and hard to get correct - you are doing too much at a time.
    Splitting things up - divide and conquer - is a big win. But too many
    levels also makes it hard to see what is going on and can be a
    maintenance challenge.

    So once you first have too many levels - for whatever reasons - and the
    code is correct and does the job, you have to decide whether or not to
    refactor into simpler, neater and clearer code. The result would be
    easier to maintain in the future, but it has a cost in time, effort and
    the risk of making mistakes.


    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. I'd want to look at the macro definitions, and how they are used in the code - the macros alone are
    far from enough. I'd want to know the history of the project. I'd want
    to know the target usage of the code. I'd want to see its
    documentation, its roadmap for the future, its history of bugs and other problems. I'd want to know more about the developers behind the code.
    I'd want to know if other people are expected to read the code, to work
    with it, to re-use it, or if they would simply see it as a finished
    product. I'd want to know if it is targeting a specific device, OS,
    processor, compiler, or if it is intended to be portable. I'd want to
    know how it is tested. I'd want to know if it is viewed as low-risk,
    low-cost software, or intended to be secure or highly reliable.

    Clearly, I haven't the slightest interest in going to that effort just
    to please you.

    So the best I can offer you is a very cursory glance at the code, and
    the opinion that - in the case of the Lua source code snippet - it's
    probably fine, but a bit more finely divided than I would like myself,
    though there may be good reasons behind that.

    I really can't see what any of that gives you.

    We all know that some people write bad code, or have written bad code.
    I don't think anyone will deny that. We also all have different
    thoughts and experiences on what makes code good or bad, or when bad
    code might be perfectly acceptable - all such judgements are very
    subjective, and therefore of little use here.

    And we all know this is entirely independent of programming language.


    There doesn't seem to be any limit - or not any that they want to admit
    to me.

    It is absolutely correct that there is no dividing line between "good
    code" and "bad code", or "use" and "abuse".



    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.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Muttley@DastardlyHQ.org@21:1/5 to All on Wed Apr 23 16:00:16 2025
    On Wed, 23 Apr 2025 14:50:22 +0100
    bart <bc@freeuk.com> wibbled:
    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.

    It needs something so why not use 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?

    No, have you? I've not built a smartphone either but I don't find them complex to use.

    * 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!)

    Why would you want scoping with macros FFS? They need to be consistent and available everywhere in a piece of source code.

    So what? You'd get a compilation error.

    If you're lucky. Or you get a silent, undetectable bug, since any

    No, not if you're lucky - you WILL get an error with that example. Why don't you try it instead of gassing.

    alphanumeric token: identifier, type, or reserved word, gets expanded,
    no matter what the context.

    Yes, thats whole the fucking point. If you don't want that don't use them!

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Keith Thompson on Wed Apr 23 19:05:01 2025
    On 23/04/2025 00:02, Keith Thompson wrote:
    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 had considered them as noticeably different, but I will of course
    accept your opinion here! To me, "cautiously accept" is a sceptical
    "yes" vote, while "would not object" is an "abstain".

    [...]

    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.


    Sorry, yes. The pdf I was viewing was the latest draft for post-C23
    rather than C23. My mistake.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Muttley@DastardlyHQ.org on Wed Apr 23 17:39:18 2025
    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.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to David Brown on Wed Apr 23 18:43:14 2025
    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.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to David Brown on Wed Apr 23 18:52:34 2025
    On 23/04/2025 18:05, David Brown wrote:
    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.


    Actually, my C compiler has 'lengthof'. I'd got rid of most experimental
    bits in the lastest version, but that got left in:

    #include <stdio.h>

    int main(void) {
    int A[100];

    printf("%zu\n", lengthof(A)); # shows 100
    }

    Looking at the sources, it is a sub-operator of 'sizeof' and its
    implementation took under 30 extra lines.

    It works for expressions and types, just like sizeof.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to bart on Wed Apr 23 18:43:33 2025
    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.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Kaz Kylheku on Wed Apr 23 21:49:03 2025
    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.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Muttley@DastardlyHQ.org@21:1/5 to All on Thu Apr 24 07:40:52 2025
    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.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Muttley@DastardlyHQ.org@21:1/5 to All on Thu Apr 24 07:41:25 2025
    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. No doubt no one has ever seen it except you.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Muttley@DastardlyHQ.org on Thu Apr 24 09:31:21 2025
    On 24/04/2025 08:41, Muttley@DastardlyHQ.org wrote:
    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.


    Oh, thank you, you're so kind.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Muttley@DastardlyHQ.org on Thu Apr 24 09:26:23 2025
    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.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Muttley@DastardlyHQ.org@21:1/5 to All on Thu Apr 24 10:52:35 2025
    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!

    Functions return a value of some type;

    Not always. Heard of void?

    macros yield a bunch of lexical
    tokens.

    Not always. Eg if used in #ifdef.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Muttley@DastardlyHQ.org on Thu Apr 24 12:44:48 2025
    On 24/04/2025 11:52, Muttley@DastardlyHQ.org wrote:
    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!

    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.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to bart on Thu Apr 24 15:12:50 2025
    On 23/04/2025 22:49, bart wrote:
    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.

    He said he had /not/ seen things like your example. Your example is
    very different from what he described.

    Your example is unbalanced - most real-world macros which are not "brace complete" come in matched pairs. And passing an arbitrary token such as
    "[" as a macro parameter is extremely unusual.


    As an example of macros with more complex structure, look at the
    "protothreads" library: <https://dunkels.com/adam/pt/>



    #include "pt.h"

    struct pt pt;
    struct timer timer;

    PT_THREAD(example(struct pt *pt))
    {
    PT_BEGIN(pt);

    while(1) {
    if(initiate_io()) {
    timer_start(&timer);
    PT_WAIT_UNTIL(pt,
    io_completed() ||
    timer_expired(&timer));
    read_data();
    }
    }
    PT_END(pt);
    }



    The PT_BEGIN and PT_END macros each have unbalanced braces, but they
    balance when used correctly in pairs as shown above :

    #define PT_BEGIN(pt) { char PT_YIELD_FLAG = 1; LC_RESUME((pt)->lc)

    #define PT_END(pt) LC_END((pt)->lc); PT_YIELD_FLAG = 0; \
    PT_INIT(pt); return PT_ENDED; }



    There are entire libraries built
    solely with the C processor, some even implement languages; they will
    use every trick they can.

    Would you think it was better if they were bigger, slower, or less
    functional because they did /not/ use every trick?

    You seem to have this bizarre paranoia about macros, as though they are
    only used specifically to cause you personal grief. In reality, there
    are perhaps three main good reasons macros are used in C coding :

    1. When they are the best, or perhaps only, way to achieve the goal for
    the code.

    2. When they make code clearer, neater and more maintainable.

    3. When the code is written for older C standards or older or weaker
    compilers and can't make use of safer alternatives (such as inline
    functions, static const's, constexpr values, etc.)

    They are also often used by programmer habit when they are not
    necessary, but that is rarely harmful (such as using #define'd constants
    when an enum constant might be considered better).


    So if someone builds an entire library from macros, it is because macros
    are the best or only way to build a library with that kind of
    functionality. Protothreads is a fine example of this. That is a
    /good/ thing, not a bad thing.


    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


    Why do you think this is a bad thing? This is one of the points of C
    macros - they are text substitution macros, precisely so that you can do
    things like that.



    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.

    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. In particular, moving to C++
    rather than C gives you templates and compile-time execution that
    greatly reduce the need for macros, and provide a safer solution that is
    more flexible in many ways (but less flexible in others).

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to bart on Thu Apr 24 14:25:38 2025
    On 23/04/2025 19:43, bart 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, [) ];


    I know perfectly well that there are differences between what you can do
    with macros in C, and what you can do with functions.

    In terms of dividing up code into manageable parts that fit together as
    a whole - and in doing so too much or too little - there is no
    conceptual difference between macros and functions.


    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

    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 (; f>=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.



      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?

    You want me to suppose that I know for sure that the code is more
    complex than it needs to be, and then you are asking me if I'll say it
    is more complex than it needs to be? Yes, under those circumstances I'd
    say it was more complex than it needs to be.

    Would I be tolerant of it? That depends on why it is so complex, what
    the consequences of that complexity are, and the consequences of
    refactoring it to reduce the complexity. For single-use code that is
    unlikely to ever need to be changed or fixed, and which results in
    object code that is efficient enough for the task, it is not going to be
    worth the effort or risk to change. If the code is expected to be
    maintained over a long time period, re-used in other projects, or if
    many people need to understand it, then the balance could be very different.


    Your questions here are like presenting someone with a picture and
    asking if it is good or not, without saying whether it was made by your six-year-old grandkid or hanging in an art gallery.


    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'd expect most experienced C programmers to be able to answer that.
    I'd certainly expect anyone who claimed to have implemented a C
    preprocessor to be able to answer it. No, C does not have recursive
    macros (or any other kind of pre-processing loop), which is a
    significant limitation for some uses.

    There are some limits for aspects of C given in the "translation limits" section of the standards, such as 4095 macro identifiers in a
    translation unit, which are minimum limits for conforming compilers. In practice, pretty much all C compilers have no caps or limits on these
    things other than the memory (or other limits) of the host computer.

    If a language or tool supports recursion or loops during translation,
    then I agree a cap of some sort is a good idea. Alternatively there
    could be other detection of infinite loops, or a timeout. In C
    preprocessing, you can have recursion if a file includes itself
    (directly or indirectly), so a limit on file inclusion nesting makes
    sense. (For reference, gcc has 200 by default, but it is changeable by
    a command-line flag. The C standard requires a minimum of 15 levels.)


    (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.


    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, anyone else using macros for an interpreter
    (no matter how similar or different) must be doing something wrong.

    (Again, I have no idea whether or not these macros are a good idea in
    the Lua source code - I am merely saying that your opinion here is
    highly subjective and is not based on any kind of objective rationale or
    full knowledge of the situation.)



    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.

    I know you have written interpreters for your own languages. I know you
    say they are fantastic and better than anything else in existence -
    since no one else has ever used them, we have only your word for that,
    but I'll assume it is true.

    You /know/ I know that.

    And yet I still asked you how you thought you were in a position to
    judge the way the Lua developers implemented Lua in such a categorical
    manner.

    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. Your experience tells you
    about your own experience - it tells you nothing about anyone else's experiences.



    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.



    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to David Brown on Thu Apr 24 14:51:12 2025
    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?

    As it happens, my main scripting language is bigger and richer than Lua.
    And yet, my implementation is somewhat smaller, and most programs run
    faster. It's a paradox.

    Here:

    c:\cx>bcc -i lua hello.lua
    Compiling lua.c to lua.(int)
    Hello from Lua

    This is my C interpreter, interpreting lua.c (a one-file version), which
    then runs hello.c.

    Further, this is part of a survey of interpreters running a recursive
    Fibonacci benchmark:

    Lang Implem Type Category Millions of Calls/second

    Bash Bash ? Int 0.0014
    C Pico C S Int 0.7
    Seed7 s7 S Int 3.5
    Algol68 A68G S Int 5
    Python CPython 3.14 D Int 11
    Lox Clox D Int 17
    Lua Lua 5.4 D Int 22
    Q DD D Int 73

    PyPy PyPy 7.3.19 D JIT 128
    JavaScript NodeJS D JIT 250 (See Note 2)

    My product is Q/DD. It has the best performance of the pure
    interpreters; beyond that you need to employ JIT techniques.

    The 'Note 2' mentioned is to do with NodeJS having a 0.5 second startup
    latency which had to be compensated for.

    So I know fuck-all about writing interpreters, they are all toys, my
    languages are toys, and my opinion counts for nothing?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Muttley@DastardlyHQ.org@21:1/5 to All on Thu Apr 24 14:31:54 2025
    On Thu, 24 Apr 2025 12:44:48 +0100
    bart <bc@freeuk.com> wibbled:
    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)

    Depends on the "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.

    C functions are not pure functions, they're procedures that can return values.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to David Brown on Thu Apr 24 15:28:57 2025
    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.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to bart on Thu Apr 24 15:32:51 2025
    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?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to bart on Thu Apr 24 17:21:33 2025
    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.

    The C preprocessor directives are part of C, are described in the C
    standards, and are covered by all C tutorial books, courses, and similar material. They take about 20 pages of the C standard of 700 pages (C11)
    - it is not hard to get the hang of typical use of macros in C.

    They do not form a "language". You get text-based macro substitution
    (without the possibility of recursion), conditional compilation, file inclusion, and error directives. They are integral to C, and usually
    integral to C compilers.

    Yes, there are some things that can be a bit fiddly - sometimes you need
    to have an extra layer of macro expansion for complicated things. Most
    people don't need to do that sort of code, and if they do, you can
    figure it out with a bit of trial and error and some googling.


    (But if someone is up to it, perhaps they can write 'make' in it!)


    The C preprocessor directives are not a programming language.

         + 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.


    I can't figure out what you are trying to say here. But it's probably
    not worth going into detail.

         + 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.

    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 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.

    These are macros for defining some types and macros for accessing those
    types with extra compile-time checks. You can mix them freely with
    normal C code. You don't have to use the macros for accessing the data
    - it's your choice. And you can mix it all with other C code.


    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.

    I also think writing identifiers and comments in Greek is incredibly
    confusing to someone who only reads English, but that does not mean
    Greek speakers should not be allowed to write code in Greek!


         - 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.

    You need to document the use of the macros you write if you make macros
    - you need to document an entire language if you go that route.


         - 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.


    No, it is not.

         - 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.

    You suggested it would be a better idea than writing some C macros.

    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."


    You do realise that Brainfuck is an intentionally silly language, made
    only for the fun and challenge of it? And a C pre-processor
    implementation is also intentionally silly, for fun and the challenge?

    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.

    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.



    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Janis Papanagnou@21:1/5 to David Brown on Thu Apr 24 18:51:51 2025
    On 24.04.2025 14:25, David Brown wrote:
    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.

    Right. (I see that bart still does this stupid argument trick.)

    Here's one of the functions from it:

    But I spent a *few seconds* to simply _reformat_ that code (in my
    brain[*]) and it seems quite obvious!

    [*] Below you find the brain dump codified for the post...


    lval rest(lval *h, lval *g) { lval *f = h-1; lval r = 0; for (;
    =g; f--)
    r = cons(h, *f, r); return r; }

    lval rest (lval *h, lval *g)
    {
    lval r = 0;
    for (lval *f = h-1; f>=g; f--)
    r = cons(h, *f, r);
    return r;
    }

    I just swapped the two [independent] assignments (r and f) so I got
    the "complete" 'for' loop which is even of the simple type (the one
    that also bart may understand), with just one running parameter 'f'.

    They're obviously constructing in 'r' the return value by iterating
    from the right to left. (The functional effect of course depends on
    cons() that is undocumented here, but r=cons(..., r) is exposing.)


    Presumably that makes sense to the people who wrote the code. But it
    makes no sense to me.

    (It's obviously much simpler than it was in its unformatted state.)

    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.

    And of course you're right.


    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

    In my book this non-abstracted form is harder to understand than the
    simple reformatted 'for' loop version. (and if we'd replace the 'while'
    by a 'goto' based construct it would get yet more harder to identify
    the semantic building blocks. (But it's pointless to discuss that with
    you, bart; given your arguments yet I'm sure you don't grok that.)

    Yes, you might apply a C formatter too, and keep it in C, it won't fix
    that for-loop though.

    Yes, the 'for' loop wasn't sensibly used for that algorithm. But it
    was trivial to fix that. (And it's no argument against 'for', just an
    argument against bad programmers and badly written code, as David also
    said.)


    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.

    The (low-level) [imperative] _mechanics_ wasn't the crucial thing here
    for understanding the code.

    And if I were writing the same function myself, I'd use a while loop
    rather than a for loop.

    I wouldn't, as I demonstrated.

    I'd guess you also wouldn't have used 'while' if you'd also have spent
    the 5-10 seconds (that I had spent) to "analyze" the code beforehand.

    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.

    Janis

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Muttley@DastardlyHQ.org@21:1/5 to All on Thu Apr 24 18:03:46 2025
    On Thu, 24 Apr 2025 18:49:00 +0100
    bart <bc@freeuk.com> wibbled:
    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.

    Oh dear, look at those poor toys being flung out of the pram at supersonic speed.

    Get a grip you snowflake twat, its just a sodding newsgroup.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to David Brown on Thu Apr 24 18:30:27 2025
    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.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Muttley@DastardlyHQ.org@21:1/5 to All on Thu Apr 24 17:59:41 2025
    On Thu, 24 Apr 2025 18:30:27 +0100
    bart <bc@freeuk.com> wibbled:
    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

    s/ambitious/fucking stupid/g

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Scott Lurndal on Thu Apr 24 18:49:00 2025
    On 24/04/2025 16:32, Scott Lurndal wrote:
    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?

    OK, you're OK with everybody in this group constantly bullying,
    disrespecting, belittling and insulting me?

    I gave an opinion about X, and people immediately disagreed with me
    despite not being familiar with that field. They gave it thje benefit of
    a doubt, and suggested there were good reasons for it being as it is.

    So I was calling it: I'm interested in why they did that.

    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.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Rosario19@21:1/5 to Janis Papanagnou on Thu Apr 24 22:35:48 2025
    On Fri, 18 Apr 2025 16:57:24 +0200, Janis Papanagnou wrote:
    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.

    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...

    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

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to bart on Fri Apr 25 02:56:44 2025
    On 2025-04-24, bart <bc@freeuk.com> wrote:
    NOTHING I SAY IS EVER RIGHT.

    Well, /that/ isn't; you're blindly discounting all the times
    someone has agreed with you.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Janis Papanagnou@21:1/5 to All on Fri Apr 25 07:46:21 2025
    On 24.04.2025 22:35, Rosario19 wrote:
    [ about gotos ]

    for loop is easy because propose always the same type of loop, the initialization, the condition, the increment etc

    I wouldn't call it "the same type of loop", given that you can
    formulate different types of loops (increments, shifts, other
    operations, etc.). I think the "same type of loop _structure_"
    might describe it better.

    all ok but sometimes
    one need to exit the loop before the main condition of exit,

    The comparably rare cases for early exits can (in "C") be done
    also with 'break' statements.

    Other methods are a reconsideration whether a structural change
    of the code would be more natural; we often see code evolving
    and it's IME not always clear from the beginning whether a
    'for' or a 'while' or a 'do-while' (or something else) is best.
    If we're _designing_ the code from scratch [before coding] (as
    opposed to "evolutionary" writing the code) the "right" control
    constructs usually appear even more naturally.

    But given that there's various ways of developing code (beyond
    just writing it up as one thinks, as opposed to a design with
    various top-down methods) that will lead to different results.

    and go down or up in the code lines going out of that loop,

    I think there was a discussion some time ago about languages
    (or a discussion about the "C" language evolution?) that allow
    to exit loops, and the various allowed ways; one was (AFAIR)
    to allow [for reasons presented there] only forward exits, for
    example.

    Especially if you have backward 'goto's it's IMO worth to have
    a second look on your iteration construct.

    for these cases it is better for me, not use forloop but the right
    if()s goto labels...

    I'll not (re-)open the can of a fundamental discussion on that.

    Use what suits you, and what fits in your [professional/private]
    coding environment.

    Janis

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Rosario19@21:1/5 to Janis Papanagnou on Fri Apr 25 10:29:12 2025
    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'

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Muttley@DastardlyHQ.org@21:1/5 to All on Fri Apr 25 08:53:39 2025
    On Thu, 24 Apr 2025 18:26:16 -0700
    Keith Thompson <Keith.S.Thompson+u@gmail.com> wibbled:
    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.

    If its true and not just him grandstanding then he has bigger problems than having a meltdown on a newsgroup because people disagreed with him.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Rosario19@21:1/5 to Kaz Kylheku on Fri Apr 25 13:48:07 2025
    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

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Janis Papanagnou@21:1/5 to All on Fri Apr 25 14:22:25 2025
    On 25.04.2025 10:29, Rosario19 wrote:
    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'

    No.

    That's why loop _abstractions_ have been introduced in programming
    languages, to *not need* _low-level_ loop representations appear in
    high-level programming.

    (You seem to be arguing on assembler level on von Neumann machines,
    but that misses the point of a loop abstraction in loops constructs
    as you find them in form of for, while, repeat, until, etc. in the
    high-level languages. BTW, in your argumentation it would have been
    more precise to have said that there's at least one 'JMP' *and* at
    least one conditional jump, like 'JNZ', to implement loops on such
    a low machine model level.)

    Janis

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to bart on Fri Apr 25 15:19:12 2025
    On 24/04/2025 19:30, bart wrote:
    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?

    Perhaps we are talking at cross-purposes here.

    The C preprocessor supports a few directives - such as #define. It is
    not a language - it is part of standard C. It is not hard to learn or understand, though there are a few awkward issues. Of course it can be
    /used/ in ways that are hard to understand.

    You seem to be talking about some imaginary "language" made by using
    extensive C macros and compiling with a C compiler. I have never heard
    of such a thing, other than perhaps people doing things for fun or the challenge of it and the occasional person who wants to make C look more
    like Pascal by using "#define begin {" and "#define end }".

    Sometimes people use a bit more advanced macros for simplifying their
    code or avoiding duplication of code. That can be things like wrappers
    for easily defining tree-like structs with common "left" and "right"
    pointers, or for defining lists of rules with a common format, or using "x-macros" to let you generate a simple command-line interface where you
    can get the instruction parser, enumerations, help text, etc., generated
    from one list of commands instead of duplicating them. All these things
    are examples of good use of complicated macro setups. All of them can
    also be handled in alternative ways such as using other languages or
    external scripts for generating the C code, or simply duplicating the C
    code manually.


    And can you honestly say that writing those complicated collections of
    macros requires no extra skill?

    Writing more complicated code always requires more skill than writing
    less complicated code.


    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.)

    Please let me know if you are talking about writing macros as C
    programmers do regularly, or if you are talking about some very unusual
    usage of macro collections. And if it is the later, please say if you
    are talking about developing such "macro libraries", or /using/ such macros.



    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.

    Can you cite evidence of that? Exclude any broken or non-standard
    compilers, or ones that only implement a bit of C, or ones written by
    people who haven't bothered reading the relevant parts of the C
    standards. Also exclude any non-conformities around unrealistic
    situations (such as mixes of macro definitions across #include'd files).

    Now there is less variance, maybe because they're sharing the one implementation that got it just right.

    There are some C compilers that share front ends, but I expect that most
    will have their own pre-processors - either stand-alone, or, more often, integrated with the rest of the compiler.


    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.

    I happily agree that confusing code is confusing. That applies to
    confusing functions, confusing types, confusing macros, confusing
    comments, and anything else, in any language, using any feature. I
    don't agree that there is something special about macros in C that are confusing.


    Remember I claimed such things to be 'impractical, unwieldy and
    inefficient' -'usually'.

    If you are talking about C macros, which you regularly complain about,
    then you are wrong. (I say "wrong", rather than "I disagree with you",
    because macros are used so often by so many C programmers that it is
    obvious they are neither impractical, unwieldy, nor inefficient.)

    If you are talking about some hypothetical extra embedded language
    written in macros, then it's a non-starter - such things are not used in
    real code, except in very niche cases when they are used precisely
    because they are the most convenient, efficient and safe method in those
    niche situations.

    And if you are talking about something like that "datatype99" macro
    library, then your opinion is worthless unless you can show some
    real-world usage of it, compare it to real code in C doing a similar job without the macro library (or equivalent macros), and demonstrate that
    the "datatype99" version is clearly inferior.

    You are, of course, welcome to your opinions - but if you can't justify
    them or show that they are reasoned opinions, they are worthless.


    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."

    Your imagination is running wild again. Try reading what I wrote.

    I have not advocated for making any kind of "embedded language" in C, in
    any sense.


    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.

    No, you did not - in either case.


    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.




    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to All on Fri Apr 25 17:51:15 2025
    On 25/04/2025 13:48, Rosario19 wrote:
    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

    Not me - I'm never wrong.

    (Except perhaps in this post :-) )

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to bart on Fri Apr 25 17:48:58 2025
    On 24/04/2025 15:51, bart wrote:
    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?

    Why are you criticising them (the Lua authors) when you have no idea
    what is behind their code?

    I am not saying that you are wrong as such - maybe the Lua code /is/
    overly complicated, and uses more macros than makes sense, and the code
    is harder to maintain because of it. Maybe there is no particularly
    good reason for this - maybe the Lua programmers are just not very good developers.

    What I am saying is that you don't have the information or background to
    give a fair critique of the code. All you are giving is a highly
    subjective opinion based on a bit of the code, and your own strong
    prejudices about C in general, and certain C features in particular.

    Of course I too could say that at a first glance, there are too many
    low-value macros here, the identifier names are poorly chosen, and at
    least some of the macros would be better if they were replaced by inline functions. But I know that would be a first-glance viewpoint, and could
    easily be wrong or unfair.


    What qualifications and what experience would somebody need for David
    Brown to give credence to their opinion?

    I already outlined that.


    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.

    I am not so sure of that.

    Of course you can form a first impression in many cases, but I don't
    like to place too much weight on that. It is not unreasonable to talk
    about your first impressions, if they are clear enough, but it should be
    a qualified judgement. Don't say "this code abuses too many macros" -
    say "from what I have seen so far, it looks like the code uses more
    macros than I think is sensible for such a task".


    Suppose you reported that here, but I suggested that you don't know
    that; you're only guessing?


    If you called me out on a claim about some code without appropriate understanding of the context, then I should be grateful for the
    correction. I am not going to promise I /would/ be grateful - I'm a
    mere human, with as fragile an ego as the next person.


    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.

    So what?

    That does not mean you can judge whether the given macros are a good
    solution in this code. In fact, I'd say it makes you a /worse/ judge
    than most C programmers, since you are unfamiliar with macro usage
    (except to criticise it without justification).

    I've written a million lines of code, and never touched Rust - does that
    mean I can tell Rust programmers that their choices are inefficient and impractical?


    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.


    They are prevalent in C programming primarily because they are very
    useful in C programming, and fully supported by the language and tools.

    Do you also complain about people using the number 7 in their C code?
    After all, it is available in the language, but it is completely
    unnecessary - people could just write "6 + 1" instead and avoid all
    those silly sevens. Or do you think perhaps people write the number 7
    rather than "6 + 1" because it gives them clearer code in a more
    convenient manner, just like good use of macros does?


    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?


    Lua is an established language used by lots of people - it is
    documented, popular, and battle-tested (both as a language, and the
    tools) - yours is not. It is designed as an embedded scripting language
    - yours is, AFAIK, not. Lua is based on tables (for hashmaps, arrays,
    etc.), while yours AFAIK is not. Lua has anonymous functions - indeed,
    all functions are anonymous in Lua. Lua has support for OOP via tables.
    It has proper scoping. I'm confident there are a great many
    differences, though I don't know details of your language. I'm sure
    there are plenty of similarities however, as there are between any pair
    of 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.

    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.


    No, I didn't forget.

    I've also got my C compiler to build and run some of those (Lua and
    Seed7 among them).


    I've compiled Lua too, linked in with my own code.

    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.


    I realise you have done lots of stuff, and have lots of experience. I
    simply don't see it as relevant to be able to judge the way the Lua
    source code is structured without further knowledge of that code.

    And while I am loathe to make ad hominem judgements or appeals to
    authority, you are a guy off the internet who has made a lifetime career
    of living in your own bubble, avoiding any contact or information from
    other languages, other software, or computer science research -
    convinced in your own superiority in language design and implementation
    over the entire rest of humanity. Lua was designed and implemented by university professors - experts in the field, familiar with the
    strengths and weaknesses of lots of languages and implementation
    techniques. The language and its implementation has been refined and
    developed with the feedback of huge numbers of people.

    Of course you can have your subjective opinion on the language and its implementation. It is only natural that you would prefer your language,
    and your style of implementation. But do you understand that I do not
    consider your opinion to be objective? I am not giving any kind of
    opinion or critique on the Lua code, because I know I am not able to
    give a fair and justified analysis. I do not think you are either.

    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!

    Perhaps you should. I am sure you have a lot of information (about many topics) that you could put in a book - or at least a series of articles.
    It would be a more constructive use of your keyboard than a lot of
    these Usenet threads :-)



    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?)

    I don't know the Lua developers personally - but since the core team are university professors, I imagine they have at least /some/ suitable qualifications and knowledge.


    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.

    I /do/ acknowledge your experience. I don't acknowledge that you are in
    a position to judge whether the macros used in the implementation of Lua
    are a good idea or not. Can't you understand that difference?


    More so that you seem to know little about Lua, but decide to give them
    the benefit of a doubt anyway.


    Note - again - that I have not said their use of macros was good. I
    have said that neither you nor I are in a position to say their use of
    macros was bad.

    /Maybe/ they had good reasons for using so many macros, but /maybe/ so
    do I for having so few!


    Equally, I am not in a position to say that your use of few macros is
    bad or that your code would be improved with more of them.


    So why are you automatically assuming they must be right, and I must be wrong?

    Why are you assuming I have said they are right in the way they designed
    their code, or that I have said you were wrong?

    Stop trying to view everything as binary.


    Further, this is part of a survey of interpreters running a recursive Fibonacci benchmark:


    That tells you nothing useful.

    So I know fuck-all about writing interpreters, they are all toys, my languages are toys, and my opinion counts for nothing?


    Your opinions count as your opinions. Nothing more, nothing less.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Tim Rentsch@21:1/5 to Michael S on Sun May 4 07:31:11 2025
    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.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Tim Rentsch@21:1/5 to Richard Heathfield on Sun May 4 07:40:30 2025
    Richard Heathfield <rjh@cpax.org.uk> writes:

    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.

    Out of curiousity, I tabulated a similar set of statistics for
    a recent C project. Considering just the three iteration
    control structures (do/for/while), the results (rounded to the
    nearest 0.1 percent) were

    while 56.1 %
    for 24.1 %
    do/while 19.5 %

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Michael S@21:1/5 to Tim Rentsch on Sun May 4 18:08:33 2025
    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.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Tim Rentsch@21:1/5 to Scott Lurndal on Sun May 4 21:35:16 2025
    scott@slp53.sl.home (Scott Lurndal) writes:

    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.

    Any statement with the word "should" in it is a lot more likely
    to be a statement of opinion than a statement of fact.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Tim Rentsch@21:1/5 to Scott Lurndal on Sun May 4 21:32:22 2025
    scott@slp53.sl.home (Scott Lurndal) writes:

    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, [...]

    Does this mean you think the for() control structure defined
    in ISO C is ideal?

    I'm okay with using C for() statements as an iterative control
    structure, but I don't think I'd describe it as ideal.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Tim Rentsch@21:1/5 to Keith Thompson on Sun May 4 22:17:21 2025
    Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:

    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.

    If any poster shows signs of being enormously distraught by
    exchanges in a newsgroup (and comp.lang.c specifically), I hope
    other participants would notice that and consider disengaging
    with that poster for some period of time, not only for the sake
    of the distraught poster but also for the benefit of the rest of
    the group.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Michael S@21:1/5 to Michael S on Mon May 5 10:42:09 2025
    On Sun, 4 May 2025 18:08:33 +0300
    Michael S <already5chosen@yahoo.com> wrote:

    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
    ^^^^^^^^^
    keywords

    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.
    ^^
    my




    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Tim Rentsch@21:1/5 to Michael S on Tue May 6 05:59:20 2025
    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:

    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.

    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)?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Michael S@21:1/5 to Tim Rentsch on Wed May 7 12:32:32 2025
    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.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Janis Papanagnou@21:1/5 to Michael S on Wed May 7 14:54:09 2025
    On 07.05.2025 11:32, Michael S wrote:
    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.

    What I derive here is that [formal] reviewers should have some formal
    set of rules that have _sensible rationales_ attached. (This is what
    we've done in one company, at least.) The explanations above sound
    more like some individual/subjective feeling; that of course may have
    its validity (but maybe as well it has not).

    Coding standards, BTW, should thus also be subject to review, and its
    reviewers carefully selected. You can inflict harm to the process if
    you get such standard wrong and install opinion-based style-sheriffs.

    My own subjective feelings about the code above with the two loops is
    also not positive. But I'd neither consider the post-increment as a
    problem (comprehension or else), nor do I find the behavior surprising.

    Janis

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to Michael S on Wed May 7 13:50:26 2025
    Michael S <already5chosen@yahoo.com> writes:
    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.

    My reply, was of course, also tongue-in-cheek as a response to
    Bart's silly token counting. I've never found a reason to code
    a for loop like that.


    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.

    An overreaction, perhaps. I'm not sure what I'd do if I saw
    it during code review - it would depend on the surrounding context
    I suspect.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Tim Rentsch@21:1/5 to Michael S on Sat May 10 06:43:38 2025
    Michael S <already5chosen@yahoo.com> writes:

    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.

    Let me take these one at a time, roughly in order of most overloaded
    first. My aim here is to offer some perspectives that may alleviate
    the negative effects you feel.

    A new use for 'static' was added in C99, having to do with parameter
    array declarators. This new use has nothing to do with the earlier
    uses. All I can offer here is that these new uses don't occur very
    often, and are never necessary (in the same sense that 'restrict' is
    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
    extra effort needed could be seen as a positive.

    Starting already in pre-C99, 'static' has two uses that are somewhat
    different from each other, those uses being one, outside of any
    function definition, and two, local to a particular function body.
    Do you find the two kinds of circumstances need extra mental effort?
    To me the meaning in the two cases is the same but the applications
    are different, in much the same way that 'int x;' at file scope and
    'int x;' in block scope are both the same and different. So even
    though the how 'static' is used is different in the two cases, for
    consistency with other parts of declarations it seems better just to
    use 'static' for both.

    For 'long', maybe it would help to think of 'long' as an independent
    axis, sort of like 'volatile'. I think most programmers wouldn't
    have trouble with two independent axes, such as {binary, decimal}
    and {fixed-point, floating-point}. What modifiers would you use
    if had to have different words for integer types and floating-point
    types? Here I think using 'long' (or maybe 'wide'?) for both is
    more natural than having different words for the two cases.

    For 'break', to me all uses of 'break' do the same thing: they exit
    one level of control structure. It is perhaps odd that 'break'
    cannot be used with 'if', but other than that it seems needlessly
    redundant to have different kinds of 'break' for each of the various
    kinds of control structure. And if a function is so complicated
    that it isn't immediately apparent which control structure is being
    exited, that is an indication that the code needs revising, and not
    just because of problems with break.

    Similarly with 'while', both uses of 'while' mean the same thing: a
    single test to control whether the controlled loop continues or
    exits. We might think of the two kinds of loops as being different,
    but as far as 'while' goes it is doing the same thing whether it is
    at the top or at the bottom.

    I hope you have found these comments helpful. On a larger scale, it
    might help to think in general terms rather than being distracted by
    low-level details.

    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.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Muttley@dastardlyhq.com@21:1/5 to All on Sat May 10 15:56:45 2025
    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.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to Muttley@dastardlyhq.com on Sat May 10 17:48:20 2025
    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.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Michael S@21:1/5 to Tim Rentsch on Sun May 11 01:09:17 2025
    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.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Tim Rentsch@21:1/5 to Muttley@dastardlyhq.com on Sat May 10 14:29:50 2025
    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?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Muttley@dastardlyhq.com@21:1/5 to All on Sun May 11 08:20:56 2025
    On Sat, 10 May 2025 17:48:20 GMT
    scott@slp53.sl.home (Scott Lurndal) 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.

    '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.

    Overloading the same keyword to mean different things is never desirable IMO otherwise taken to its logical conclusion you might as well just have 1 keyword that does everything.

    Compiler switches are a thing - if there's a name clash with your old code don't compile it with the new version of the language. I'm sure plenty of old
    C code used variable names such as "new" or "class" so it can't be compiled
    by a C++ compiler.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Muttley@dastardlyhq.com@21:1/5 to All on Sun May 11 08:21:22 2025
    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.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Muttley@dastardlyhq.com on Sun May 11 12:02:25 2025
    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:
    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.


    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.)

    However, there are possible situations where it could be useful to the compiler. Consider instead this function :

    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
    so. And that means it can use a "prefetch 32 bytes" instruction, or a
    "load 4 doubles into a SIMD register" instruction, if these can result
    in faster code.

    When "[static X]" was added to C99, it was thought that this could be a significant help in some code. It certainly /can/ be helpful for some
    code (consider a case where the loop range is from another parameter
    rather than a fixed value like the examples above), but the combination
    of super-scaler and re-ordering modern processors and smarter modern
    compilers means it is very rare that it is helpful. And in cases where
    it could increase efficiency and you really need maximal efficiency, you
    are almost certainly better off using compiler-specific features (like
    gcc's __builtin_prefetch).


    So the obvious answer to Tim's question of what to use instead of
    "static" is "nothing at all" :-)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Muttley@dastardlyhq.com@21:1/5 to All on Sun May 11 15:30:01 2025
    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.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Muttley@dastardlyhq.com on Sun May 11 18:49:44 2025
    On 11/05/2025 17:30, Muttley@dastardlyhq.com wrote:
    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.



    No, C (and the C compiler) /does/ care if it something is safe or not.
    But it believes the programmer if the programmer says it is safe. Thus
    if the programmer writes code that accesses "values[3]", the compiler
    may assume it is safe to do so at other times too. But if the
    programmer never accesses beyond "values[2]", the compiler cannot assume
    it is safe to access "values[3]". If you want the compiler to be able
    to generate code that is more efficient by accessing "values[4]", you
    have to tell it that it is safe to do so. And using "static 4" here in
    the parameter is one way of saying that.

    There are other ways that could be used - if you write "(void)
    values[3];", the compiler could see that it is safe to read that
    address. Whether or not a given compiler optimiser would use that
    information is another matter, of course.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to Muttley@dastardlyhq.com on Sun May 11 16:29:39 2025
    Muttley@dastardlyhq.com writes:
    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.

    He meant that the optimizer could choose to generate a SIMD load
    instruction rather than three separate load instructions, trusting that
    the fourth entry will be legally acessible even if that extra loaded value is not used.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From James Kuyper@21:1/5 to David Brown on Sun May 11 17:43:42 2025
    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.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From James Kuyper@21:1/5 to Keith Thompson on Sun May 11 18:30:35 2025
    On 5/11/25 18:06, Keith Thompson wrote:
    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.

    True - I was responding to his claim that the information in the
    declaration was unneeded, because it's implicit in the function
    definition. That would be true if the primary purpose of this feature
    was to enable optimizations, but I don't thing it was. I think it was
    primarily intended to motivate (but not, unfortunately, mandate)
    warnings when the undefined behavior is predictable at compile time.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Tim Rentsch@21:1/5 to Michael S on Sun May 11 17:30:14 2025
    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?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Tim Rentsch@21:1/5 to Muttley@dastardlyhq.com on Sun May 11 17:59:31 2025
    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.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Tim Rentsch@21:1/5 to Keith Thompson on Sun May 11 18:15:33 2025
    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, 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.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From James Kuyper@21:1/5 to Tim Rentsch on Mon May 12 00:07:15 2025
    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, only explaining why
    the status quo is what it is.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Tim Rentsch@21:1/5 to Michael S on Sun May 11 23:48:26 2025
    Michael S <already5chosen@yahoo.com> writes:

    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.

    I took the posting at face value.

    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.

    I see your comments as saying a few different things. My intention
    below is to give fair paraphrases; please voice an objection if you
    feel that this was not done.

    One: the given code syntactically resembles a common code pattern
    but has quite different semantics.

    Two: postfix ++ is harder to understand than other operators.

    Three: the combination of postfix ++ and a comparison operator
    is even harder to understand than its simpler uses.

    Four: postfix ++ should never be used except in familiar and
    commonly used patterns.

    Five: the effect of the for() controlling statement is not what
    most readers (selected from experienced developers) would expect.

    Would you say that these statements above give a fair restatement
    of your comments? Or have I missed or misrepresented something?

    Taking the above points as fair restatements, here are my reactions.

    Point one is never a reason to reject a given code fragment. If a
    piece of code intends to express a particular semantics, but happens
    to resemble a more common pattern that has different semantics, that
    by itself is never reason to reject the code under consideration.
    It's reasonable to say that a comment be given to alert a reader to
    an unobvious difference, but not to reject the code outright.

    Point two is true at least insofar as postfix ++ is more complicated
    than simple arithmetic operators like + and *. The same could be
    said of all operators with side-effects. Do you think postfix ++ is
    more complicated than postfix --? Both postfix ++ and postfix --
    appear in common and familiar patterns. I don't think the given
    code fragment should be rejected solely for using a postfix ++.

    Point three is certainly true, at least in comparison with using
    postfix ++ by itself. But I don't see why you think using the
    result with a comparison operator is more complicated than other
    compound uses. Consider these code fragments, not too difficult
    I think for most developers:

    *lines++ = next_input_line();
    while( i-- > 0 ) ...

    I don't see either of these examples as being too different than the
    condition 'i++ <= 10'. Is your only reason for rejecting the given
    code is that you find it unfamiliar?

    Point four seems to say explicitly what came up implicitly in point
    three. I think a lot of people follow a similar rule, one not
    necessarily restricted to postfix ++ or other less-used patterns.
    As a guideline I think that's okay; as an inviolate rule I think
    it's wrong. Developers should always write code in a way that they
    feel best expresses their intentions, whether that way is an old
    way or a new way. A second consideration is that, if code is only
    ever written in old familiar ways, then newer and better ways will
    never be discovered. Taking a conservative stance is fine most of
    the time; always vetoing a new form just because it is new is
    overdoing it.

    Point five seems like simply another way of saying, in a more
    generic way, the view raised in point four, or maybe points four
    and one in combination. Same response as was given above.

    I might summarize my reactions as follows. I am philosophically
    opposed to the idea that code should only ever be written in common
    forms or that it should be "dumbed down" for less sophisticated
    developers. An advantage of C is that it is small enough so any
    experienced developer can understand all of it. If someone isn't
    capable of handling basic expressions like those under discussion
    here then they just shouldn't be programming.

    Incidentally, I might agree with you in the sense that I think
    the semantics for the code being considered can be effected using
    code that is either more familiar, or easier to follow than how
    it was written above, or both. If that is your actual underlying
    objection then I think we are pretty much on the same page. My
    response above though gives reactions to what was written, not
    what I imagined you were thinking.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Muttley@DastardlyHQ.org@21:1/5 to All on Mon May 12 10:11:43 2025
    On Sun, 11 May 2025 17:59:31 -0700
    Tim Rentsch <tr.17687@z991.linuxsc.com> wibbled:
    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.

    average( double values[ pointless_keyword 10 ] ){

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Michael S@21:1/5 to Tim Rentsch on Mon May 12 16:18:19 2025
    On Sun, 11 May 2025 17:30:14 -0700
    Tim Rentsch <tr.17687@z991.linuxsc.com> wrote:

    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.

    I don't see it as the same or similar. if we compare switch() with if/ if-else/else chain it's clear that switch() helps DRY which is very
    important (most important?) principle of programming.
    OTOH, while() does not help anything relatively to for().

    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
    }


    That is an interesting idea.
    Loops like those do happen quite often in my practice. Of course, if
    have to code them as
    for (;;) {
    ...
    if (!condition)
    break;
    ...
    }

    I find it probable that proposed extended do-while is somewhat easier
    for reader, if for nothing else then because positive logical
    conditions tend to be easier to grasp then negative conditions.

    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?

    I don't think that it's the same. for(;cond;) alternative is less
    clumsy and is not even longer than while(cond) in # of characters.

    If we compare to newer languages, authors of Go went one step further
    and completely gave up on loop with post-condition. Probably for early
    70s that was too radical. May be, it is still too radical now. I didn't
    write a lot of Go, so can't tell.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Michael S@21:1/5 to Muttley@DastardlyHQ.org on Mon May 12 17:09:07 2025
    On Mon, 12 May 2025 10:11:43 -0000 (UTC)
    Muttley@DastardlyHQ.org wrote:

    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 ] ){


    Richard Heathfield recently proposed newfangled_gibberish :-)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Tim Rentsch@21:1/5 to Keith Thompson on Mon May 12 07:19:00 2025
    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:

    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.)

    Please read again what I said: parameter array length information,
    specified by way of 'static', is ignored _outside of function
    definitions_.

    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. [...]

    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.

    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?

    There was no reason to guess, because it was written in plain
    English in the earlier posting.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Richard Heathfield@21:1/5 to Tim Rentsch on Mon May 12 15:34:51 2025
    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.

    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.

    --
    Richard Heathfield
    Email: rjh at cpax dot org dot uk
    "Usenet is a strange place" - dmr 29 July 1999
    Sig line 4 vacant - apply within

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Tim Rentsch@21:1/5 to Keith Thompson on Mon May 12 00:16:34 2025
    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.

    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.

    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.

    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.

    Neither do I, nor did I mean to imply that anyone had.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Tim Rentsch@21:1/5 to James Kuyper on Mon May 12 00:43:22 2025
    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.

    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.

    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.

    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, [...]

    I didn't mean to imply you had.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to James Kuyper on Mon May 12 17:08:32 2025
    On 11/05/2025 23:43, James Kuyper wrote:
    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.


    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.
    """

    I think my wording is at least as "precise" as yours. The potential UB
    here comes from violating the "shall" requirement.


    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.

    Indeed. I am also a big fan, in general, of making mistakes in code
    into compiler errors when possible - I'd rather the compiler found my
    bugs than leave it to run-time testing!

    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.

    Unfortunately, that is not quite true. A C programmer is free to write whatever nonsense and run-time UB they like, as long as that UB is not
    actually "executed". So a compiler, if it aims to be conforming and to
    accept code with well-defined behaviour, has to be sure that the bad
    code in question would inevitably be executed before it is justified in rejecting the code with an error.

    (When the user has asked for non-conforming behaviour, or additional
    analysis and warnings, then of course the compiler can complain when it
    sees a potential error. Such warnings are almost always a good idea.)

    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.


    The C99 rational says it was added for efficiency reasons. But often efficiency and static error analysis go together - the more information
    the compiler has, the better it is at both tasks. And I agree with your attitude of emphasising the static error analysis as being more
    important than the efficiency considerations in most cases.

    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.

    That is the case for its potential usefulness in static error checking,
    but not the case for its potential usefulness in optimisation.

    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.

    This is why some compilers and other verification tools have extensions
    that can do more than "static" array parameters can - gcc's "access"
    attribute and "object size" builtins are examples.

    Still, even if it only helps occasionally, that's better than no help at
    all.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Keith Thompson on Mon May 12 17:18:06 2025
    On 12/05/2025 11:27, Keith Thompson wrote:
    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.


    Sure. If gcc had a warning enforcing such consistency, I would
    definitely use it.

    But not all C programmers are as pedantic about consistency between a
    function declaration and definition. It is not uncommon to be
    inconsistent about the names of parameters (or even if the parameters
    have names at all), or mixing array-style parameters and pointer-style parameters.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Keith Thompson on Mon May 12 17:24:16 2025
    On 12/05/2025 11:23, 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:
    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.)


    The UB is actually at the function call site, rather than in the
    function itself. Thus adding a "static N" to the function definition
    can change the semantics of the call site:

    // file1.c
    extern int foo(int * p);

    int bar(void) {
    int xs[4];
    return foo(xs);
    }

    // file2.c
    #define N 4

    int foo(int p[static N]) {
    return 42;
    }



    Changing "N" to 5 will render the call in file1.c undefined behaviour,
    without having any semantic effect on the function definition in file2.c

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From James Kuyper@21:1/5 to Tim Rentsch on Mon May 12 19:53:14 2025
    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. The violation would have
    to be detected in the code that sets the pointer's value before passing
    it, directly or indirectly, to the function with a "static" array length.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Tim Rentsch@21:1/5 to Tim Rentsch on Mon May 12 19:04:20 2025
    Tim Rentsch <tr.17687@z991.linuxsc.com> writes:

    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.

    I need to correct myself. This statement is simply wrong. Giving a
    parameter declarator a 'static' length specification, as part of any
    function declaration whatsoever, has the same effect as it would if
    it had been given on the function definition. To present an example,
    if we have three source files, x.c, y.c, and z.c, with

    /* x.c */
    int foo( int v[] );

    int
    example(){
    return foo( 0 );
    }


    /* y.c */
    int
    foo( int v[] ){
    return v ? v[0] : -1;
    }


    /* z.c */
    int foo( int v[ static 1 ] );


    then because of the declaration of foo() in z.c, the call to foo()
    in x.c has undefined behavior, because a "shall" requirement is
    violated by passing a null pointer for parameter v, which must
    point to an array with at least one element.

    As best I can determine the description of how 'static' works in
    such cases has not changed since its original introduction in C99
    up to the present.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Tim Rentsch@21:1/5 to Richard Heathfield on Mon May 12 22:42:30 2025
    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. Certainly I would say you
    have your own views and opinions, and many of them strongly
    held. But that isn't the same as what I mean by self-centered.
    I don't think of you as being self-centered in the same sense
    as what I was trying to say above.

    (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.)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Richard Heathfield@21:1/5 to Tim Rentsch on Tue May 13 07:31:06 2025
    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.

    Keith's a big lad. He doesn't need me to defend him. But neither
    does he need you to attack him.

    --
    Richard Heathfield
    Email: rjh at cpax dot org dot uk
    "Usenet is a strange place" - dmr 29 July 1999
    Sig line 4 vacant - apply within

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Tim Rentsch@21:1/5 to James Kuyper on Mon May 12 23:03:59 2025
    James Kuyper <jameskuyper@alumni.caltech.edu> writes:

    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.
    [...]

    An implementation could issue a diagnostic whenever it could
    determine that the requirement had been violated, and also
    whenever it could not establish that the requirement was
    satisfied. A message like

    "this call to function foo() might not supply a large enough
    array to satisfy an array static length requirement".

    would, I think, satisfy the letter of the rule that any constraint
    violation must result in at least one diagnostic being produced.

    Granted, I think most people would find such behavior more
    annoying than useful, but it does seem to be a way to meet the
    stipulations for constraint violations, in letter even if not in
    spirit.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Keith Thompson on Tue May 13 12:41:22 2025
    On 12/05/2025 22:38, Keith Thompson wrote:
    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.)


    Certainly compilers are more likely to be able to differentiate between
    null values and non-null values, than between pointers to different
    lengths of data. So yes, this can be a useful check.

    For my own use, I dislike this syntax - if I want a pointer to an int, I
    prefer to give the parameter the form pointer to an int. The array form
    might mean the same thing to the compiler, but it does not give the same clarity to the human programmer. And if I want extra checking on this
    kind of thing, I use gcc attributes. (Of course not all programmers can
    do that, or want to do that, in their code.)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Janis Papanagnou@21:1/5 to Tim Rentsch on Tue May 13 15:57:00 2025
    On 12.05.2025 02:30, Tim Rentsch wrote:
    [...]

    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. [...]

    This is an interesting proposal.

    When was it suggested, and what is the decision state?

    What were the arguments, pros and cons?

    Janis

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Tim Rentsch@21:1/5 to Richard Heathfield on Tue May 13 09:30:55 2025
    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.

    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.

    I have two more short comments to add.

    One, I have no desire to start a fight, or engage in one.

    Two, I mean to observe a distinction between self-centered and
    selfish. These two words mean different things.

    Also let me add, parenthetically, in most cases I consider it
    bad manners to discuss a third party in a public forum. (That
    is not meant as a criticism of your commment, which I take to
    be a sincere interest in everyone's well being.) I don't mind
    responding to your comments but I don't expect to say anything
    specifically about Keith.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Janis Papanagnou@21:1/5 to Richard Heathfield on Tue May 13 22:28:25 2025
    On 12.05.2025 16:34, Richard Heathfield wrote:
    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.

    It's a social and psychological theme; when a topic gets in a personal direction and getting disrespectful and that is publicly exchanged the
    response (defense or counterstrike) is likely also continued where the
    "attack" had taken place. That bickering doesn't go ad infinitum but
    it at least leads to long and deep threads (with little value). If the disputants have a strong backbone - any many here have - it's getting difficult. But I sense occasional attempts to soothe the situations
    and thus interrupt the [perceived] endless process.

    f() { g(); }
    g() { f(); }

    without an appropriate 'if' will not be good. An alternative is to not
    start that process by f() or g() in the first place. - Just an attempt
    to have this meta-topic getting a bit "C"-like. Feel free to ignore it.
    :-p

    Janis

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Janis Papanagnou@21:1/5 to David Brown on Tue May 13 23:16:49 2025
    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?

    Janis

    [...]

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Tim Rentsch@21:1/5 to Keith Thompson on Tue May 13 15:10:56 2025
    Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:

    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.

    In N1570, 6.7.6.3 p7.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Tim Rentsch@21:1/5 to Keith Thompson on Tue May 13 18:38:32 2025
    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.

    """
    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.

    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.

    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)'.

    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.

    That isn't what the C standard says. It just says "array", not
    "array object".

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From James Kuyper@21:1/5 to Keith Thompson on Tue May 13 23:54:36 2025
    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 - you very commonly post messages leaving out lots of relevant
    information, seemingly expecting people to figure out the details that
    you don't bother to provide. This can be a valid teaching technique, but
    using that technique relies on the assumption that what you've left out
    can be determined by careful examination of what you put in. However,
    Keith had no reasonable way of guessing what you had left out, because
    he did not anticipate that you would address the wrong question. As a
    general rule, if there's any possibility that you've made a mistake like
    you did in this case, leaving out too many details as "an exorcise for
    the student" runs the risk of not not providing enough information for
    others to identify and correct the mistake.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Keith Thompson on Wed May 14 03:35:27 2025
    On 2025-05-13, Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:
    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 haven't found explicit wording which supports the concept in the above use scenario.

    The Library clause (I'm referring to n3301 here0 has the wording:

    If a function argument is described as being an array, the pointer passed to
    the function shall have a value such that all address computations and
    accesses to objects (that would be valid if the pointer did point to the first
    element of such an array) are valid.(218)

    Footnote 218 gives some degenerate examples more extreme than the
    arr + 5 situation:

    This includes, for example, passing a valid pointer that points
    one-past-the-end of an array along with a size of 0, or using any valid
    pointer with a size of 0.

    The presence of the wording in the Library clause suggests that someone though it was necessary. It needs to be extended (or repeated elsewhere with adjusted wording) to cover situations involving language constructs that are not standard library functions.

    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.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Tim Rentsch@21:1/5 to Keith Thompson on Tue May 13 21:12:53 2025
    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:

    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?

    In my view the same reasoning about the meaning applies to both
    cases, so there is no reason to talk about them separately.

    """
    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.

    To me it seems obvious that 6.7.6.3 p7 is meant to cover the
    case of 'func(arr+5)' as satisfying the "shall" requirement,
    for the same reasons that it is meant to cover the case of
    'func(arr+6)' as being undefined behavior.

    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.

    The meanings follow from a plain understanding of the English
    language.

    Consider the following example:

    typedef struct { int s; float f; } T;

    extern void foo( unsigned char blah[ static sizeof(T)-1 ] );

    void
    bas( T *it ){
    foo( (unsigned char *)it + 1 );
    }

    There is no array object. But surely there is an array (or at
    least an array is indicatated, and an array is present if 'it' is
    a valid pointer). This example satisfies the "shall" requirement
    in 6.7.6.3 p7, despite there being no array object in sight.

    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.)

    The wording of 6.7.6.3 p7 was carefully chosen so that it would allow
    cases like 'func(arr+5)', for the same reasons that it would deem
    'func(arr+6)' as being undefined behavior.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Tim Rentsch@21:1/5 to James Kuyper on Tue May 13 21:19:54 2025
    James Kuyper <jameskuyper@alumni.caltech.edu> writes:

    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 - [...]

    I don't know why you expect me to read postings meant for me but
    posted as a reply to another poster. Sometimes I do, sometimes
    I don't. You may take the correlation as being determined by
    quantum mechanical uncertainty.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Keith Thompson on Wed May 14 06:31:01 2025
    On 2025-05-14, Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:
    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.

    This issue with a + 5 and all points to a more general issue.
    There is no discussion about aliasing between arrays of
    the same element type, but different sizes and displacements.

    Given int a[10], we can do int (*p)[5] = (int (*)[5])(a + 5) to obtain
    a pointer the upper half of the array as an int [5] array type.

    Then *p is an array lvalue which recovers us a + 5.
    Surely, that is valid; but it's not spelled out.

    Arrays are not accessed themselves since they cannot be passed,
    returned or assigned, and their values "decay" to pointers.

    An access like (*p)[1] cannot be considered an invalid aliasing for
    a[6], since it uses the declared type of the element. The pointer is
    valid, and not by chance; but doe to good arithmetic
    on a good starting value.

    *p itself cannot be invalid aliasing because that concept is defined in
    terms of access using the wrong type, and *p doesn't access.

    (And so, why couldn't we pass the expression *p as the argument to that
    int a[static 5]? If *p doesn't count as an array, by what
    rule is that? Surely not aliasing. And if *p is okay but a + 5 is not,
    what's the point of the hair-splititng.)

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Keith Thompson on Wed May 14 13:00:51 2025
    On 13/05/2025 23:35, Keith Thompson wrote:
    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.


    My interpretation matches yours. I can't find any indication in the
    standard of a definition of what an "array" actually means. It would be extraordinary - and render vast amounts of C code as having undefined
    behaviour - if "arr + 5" above could not be considered as pointing to
    the first element of an array of 5 int. (I believe the same logic
    applies to pointer arithmetic in general - not just this specific
    situation.) But that does not imply that the standard says a pointer to
    an element inside an array or array object can be treated as a pointer
    to the first element of an array and/or array object of appropriate size.

    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.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Janis Papanagnou@21:1/5 to David Brown on Wed May 14 13:20:57 2025
    On 14.05.2025 13:00, David Brown wrote:
    [...]

    I believe we can reassure Janis that your example code will be
    considered valid by any real-world compiler.

    Thanks. Much appreciated. (I wasn't sure after some postings that were
    a bit distracting and thus more muddying the topic than clarifying.)

    But it would be nice to
    find some combination of standard paragraphs that guarantee it.

    Yes, indeed. (Heuristics are fine but certainty is better.)

    Janis

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Tim Rentsch@21:1/5 to All on Wed May 14 11:09:51 2025
    Michael S <already5chosen@yahoo.com> writes:

    (I am summarizing heavily in an effort to return to the main area
    of interest.)

    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]

    I did a more comprehensive gathering of statistics, using an ad hoc
    collection of 68 open source projects, ranging in size from just
    over 1500 lines to over a million lines, including two outliers
    with 20 million lines and 75 million lines. The ratio of while()
    to do/while() ranged from 12.5% to 2100%, with an average of
    573.9%. The ratio of while() to for() ranged from 1.6% to 300%,
    with an average of 60.8%.

    [...] while() does not help anything relatively to for().

    I understand that that is your view. I don't remember seeing any
    supporting statements other than your personal reactions. My own
    experience is different. Judging by the open source statistics
    reported above, it appears that a fair number of other developers
    don't share your views on this question either.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From James Kuyper@21:1/5 to David Brown on Wed May 14 23:20:48 2025
    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".

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Tim Rentsch@21:1/5 to Keith Thompson on Wed May 14 20:44:54 2025
    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:

    [...]

    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.

    There was no animosity meant in my comments. I am very sorry
    if it came across otherwise. I am making an effort to frame
    my future statements to help with this.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Tim Rentsch@21:1/5 to Richard Heathfield on Wed May 14 21:12:21 2025
    Richard Heathfield <rjh@cpax.org.uk> writes:

    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.

    I responded twice to your earlier message. I want to acknowledge this
    later message and respond here in an effort to achieve some closure.

    I appreciate the intention of your two followups. It isn't easy to
    play the role of peacemaker, especially in comp.lang.c, and I think
    your comments are helping cool things down. Thank you for that.

    Clearly there have been some misunderstandings. I think it would
    be a real effort to unwind those (even assuming that they have all
    been identified), and besides this isn't the right venue at least
    for some of them. So I don't have more to say about that except
    to say I am making an effort to do better in the future.

    Thank you again for trying to help.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to James Kuyper on Thu May 15 11:23:04 2025
    On 15/05/2025 05:20, James Kuyper wrote:
    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".


    Yes, I agree.

    I guess that's why they call c.l.c. regulars who discuss these things
    "language lawyers"! Now all we need to do is find a way to get paid
    like lawyers...

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)