• Effect of CPP tags

    From Janis Papanagnou@21:1/5 to All on Tue Dec 26 16:59:40 2023
    This is a CPP question that arose last month. It's not about an actual
    issue with the software, just out of curiosity and to be sure it works
    reliable (it seemingly does).

    In a C99 program on Linux (Ubuntu) I intended to use usleep() and then
    also strnlen().

    When I added usleep() and its include file I got an error and was asked
    to define the CPP tag '_BSD_SOURCE'. I did so, and because I wanted
    side effects of that tag kept as small as possible I prepended it just
    before the respective #include and put it at the end of my #include list

    ...other #includes...
    #define _BSD_SOURCE
    #include <unistd.h>

    But as got obvious *that* way there had been side-effects and I had to
    put the tag at the beginning of all include files (which astonished me)

    #define _BSD_SOURCE
    #include <unistd.h>
    ...other #includes here...

    For the strnlen() function I needed another CPP tag, '_GNU_SOURCE'. So
    now I have both CPP tag definitions before the includes

    #define _GNU_SOURCE /* necessary for strnlen() in string.h */
    #define _BSD_SOURCE /* necessary for usleep() in unistd.h */
    ...all #includes here...

    The compile showed no error messages and the code works fine (it seems).

    Now I'm not feeling very comfortable with that; I seem to declare two
    different "philosophies" that way, GNU and BSD. And both tags affect
    all include files. - Is that okay?

    Last time I looked into the system header files, three decades ago, I
    got repelled by all the #ifdef's, cascaded and nested, a spaghetti code
    of dependencies; I'm astonished it works. - And the responsibility to
    keep all the CPP tags consistent with _all_ the header files lies at
    the side of the system headers' developers? The programmer doesn't need
    to care?

    Janis

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lowell Gilbert@21:1/5 to Janis Papanagnou on Tue Dec 26 17:45:08 2023
    Janis Papanagnou <janis_papanagnou+ng@hotmail.com> writes:

    In a C99 program on Linux (Ubuntu) I intended to use usleep() and then
    also strnlen().

    usleep() isn't in C99. It comes from POSIX, where it was declared
    obsolete in 2001. [This information is available in "man usleep".]

    I don't want to get into checking which version of glibc you're using,
    and such details, so in this case I'll just recommend that you follow
    POSIX and use nanosleep() instead. You'll need to multiply by a thousand
    and reference different include files, but I strongly suspect you can
    figure that all out on your own.

    With only fabulously rare exceptions (which mostly involve the
    programmer knowing more about the implementation of libc than the libc implementors did), user code should not be defining (or undefining)
    _GNU_SOURCE or _BSD_SOURCE. You seem to have had thoughts in that
    direction, and you were absolutely right.

    Be well.
    --
    Lowell Gilbert, embedded/networking software engineer
    http://be-well.ilk.org/~lowell/

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Janis Papanagnou on Tue Dec 26 22:50:40 2023
    On 2023-12-26, Janis Papanagnou <janis_papanagnou+ng@hotmail.com> wrote:
    This is a CPP question that arose last month. It's not about an actual
    issue with the software, just out of curiosity and to be sure it works reliable (it seemingly does).

    In a C99 program on Linux (Ubuntu) I intended to use usleep() and then
    also strnlen().

    When I added usleep() and its include file I got an error and was asked
    to define the CPP tag '_BSD_SOURCE'. I did so, and because I wanted
    side effects of that tag kept as small as possible I prepended it just
    before the respective #include and put it at the end of my #include list

    ...other #includes...
    #define _BSD_SOURCE
    #include <unistd.h>

    But as got obvious *that* way there had been side-effects and I had to
    put the tag at the beginning of all include files (which astonished me)

    Feature selection macros must be in effect before any system header
    is included, and are usually put on the compiler command line:

    cc ... -D_BSD_SOURCE -D_XOPEN_SOURCE=700 ...

    The concept of feature selection macros is documented in POSIX.

    https://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_xsh_chap02.html

    I will give you an excellent reason not to put them in source code:
    their behavior is system-specific. No single combination of these things
    works everywhere. It's best to wrestle that out in the configure
    scripts and Makefiles, keeping the code clean.

    The header files in BSD Unixes make the wrong interpretation of how
    feature selection macros are supposed to work. This affects everything
    that is based on BSD or has copy and pasted C libraries from BSD:
    Android's Bionic, Cygwin's Newlib, Apple's Darwin environment.

    The BSD interpretation of feature selection macros is this:

    1. All available identifiers are turned on by default.

    2. Feature selection macros *restrict* (subtract) from that set.

    3. So do macros coming from the compiler's dialect selection.

    Under BSD, if you use, say gcc -ansi -D_POSIX_SOURCE, it will
    stupidly produce the intersection of pure ANSI and POSIX.
    What you wanted was to use the ANSI C dialect of the language, with
    POSIX functions. The BSD interpretation is that ANSI means you don't
    want <stdio.h> to give you the declaration of fileno or fdopen,
    and so the -D_POSIX_SOURCE won't reveal POSIX things in ANSI/ISO C
    headers.

    Similarly, if you picked _POSIX_SOURCE and _BSD_SOURCE, you would
    stupidly get the intersection of those two, not the union.

    In other systems, like GNU/Linuxes, Solaris (I think), you get
    the union: -ansi -D_POSIX_SOURCE -D_BSD_SOURCE would work as you
    expect: your code is the ANSI dialect, and you want header files
    to reveal POSIXisms as well as BSD-isms.

    In the Apple environment, forget about it; fine-grained feature
    selection is broken: you use -D_DARWIN_C_SOURCE and be done with it.
    It's like the old -D_HPUX_SOURCE to get anything compiled on HP-UX.

    #define _GNU_SOURCE /* necessary for strnlen() in string.h */

    I think once you define _GNU_SOURCE, Glibc's feature selection will
    give you everything. BSD functions in the GNU C library are also
    considered GNU extensions over POSIX. It's like the GNU equivalent of _DARWIN_C_SOURCE or _HPUX_SOURCE; you're asking to reveal everything
    from the GNU vendor.

    On Glibc, the header /usr/include/features.h is the switch where
    the feature selection public macros get converted into combinations of
    internal private macros which are then used throughout the headers
    to turn things on and off. The features.h header contains a
    block comment which lists the public feature selection macros, and
    another one that describes the internal ones you're not supposed
    to use directly.

    You can see that for _GNU_SOURCE, the comment says:

    _GNU_SOURCE All of the above, plus GNU extensions.

    All of the above refers to all the POSIX, X/Open, BSD stuff
    listed above that.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Janis Papanagnou@21:1/5 to Janis Papanagnou on Thu Dec 28 17:34:45 2023
    On 26.12.2023 16:59, Janis Papanagnou wrote:
    [...]
    In a C99 program on Linux (Ubuntu) I intended to use usleep() and then
    also strnlen().

    When I added usleep() and its include file I got an error and was asked
    to define the CPP tag '_BSD_SOURCE'. [...]

    Thanks for all the replies.


    Kaz wrote:
    Feature selection macros must be in effect before any system header
    is included, and are usually put on the compiler command line:

    Right. This time I came from the functions' man pages and read that
    such tags are necessary for them, so I didn't think about the original
    purpose of these tags. I forgot that decades ago we used for platform
    specific declarations. Thanks for refreshing my neurons.

    I think once you define _GNU_SOURCE, Glibc's feature selection will
    give you everything.

    Indeed. I removed the one obsolete tag.


    Lowell wrote:
    usleep() isn't in C99. It comes from POSIX, where it was declared
    obsolete in 2001.

    Yes, I read about that. Though here I'm just programming non-portably
    for my local (and static, non-changing) environment, so it's not an
    issue in practice. Generally I try to program close to standards (but
    standards obviously also change, as we see).

    I'll just recommend that you follow POSIX and use nanosleep()
    instead.

    When I had read about the various 'sleep' options I decided to use one
    which supports sub-second resolution and with a most simple interface.
    That's why my choice was the simple 'usleep(usec);' even if obsolete
    by POSIX. The nanosleep() is not "very complex", sure, but I'd have to
    litter my code with variables unnecessary in my context, and also the advertised "advantages" of this function do not apply in my case.[*]

    And thanks for your confirmation that my "thoughts" about "not looking
    right to use _GNU_SOURCE and _BSD_SOURCE" was not unjustified.


    Spiros wrote:
    By the way , this kind of question is more appropriate for
    comp.unix.programmer

    I was indeed pondering about that. But I'm not programming Unix, and
    here I came from an application programming question so my choice was
    this newsgroup, and I think (and hope) it has not been a bad choice.

    I certainly found the experts to get the clarifications, suggestions,
    and insights that were helpful.

    Janis

    [*] To illustrate: I recall a similar decision in Java context. There
    was a simple and easy to use rexexp library (from Apache, IIRC). And
    there was also a most flexible blown up library that made its usage a
    lot more bulky (using and instantiating many classes and dependencies).
    I used the simple, user-friendly one. Later the bulky library became
    the Java standard.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lowell Gilbert@21:1/5 to Janis Papanagnou on Thu Dec 28 14:11:42 2023
    Janis Papanagnou <janis_papanagnou+ng@hotmail.com> writes:

    Lowell wrote:
    I'll just recommend that you follow POSIX and use nanosleep()
    instead.

    When I had read about the various 'sleep' options I decided to use one
    which supports sub-second resolution and with a most simple interface.
    That's why my choice was the simple 'usleep(usec);' even if obsolete
    by POSIX. The nanosleep() is not "very complex", sure, but I'd have to
    litter my code with variables unnecessary in my context, and also the advertised "advantages" of this function do not apply in my case.[*]

    To be honest,I didn't actually understand where your problem came from
    in the first place -- I just chose not to bring up more than one point
    at a time. While usleep() is obsolete, it works fine, without any
    feature test macro games, on (as far as I know) all POSIX-ish
    systems. Certainly on recent Ubuntu, the following program compiles and
    runs perfectly well without even any warnings with even the most extreme
    levels of warning enabled:

    #include <stdio.h>
    #include <unistd.h>

    int main(void)
    {
    printf("starting\n");
    usleep(2500000);
    printf("finishing\n");
    }


    --
    Lowell Gilbert, embedded/networking software engineer
    http://be-well.ilk.org/~lowell/

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Lowell Gilbert on Thu Dec 28 21:33:33 2023
    On 2023-12-28, Lowell Gilbert <lgusenet@be-well.ilk.org> wrote:
    Janis Papanagnou <janis_papanagnou+ng@hotmail.com> writes:

    Lowell wrote:
    I'll just recommend that you follow POSIX and use nanosleep()
    instead.

    When I had read about the various 'sleep' options I decided to use one
    which supports sub-second resolution and with a most simple interface.
    That's why my choice was the simple 'usleep(usec);' even if obsolete
    by POSIX. The nanosleep() is not "very complex", sure, but I'd have to
    litter my code with variables unnecessary in my context, and also the
    advertised "advantages" of this function do not apply in my case.[*]

    To be honest,I didn't actually understand where your problem came from
    in the first place -- I just chose not to bring up more than one point
    at a time. While usleep() is obsolete, it works fine, without any
    feature test macro games, on (as far as I know) all POSIX-ish
    systems. Certainly on recent Ubuntu, the following program compiles and
    runs perfectly well without even any warnings with even the most extreme levels of warning enabled:

    #include <stdio.h>
    #include <unistd.h>

    int main(void)
    {
    printf("starting\n");
    usleep(2500000);
    printf("finishing\n");
    }

    But if you don't specify any options, you're not even specifying the C
    dialect. You will get whatever dialect your gcc installation defaults
    to. That is always a GNU dialect, firstly, which you might not want.
    Secondly, it's a moving target; it' used to be gnu89, then gnu99
    then gnu11. Now GCC defaults to gnu17.

    Once you specify the dialect, things get strict.

    $ gcc usleep.c

    Nothing

    $ gcc -std=c99 usleep.c
    usleep.c: In function ‘main’:
    usleep.c:7:4: warning: implicit declaration of function ‘usleep’; did
    you mean ‘sleep’? [-Wimplicit-function-declaration]
    usleep(2500000);
    ^~~~~~
    sleep

    Oops! Once we use any feature selection macro (or compiler option that generates #defines that serve as feature selection macros) the set of
    what is visible is reduced to some minimal set, and from then, the
    feature macros turn on the selected symbols.

    Thus <unistd.h> reveals only some fairly old POSIX functions.
    Maybe not even up to 2003? The details depend on the behavior of the C
    library, like Glibc's <features.h>.

    The GNU-documented preferred way to coax usleep out of <unistd.h>
    under this condition is this:

    $ gcc -std=c99 -D_DEFAULT_SOURCE usleep.c

    The _DEFAULT_SOURCE feature selection symbol brings out some traditional functions (or something like that) without bringing in all the full
    blown extensions of _GNU_SOURCE.

    The BSD people misunderstood this whole thing. Without feature
    selections, they reveal everything. But the semantics of a feature
    selection is "hide everything except this". They neglected to implement
    the correct logic: "if at least one feature selection is present, hide everything except for some minimal base, and then for every feature
    selection, make those respective symbols visible."

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Janis Papanagnou on Thu Dec 28 21:22:42 2023
    On 2023-12-28, Janis Papanagnou <janis_papanagnou+ng@hotmail.com> wrote:
    On 26.12.2023 16:59, Janis Papanagnou wrote:
    [...]
    In a C99 program on Linux (Ubuntu) I intended to use usleep() and then
    also strnlen().

    When I added usleep() and its include file I got an error and was asked
    to define the CPP tag '_BSD_SOURCE'. [...]

    Thanks for all the replies.


    Kaz wrote:
    Feature selection macros must be in effect before any system header
    is included, and are usually put on the compiler command line:

    Right. This time I came from the functions' man pages and read that
    such tags are necessary for them, so I didn't think about the original purpose of these tags. I forgot that decades ago we used for platform specific declarations. Thanks for refreshing my neurons.

    I'm a real stickler for not wanting to reveal everything in the headers,
    if possible.

    So here is the irony. On BSD libraries, you can get BSD symbols with
    a dialect option like -ansi or -std=C99 fi you use the secret, internal, double-leading-underscored feature selector __BSD_VISIBLE. As in:

    gcc ... -std=c99 -D__BSD_VISIBLE

    The -std=c99 generates certain #defines with BSD takes as a clue to
    hide everything not related to C99 from every standard ISO C header.

    We then pry that open with __BSD_VISIBLE. If we used -D_BSD_SOURCE,
    that wouldn't work.

    In the TXR configure script, I have an elaborate test.
    (Not shown is the conftest function which compiles conftest.c,
    with the assistance of a rule in the Makefile, and reports success):

    printf "Detecting what symbol reveals BSD functions ... "

    cat > conftest.c <<!
    #include <unistd.h>
    #include <stdlib.h>

    int main(int argc, char **argv)
    {
    int (*pdaemon)(int, int) = &daemon;
    }
    !

    if conftest ; then
    printf "none needed\n"
    else
    for flag in _DEFAULT_SOURCE _BSD_SOURCE __BSD_VISIBLE _GNU_SOURCE _X_OOPS; do
    if [ $flag = _X_OOPS ] ; then
    printf "failed\n"
    break
    fi

    if conftest EXTRA_FLAGS=-D$flag ; then
    printf "%s\n" $flag
    lang_flags="$lang_flags -D$flag"
    gen_config_make
    break
    fi
    done
    fi

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Kaz Kylheku on Thu Dec 28 21:42:04 2023
    On 2023-12-28, Kaz Kylheku <433-929-6894@kylheku.com> wrote:
    Once you specify the dialect, things get strict.

    $ gcc usleep.c

    Nothing

    $ gcc -std=c99 usleep.c
    usleep.c: In function ‘main’:
    usleep.c:7:4: warning: implicit declaration of function ‘usleep’; did
    you mean ‘sleep’? [-Wimplicit-function-declaration]
    usleep(2500000);
    ^~~~~~
    sleep

    For completeness:

    No warning with -Wall without a dialect selection: declaration of usleep
    is not hidden in <unistd.h>:

    $ gcc -Wall usleep.c

    No warning with C89. But that's because GCC doesn't warn about implicit declarations when in C89 mode:

    $ gcc -std=c89 usleep.c

    Put in -Wall and we see that usleep is hidden in <unistd.h>

    $ gcc -std=c89 -Wall usleep.c
    usleep.c: In function ‘main’:
    usleep.c:7:4: warning: implicit declaration of function ‘usleep’

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Keith Thompson on Thu Dec 28 21:47:00 2023
    On 2023-12-28, Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:
    Lowell Gilbert <lgusenet@be-well.ilk.org> writes:
    [...]
    To be honest,I didn't actually understand where your problem came from
    in the first place -- I just chose not to bring up more than one point
    at a time. While usleep() is obsolete, it works fine, without any
    feature test macro games, on (as far as I know) all POSIX-ish
    systems. Certainly on recent Ubuntu, the following program compiles and
    runs perfectly well without even any warnings with even the most extreme
    levels of warning enabled:

    #include <stdio.h>
    #include <unistd.h>

    int main(void)
    {
    printf("starting\n");
    usleep(2500000);
    printf("finishing\n");
    }

    I think the warnings you enabled weren't extreme enough:

    $ gcc -std=c11 -c c.c
    c.c: In function ‘main’:
    c.c:7:4: warning: implicit declaration of function ‘usleep’; did you mean ‘sleep’? [-Wimplicit-function-declaration]
    7 | usleep(2500000);
    | ^~~~~~
    | sleep
    $

    What's actually happening is that -std=c11 causes <unistd.h> to hide
    the usleep declaration. (Even tough <unistd.h> isn't in ISO C).

    If you just use:

    gcc -Wimplicit-function-declaration -c c.c

    it will not warn. Since no feature selection has been made, all symbols
    in headers are maximally visible.

    Unless you're using an old GCC whose default dialect is gnu89,
    the -Wimplicit-functiond-declaration option is already present in
    the default dialect (like gnu99, gnu11 or gnu17).

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lowell Gilbert@21:1/5 to Kaz Kylheku on Thu Dec 28 18:04:54 2023
    Kaz Kylheku <433-929-6894@kylheku.com> writes:

    On 2023-12-28, Lowell Gilbert <lgusenet@be-well.ilk.org> wrote:
    Janis Papanagnou <janis_papanagnou+ng@hotmail.com> writes:

    Lowell wrote:
    I'll just recommend that you follow POSIX and use nanosleep()
    instead.

    When I had read about the various 'sleep' options I decided to use one
    which supports sub-second resolution and with a most simple interface.
    That's why my choice was the simple 'usleep(usec);' even if obsolete
    by POSIX. The nanosleep() is not "very complex", sure, but I'd have to
    litter my code with variables unnecessary in my context, and also the
    advertised "advantages" of this function do not apply in my case.[*]

    To be honest,I didn't actually understand where your problem came from
    in the first place -- I just chose not to bring up more than one point
    at a time. While usleep() is obsolete, it works fine, without any
    feature test macro games, on (as far as I know) all POSIX-ish
    systems. Certainly on recent Ubuntu, the following program compiles and
    runs perfectly well without even any warnings with even the most extreme
    levels of warning enabled:

    #include <stdio.h>
    #include <unistd.h>

    int main(void)
    {
    printf("starting\n");
    usleep(2500000);
    printf("finishing\n");
    }

    But if you don't specify any options, you're not even specifying the C dialect. You will get whatever dialect your gcc installation defaults
    to. That is always a GNU dialect, firstly, which you might not want. Secondly, it's a moving target; it' used to be gnu89, then gnu99
    then gnu11. Now GCC defaults to gnu17.

    Once you specify the dialect, things get strict.

    Yes, that's true. I was making an educated guess that the original poster wasn't actually asking for strict C99, despite referring to a "C99 program." I think the statement that "I'm just programming non-portably for my local (and static, non-changing) environment" is strong evidence for this. The discussion had clearly gone beyond standard C before that, usleep() has never been part
    of the actual language.

    Be well.
    --
    Lowell Gilbert, embedded/networking software engineer
    http://be-well.ilk.org/~lowell/

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to Janis Papanagnou on Fri Dec 29 02:35:46 2023
    On Tue, 26 Dec 2023 16:59:40 +0100, Janis Papanagnou wrote:

    But as got obvious *that* way there had been side-effects and I had to
    put the tag at the beginning of all include files (which astonished me)

    It has always been thus <https://manpages.debian.org/7/feature_test_macros.en.html>:

    NOTE: In order to be effective, a feature test macro must be defined
    before including any header files.

    Last time I looked into the system header files, three decades ago, I
    got repelled by all the #ifdef's, cascaded and nested, a spaghetti code
    of dependencies; I'm astonished it works.

    The whole concept of include files and string-based macro processing is
    flawed. But that’s C for you ...

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to Lawrence D'Oliveiro on Fri Dec 29 13:31:37 2023
    On 29/12/2023 02:35, Lawrence D'Oliveiro wrote:
    On Tue, 26 Dec 2023 16:59:40 +0100, Janis Papanagnou wrote:

    But as got obvious *that* way there had been side-effects and I had to
    put the tag at the beginning of all include files (which astonished me)

    It has always been thus <https://manpages.debian.org/7/feature_test_macros.en.html>:

    NOTE: In order to be effective, a feature test macro must be defined
    before including any header files.

    Last time I looked into the system header files, three decades ago, I
    got repelled by all the #ifdef's, cascaded and nested, a spaghetti code
    of dependencies; I'm astonished it works.

    The whole concept of include files and string-based macro processing is flawed. But that’s C for you ...

    It's not just C's fault. It's the insistence of having have just ONE
    system header that has to work for as many platforms and versions as
    possible.

    Then that is just added to over the years to include to result in the patched-together mess that you see that is utterly unreadable. You can't simplify it it take things out because something could break. It is fragile.

    Why not have a dedicated header file that is the specific to a
    particular version of a C compiler for a given platform? That it can be streamlined for that purpose.

    If someone is maintaining compilers that need to work across a range of targets, then they can have a process that synthesises the header needed
    for a specific configuration.

    (I guess this is something that is harder on Linux because there, many
    standard headers are not part of a specific C compiler, but are a
    resource shared by all C compilers, or tools that need to process C
    headers.)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Janis Papanagnou@21:1/5 to Lowell Gilbert on Fri Dec 29 16:04:13 2023
    On 28.12.2023 20:11, Lowell Gilbert wrote:

    To be honest,I didn't actually understand where your problem came from
    in the first place -- I just chose not to bring up more than one point
    at a time. While usleep() is obsolete, it works fine, without any
    feature test macro games, on (as far as I know) all POSIX-ish
    systems. Certainly on recent Ubuntu, the following program compiles and
    runs perfectly well without even any warnings with even the most extreme levels of warning enabled:
    [snip code]

    Here's the output of the compiler call with #define _GNU_SOURCE removed

    $ cc -std=c99 -o warn warn.c
    warn.c: In function delay_time:
    warn.c:368:3: warning: implicit declaration of function strnlen [-Wimplicit-function-declaration]
    warn.c: In function main:
    warn.c:579:5: warning: implicit declaration of function usleep [-Wimplicit-function-declaration]

    It compiles, but if I see warnings I nonetheless try to get rid of them.

    $ cc --version
    cc (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3

    (It's no "recent Ubuntu", I'm sure.)

    Janis

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Janis Papanagnou@21:1/5 to Lowell Gilbert on Fri Dec 29 16:11:49 2023
    On 29.12.2023 00:04, Lowell Gilbert wrote:

    Yes, that's true. I was making an educated guess that the original poster wasn't actually asking for strict C99, despite referring to a "C99 program."

    I switched (from the default cc setting) to -std=c99 since when a
    compile run said that my program needs C99 to run. That was the
    whole reason for it. And I mentioned it in my post since I thought
    it would be relevant context information to answer the question.

    Janis

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Bart on Fri Dec 29 15:58:22 2023
    On 29/12/2023 14:31, Bart wrote:
    On 29/12/2023 02:35, Lawrence D'Oliveiro wrote:
    On Tue, 26 Dec 2023 16:59:40 +0100, Janis Papanagnou wrote:

    But as got obvious *that* way there had been side-effects and I had to
    put the tag at the beginning of all include files (which astonished me)

    It has always been thus <https://manpages.debian.org/7/feature_test_macros.en.html>:

          NOTE: In order to be effective, a feature test macro must be defined
          before including any header files.

    Last time I looked into the system header files, three decades ago, I
    got repelled by all the #ifdef's, cascaded and nested, a spaghetti code
    of dependencies; I'm astonished it works.

    The whole concept of include files and string-based macro processing is flawed. But that’s C for you ...

    It's not just C's fault. It's the insistence of having have just ONE
    system header that has to work for as many platforms and versions as possible.

    Then that is just added to over the years to include to result in the patched-together mess that you see that is utterly unreadable. You can't simplify it it take things out because something could break. It is
    fragile.

    Why not have a dedicated header file that is the specific to a
    particular version of a C compiler for a given platform? That it can be streamlined for that purpose.


    This kind of dilemma turns up all the time in development. You
    regularly have multiple variations of projects or code, where most
    things are the same but there are a few important differences scattered
    around. If you choose to have separate code bases, you have to
    duplicate work for new features or bug fixes. If you choose to combine
    them, you risk a mess of compile-time conditionals, or run-time
    conditionals that complicate the code and make it less efficient. The
    first method is a pain for people maintaining the general code, while
    the second method is a pain for people only interested in one variation.
    Sometimes it is possible to have a clean separation between common
    parts and variant-specific parts, other times that just makes things
    even more complicated.

    There is no good answer here, and often you are stuck with decisions
    that made sense when they were made, but are no longer the best choice
    as the project has grown and developed. And yet at no point is it
    feasible to throw things out and start again.

    I agree it is not just C's fault - it is, I think, inevitable for any long-lived large project that is used in many ways. And it is always
    going to be a pain for some people, no matter what is done.

    At least for things like glibc the great majority of people using it for
    their development can ignore the mess and read the documentation instead
    of looking into the header files. And most of those that /do/ need to
    look in the headers, only need to do so occasionally.


    A useful tool that someone might like to write for this particular
    situation would be a partial C preprocessor, letting you choose what
    gets handled. You could choose to expand the code here for, say,
    _GNU_SOURCE and _BSD_SOURCE - any use of these in #ifdef's and
    conditional compilation would be expanded according to whether you have
    defined the symbols or not, leaving an output that is easier to
    understand while keeping most of the pre-processor stuff unchanged (so
    not affecting #includes, and leaving #define'd macros and constants
    untouched and therefore more readable).

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to Janis Papanagnou on Fri Dec 29 15:52:46 2023
    Janis Papanagnou <janis_papanagnou+ng@hotmail.com> writes:

    I'll just recommend that you follow POSIX and use nanosleep()
    instead.

    When I had read about the various 'sleep' options I decided to use one
    which supports sub-second resolution and with a most simple interface.
    That's why my choice was the simple 'usleep(usec);' even if obsolete
    by POSIX. The nanosleep() is not "very complex", sure, but I'd have to
    litter my code with variables unnecessary in my context, and also the >advertised "advantages" of this function do not apply in my case.[*]

    You can always define your own usleep:

    inline int
    usleep(useconds_t microseconds)
    {
    struct timespec ts;
    ts.tv_sec = microseconds / (useconds_t)1000000;
    ts.tv_nsec = (long)(microseconds % (useconds_t)1000000) * 1000L;
    return nanosleep(&ts, NULL);
    }

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Richard Damon@21:1/5 to Bart on Fri Dec 29 11:58:18 2023
    On 12/29/23 8:31 AM, Bart wrote:
    On 29/12/2023 02:35, Lawrence D'Oliveiro wrote:
    On Tue, 26 Dec 2023 16:59:40 +0100, Janis Papanagnou wrote:

    But as got obvious *that* way there had been side-effects and I had to
    put the tag at the beginning of all include files (which astonished me)

    It has always been thus <https://manpages.debian.org/7/feature_test_macros.en.html>:

          NOTE: In order to be effective, a feature test macro must be defined
          before including any header files.

    Last time I looked into the system header files, three decades ago, I
    got repelled by all the #ifdef's, cascaded and nested, a spaghetti code
    of dependencies; I'm astonished it works.

    The whole concept of include files and string-based macro processing is flawed. But that’s C for you ...

    It's not just C's fault. It's the insistence of having have just ONE
    system header that has to work for as many platforms and versions as possible.

    Then that is just added to over the years to include to result in the patched-together mess that you see that is utterly unreadable. You can't simplify it it take things out because something could break. It is
    fragile.

    Why not have a dedicated header file that is the specific to a
    particular version of a C compiler for a given platform? That it can be streamlined for that purpose.

    If someone is maintaining compilers that need to work across a range of targets, then they can have a process that synthesises the header needed
    for a specific configuration.

    (I guess this is something that is harder on Linux because there, many standard headers are not part of a specific C compiler, but are a
    resource shared by all C compilers, or tools that need to process C
    headers.)



    And using #if in the headers is one way to "have a process" that
    "synthesises the header needed for a specific configuration", without
    needing some special code in the compiler, and still makes the header
    available to the user for inspection.

    I suppose the alternative is a separate include hierarchy for EVERY
    combination of configurations, and needing to modify multiple headers to add/fix features.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Janis Papanagnou@21:1/5 to Scott Lurndal on Fri Dec 29 17:27:20 2023
    On 29.12.2023 16:52, Scott Lurndal wrote:
    Janis Papanagnou <janis_papanagnou+ng@hotmail.com> writes:

    I'll just recommend that you follow POSIX and use nanosleep()
    instead.

    When I had read about the various 'sleep' options I decided to use one
    which supports sub-second resolution and with a most simple interface.
    That's why my choice was the simple 'usleep(usec);' even if obsolete
    by POSIX. The nanosleep() is not "very complex", sure, but I'd have to
    litter my code with variables unnecessary in my context, and also the
    advertised "advantages" of this function do not apply in my case.[*]

    You can always define your own usleep:

    LOL, yes. :-)

    I usually want to take what's already there and not re-implement
    every function. What I mean is; in my case I use a function call
    and that is it.


    inline int
    usleep(useconds_t microseconds)
    {
    struct timespec ts;
    ts.tv_sec = microseconds / (useconds_t)1000000;
    ts.tv_nsec = (long)(microseconds % (useconds_t)1000000) * 1000L;
    return nanosleep(&ts, NULL);
    }


    Lowell Gilbert upthread already suggested to use nanosleep() and
    do the multiplications, and he was confident, rightly, that I'm
    able to "figure that out". :-)

    BTW, is 'inline' meanwhile C standard? (I know that from C++ but
    haven't done much C for long now.)

    Janis

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Janis Papanagnou on Fri Dec 29 17:51:13 2023
    On 2023-12-29, Janis Papanagnou <janis_papanagnou+ng@hotmail.com> wrote:
    On 28.12.2023 20:11, Lowell Gilbert wrote:

    To be honest,I didn't actually understand where your problem came from
    in the first place -- I just chose not to bring up more than one point
    at a time. While usleep() is obsolete, it works fine, without any
    feature test macro games, on (as far as I know) all POSIX-ish
    systems. Certainly on recent Ubuntu, the following program compiles and
    runs perfectly well without even any warnings with even the most extreme
    levels of warning enabled:
    [snip code]

    Here's the output of the compiler call with #define _GNU_SOURCE removed

    $ cc -std=c99 -o warn warn.c
    warn.c: In function ‘delay_time’:
    warn.c:368:3: warning: implicit declaration of function ‘strnlen’ [-Wimplicit-function-declaration]
    warn.c: In function ‘main’:
    warn.c:579:5: warning: implicit declaration of function ‘usleep’ [-Wimplicit-function-declaration]

    It compiles, but if I see warnings I nonetheless try to get rid of them.

    This is a good plan because the implicit declaration of a function
    is only a guess, which can easily be wrong. The return type is
    guessed to be int (wrong for strnlen which has size_t). The number
    of arguments and their types are guessed from the call, and could
    be wrong. E.g. if sin is not declared then sin(0) implicity declares
    it as int (int). Implicit declaration has been dropped from ISO C;
    for a number of decades, it was there in support of K&R C programs.
    Compiler vendors can support it as long as they like, but a diagnostic
    is now required when an undeclared function is used.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Bart on Fri Dec 29 17:44:22 2023
    On 2023-12-29, Bart <bc@freeuk.cm> wrote:
    On 29/12/2023 02:35, Lawrence D'Oliveiro wrote:
    On Tue, 26 Dec 2023 16:59:40 +0100, Janis Papanagnou wrote:

    But as got obvious *that* way there had been side-effects and I had to
    put the tag at the beginning of all include files (which astonished me)

    It has always been thus <https://manpages.debian.org/7/feature_test_macros.en.html>:

    NOTE: In order to be effective, a feature test macro must be defined
    before including any header files.

    Last time I looked into the system header files, three decades ago, I
    got repelled by all the #ifdef's, cascaded and nested, a spaghetti code
    of dependencies; I'm astonished it works.

    The whole concept of include files and string-based macro processing is flawed. But that’s C for you ...

    It's not just C's fault. It's the insistence of having have just ONE
    system header that has to work for as many platforms and versions as possible.

    Umm, no; system headers are largely system-specific.

    Then that is just added to over the years to include to result in the patched-together mess that you see that is utterly unreadable. You can't simplify it it take things out because something could break. It is fragile.

    The preprocessing selection in system headers is for the different
    expectations of programs which target different versions of the system interfaces.

    It is to prevent clashes. If an identifier like usleep is hidden,
    it means that the program can use it without triggering a redefinition
    or other error.

    When you compile with, say, -std=c99 (and no other feature selection)
    and include <stdio.h>, that header must not declare fdopen or fileno,
    which are POSIX extensions. That matters if your program happens to
    use these identifiers: which an ISO C program is entitled to do.

    Why not have a dedicated header file that is the specific to a
    particular version of a C compiler for a given platform? That it can be streamlined for that purpose.

    That's predominantly the case, other than (obviously) for libraries
    that are intended to be widely portable.

    If someone is maintaining compilers that need to work across a range of targets, then they can have a process that synthesises the header needed
    for a specific configuration.

    In fact, I remember from long ago that GCC used to have a script,
    as part of its build, that would scan the platform's native headers and generate sanitized versions for GCC. Not sure if that is the case any
    more.

    (I guess this is something that is harder on Linux because there, many standard headers are not part of a specific C compiler, but are a
    resource shared by all C compilers, or tools that need to process C
    headers.)

    Headers on GNU/Linux systems tend to assume GCC. Clang would not be
    usable did it not have GCC compatibility.


    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kenny McCormack@21:1/5 to Scott Lurndal on Fri Dec 29 18:10:37 2023
    In article <iXBjN.109557$p%Mb.36381@fx15.iad>,
    Scott Lurndal <slp53@pacbell.net> wrote:
    ...
    You can always define your own usleep:

    inline int
    usleep(useconds_t microseconds)
    { etc... }

    Or just:

    int usleep(useconds_t microseconds);

    The semicolon at the end is the key.
    --
    The randomly chosen signature file that would have appeared here is more than 4 lines long. As such, it violates one or more Usenet RFCs. In order to remain in compliance with said RFCs, the actual sig can be found at the following URL:
    http://user.xmission.com/~gazelle/Sigs/Pedantic

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Keith Thompson on Fri Dec 29 20:23:25 2023
    On 2023-12-29, Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:
    David Brown <david.brown@hesbynett.no> writes:
    A useful tool that someone might like to write for this particular
    situation would be a partial C preprocessor, letting you choose what
    gets handled. You could choose to expand the code here for, say,
    _GNU_SOURCE and _BSD_SOURCE - any use of these in #ifdef's and
    conditional compilation would be expanded according to whether you
    have defined the symbols or not, leaving an output that is easier to
    understand while keeping most of the pre-processor stuff unchanged (so
    not affecting #includes, and leaving #define'd macros and constants
    untouched and therefore more readable).

    The unifdef tool does some of this. (I haven't used it much.)

    GNU cpp has an option which is something like this: -fdirectives-only.
    It causes it not to expand macros.

    However, I don't think there is any way to prevent the removal of
    comments, which could be a deal breaker.


    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Keith Thompson on Fri Dec 29 20:19:57 2023
    On 2023-12-29, Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:
    Kaz Kylheku <433-929-6894@kylheku.com> writes:
    On 2023-12-29, Bart <bc@freeuk.cm> wrote:
    [...]
    (I guess this is something that is harder on Linux because there, many
    standard headers are not part of a specific C compiler, but are a
    resource shared by all C compilers, or tools that need to process C
    headers.)

    Headers on GNU/Linux systems tend to assume GCC. Clang would not be
    usable did it not have GCC compatibility.

    On my Ubuntu 22.04 system, tcc manages to use the system headers, which
    are mostly provided by glibc. In a quick glance at /usr/include/stdio.h,
    I see some #ifdefs for symbols like __GNUC__ (which is predefined by gcc
    and clang but not by tcc) and __USE_GNU (I haven't bothered to look into
    how that's defined).

    __GNUC__ would be a definite ever-present signal from the compiler.

    __USE_GNU is the internal feature selector corresponding to when you
    use the externally documented _GNU_SOURCE.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to Kaz Kylheku on Fri Dec 29 22:40:47 2023
    On 29/12/2023 20:23, Kaz Kylheku wrote:
    On 2023-12-29, Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:
    David Brown <david.brown@hesbynett.no> writes:
    A useful tool that someone might like to write for this particular
    situation would be a partial C preprocessor, letting you choose what
    gets handled. You could choose to expand the code here for, say,
    _GNU_SOURCE and _BSD_SOURCE - any use of these in #ifdef's and
    conditional compilation would be expanded according to whether you
    have defined the symbols or not, leaving an output that is easier to
    understand while keeping most of the pre-processor stuff unchanged (so
    not affecting #includes, and leaving #define'd macros and constants
    untouched and therefore more readable).

    The unifdef tool does some of this. (I haven't used it much.)

    GNU cpp has an option which is something like this: -fdirectives-only.
    It causes it not to expand macros.

    It flattens include files, processes conditionals, and keeps #defines unchanged.

    However, it turns gcc's sys/stat.h from 300 lines into 3000 lines.

    If I apply it to my stat.h (also my stddef.h which it includes), which
    are 110 lines together, it produces 900 lines. Most of that consists of
    lots of built-in #defines with __ prefixes (each complete with a line
    saying it is built-in).

    When I use my own conversion tool (designed to turn C headers that
    define APIs into declarations in my language), the output is 65 lines.

    The gcc option does not expand typedefs or macros. So if there is a
    declaration using a type which uses both, that is unchanged, which is
    not helpful. (At least not if trying to create bindings for your FFI.)

    gcc with just -E will expand macros but still keep typedefs.

    So, for producing a streamlined standard header, it still leaves a lot
    to be desired. And for trying to flatten layers of macros and typedefs,
    to reveal the underlying types, it's not that great either.

    Purpose-built tools are always better, but dealing with C is not trivial anyway, and dealing with gnu- and gcc-specific features is even harder.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to Keith Thompson on Fri Dec 29 22:18:51 2023
    Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
    Janis Papanagnou <janis_papanagnou+ng@hotmail.com> writes:
    [...]
    BTW, is 'inline' meanwhile C standard? (I know that from C++ but
    haven't done much C for long now.)

    C added inline in C99 (the 1999 edition of the ISO C standard, the same
    one that removed implicit int).

    I think C and C++ have subtly different semantics for inline.

    Mostly related to symbol visibility, IIRC.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to Bart on Sat Dec 30 01:28:17 2023
    On Fri, 29 Dec 2023 22:40:47 +0000, Bart wrote:

    It flattens include files, processes conditionals, and keeps #defines unchanged.

    However, it turns gcc's sys/stat.h from 300 lines into 3000 lines.

    Still, merging all the stuff into fewer files likely means it loads
    faster.

    Includes files are kludge, and all these attempts to improve them are a
    kludge on top of a kludge. This is why better-designed languages have a
    proper module system that solves the whole issue.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to Lawrence D'Oliveiro on Sat Dec 30 01:58:53 2023
    On 30/12/2023 01:28, Lawrence D'Oliveiro wrote:
    On Fri, 29 Dec 2023 22:40:47 +0000, Bart wrote:

    It flattens include files, processes conditionals, and keeps #defines
    unchanged.

    However, it turns gcc's sys/stat.h from 300 lines into 3000 lines.

    Still, merging all the stuff into fewer files likely means it loads
    faster.

    Includes files are kludge, and all these attempts to improve them are a kludge on top of a kludge. This is why better-designed languages have a proper module system that solves the whole issue.

    Flattening hierarchies of include files is not popular in C
    applications. But this option doesn't go far enough anyway.

    Loading files is not a bottleneck in compilation; it is repeated
    processing of huge amounts of content.

    For example a GTK2 user will include "gtk.h", but that involves over
    1000 other #includes, 550 unique, totaling 350K lines of declarations
    across a dozen folders.

    All that processing is repeated for each module that includes it.

    When I applied my tool to GTK2, I found that all information could be
    reduced to a single, flat 25K line representation in my syntax. (My
    language /has/ a module scheme; that interface file is processed once
    per build.)

    It could just as well have generated a 25K line C header file. That's
    93% smaller than all those separate headers, and one #include instead of
    1000.

    So, why don't the vendors of the library do that exercise? End-users
    don't need 550 separate header files.

    By contrast, all 30 or so standard C headers have 3-5K lines in total.
    The problem there is that it is just very messy.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Blue-Maned_Hawk@21:1/5 to Bart on Sat Dec 30 06:51:51 2023
    Bart wrote:

    Why not have a dedicated header file that is the specific to a
    particular version of a C compiler for a given platform? That it can be streamlined for that purpose.

    In fact, this is similar to exactly what Plan 9 did: for include files
    that had arch-dependent content, it'd have a separate version of them for
    each arch, with an arch's versions of headers like these all stored in
    their own directory.



    --
    Blue-Maned_Hawk│shortens to Hawk│/blu.mɛin.dʰak/│he/him/ his/himself/Mr.
    blue-maned_hawk.srht.site
    Of course, further simplicifications would be possible by killing off all
    but one or two arches.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to Bart on Sat Dec 30 20:12:07 2023
    On Fri, 29 Dec 2023 13:31:37 +0000, Bart wrote:

    Why not have a dedicated header file that is the specific to a
    particular version of a C compiler for a given platform? That it can be streamlined for that purpose.

    The GCC compilers already work this way. For example, on my Debian
    system I have directories /usr/lib/gcc/x86_64-linux-gnu/12/ and /usr/lib/gcc/x86_64-linux-gnu/13/. And when I look to see what
    packages have put stuff in them, I find quite a lot:

    ldo@theon:~> dpkg-query -S /usr/lib/gcc/x86_64-linux-gnu/12
    libgcc-12-dev:amd64, gcc-12, libgfortran-12-dev:amd64, g++-12, cpp-12, gfortran-12, libstdc++-12-dev:amd64, gnat-12: /usr/lib/gcc/x86_64-linux-gnu/12
    ldo@theon:~> dpkg-query -S /usr/lib/gcc/x86_64-linux-gnu/13
    libstdc++-13-dev:amd64, libgcc-13-dev:amd64, libobjc-13-dev:amd64, gfortran-13, gcc-13, libgfortran-13-dev:amd64: /usr/lib/gcc/x86_64-linux-gnu/13

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to BGB on Sat Dec 30 23:21:22 2023
    On Sat, 30 Dec 2023 16:16:07 -0600, BGB wrote:

    Many people's assumption, that one needs "one true C compiler" (with
    people debating GCC cs Clang, mostly ignoring any other possibilities)
    and then using the same C library, etc, everywhere, may potentially
    actually be doing the world a disservice.

    Actually, we have quite a choice of C libraries, even on the same Linux platform. I once used one of them (I think it was musl) just to prove I
    could build a small executable† that would run in just 20kB of RAM on a modern 64-bit Linux machine.

    †It did nothing more than take an integer command-line argument and sleep
    for that number of seconds. That way the process would hang around long
    enough for me to confirm how much/little RAM it was using.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to Bart on Sun Dec 31 01:36:10 2023
    On Sat, 30 Dec 2023 01:58:53 +0000, Bart wrote:

    So, why don't the vendors of the library do that exercise?

    Maybe because most of the “vendors” of proprietary libraries have gone extinct. What we have now is “developers” and “contributors” to open- source projects. And if you have a bright idea for how they can do things better, you are free to contribute it.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to Lawrence D'Oliveiro on Sun Dec 31 02:06:37 2023
    On 31/12/2023 01:36, Lawrence D'Oliveiro wrote:
    On Sat, 30 Dec 2023 01:58:53 +0000, Bart wrote:

    So, why don't the vendors of the library do that exercise?

    Maybe because most of the “vendors” of proprietary libraries have gone extinct. What we have now is “developers” and “contributors” to open- source projects. And if you have a bright idea for how they can do things better, you are free to contribute it.

    I have plenty of ideas, but people are generally not interested. Even if
    some were, how do you persuade the creators of 100s of libraries to do
    things differently?

    So I use my ideas in my own languages and in my own compilers, including
    one for C. There, the standard headers /are/ specific to that platform, although I do only support one. (If another comes along, it will have
    its own set!)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to BGB on Sun Dec 31 01:34:28 2023
    On Sat, 30 Dec 2023 19:14:55 -0600, BGB wrote:

    Note that (unlike ELF on Linux), it is not currently possible to
    directly share global variables across DLL boundaries.

    Windows is broken in so many ways ... when you achieve something, it’s
    like getting a bear to dance: it’s not that it dances badly, but that it dances at all.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to Lawrence D'Oliveiro on Sun Dec 31 02:18:25 2023
    On 31/12/2023 01:34, Lawrence D'Oliveiro wrote:
    On Sat, 30 Dec 2023 19:14:55 -0600, BGB wrote:

    Note that (unlike ELF on Linux), it is not currently possible to
    directly share global variables across DLL boundaries.

    Windows is broken in so many ways ... when you achieve something, it’s
    like getting a bear to dance: it’s not that it dances badly, but that it dances at all.
    I think that that limitation was specific to BGB's handling of DLLs; it
    was not made clear.

    If it is an actual issue on Windows, then someone would first have to
    explain what it means, and why it happens, as I can't see it.

    Each DLL exports certain symbols such as the addresses of functions and variables. So no reason you can't access a variable exported from any
    DLL, unless perhaps multiple instances of the same DLL have to share the
    same static data, but that sounds very unlikely, as little would work.

    Windows is broken in so many ways

    Other examples? I find rather the opposite. For example I did like this
    remark of BGB's:

    Then again, not like there is much hope of getting stuff ported when
    in general, nearly all of the Linux software seems to take a "you need
    the whole jungle to get a single banana" approach to software
    dependencies (and then much of the code tends to be riddled with GCC'isms)

    This somes my experience of software originating in Linux. This is why
    Windows had to acquire CYGWIN then MSYS then WSL. You can't build the
    simplest program without involving half of Linux.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Scott Lurndal on Sun Dec 31 14:40:26 2023
    On 29/12/2023 23:18, Scott Lurndal wrote:
    Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
    Janis Papanagnou <janis_papanagnou+ng@hotmail.com> writes:
    [...]
    BTW, is 'inline' meanwhile C standard? (I know that from C++ but
    haven't done much C for long now.)

    C added inline in C99 (the 1999 edition of the ISO C standard, the same
    one that removed implicit int).

    I think C and C++ have subtly different semantics for inline.

    Mostly related to symbol visibility, IIRC.

    In C, the "inline" qualifier is pretty much a message from the
    programmer saying "I think the resulting code would be more efficient if
    this is inlined by the optimiser". Optimising compilers mostly ignore
    it. In C++, the "inline" qualifier is a message from the programmer
    saying "I might define this thing in multiple translation units, and I
    promise I'll do it the same way each time".

    You are most likely to see issues if some calls can't be inlined (or are
    not inlined by the compiler). If you have code that might need to be
    compiled as C or C++ (such as if the function is in a header), the best
    method is to declare it "static inline" rather than plain "inline". And
    in a C or C++ implementation file, any function that you'd consider
    declaring "inline" is likely to be "static" anyway. (Or in an anonymous namespace in C++, which amounts to the same thing.) "static inline"
    works, AFAIK, in exactly the same way in C and C++.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Bart on Sun Dec 31 16:25:08 2023
    On 29/12/2023 23:40, Bart wrote:
    On 29/12/2023 20:23, Kaz Kylheku wrote:
    On 2023-12-29, Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:
    David Brown <david.brown@hesbynett.no> writes:
    A useful tool that someone might like to write for this particular
    situation would be a partial C preprocessor, letting you choose what
    gets handled.  You could choose to expand the code here for, say,
    _GNU_SOURCE and _BSD_SOURCE - any use of these in #ifdef's and
    conditional compilation would be expanded according to whether you
    have defined the symbols or not, leaving an output that is easier to
    understand while keeping most of the pre-processor stuff unchanged (so >>>> not affecting #includes, and leaving #define'd macros and constants
    untouched and therefore more readable).

    The unifdef tool does some of this.  (I haven't used it much.)

    GNU cpp has an option which is something like this: -fdirectives-only.
    It causes it not to expand macros.

    It flattens include files, processes conditionals, and keeps #defines unchanged.

    However, it turns gcc's sys/stat.h from 300 lines into 3000 lines.

    If I apply it to my stat.h (also my stddef.h which it includes), which
    are 110 lines together, it produces 900 lines. Most of that consists of
    lots of built-in #defines with __ prefixes (each complete with a line
    saying it is built-in).

    When I use my own conversion tool (designed to turn C headers that
    define APIs into declarations in my language), the output is 65 lines.

    The gcc option does not expand typedefs or macros. So if there is a declaration using a type which uses both, that is unchanged, which is
    not helpful. (At least not if trying to create bindings for your FFI.)

    gcc with just -E will expand macros but still keep typedefs.


    Note that typedefs are part of the core C language, not the
    preprocessor, so there could not possibly be a cpp option to do anything
    with typedefs (the phrase "expand typedefs" is entirely wrong).

    I realise that you (and possibly others) might find it useful for a tool
    to replace typedef identifiers with their definitions, but it could only
    be done for some cases, and is not as simple as macro substitution.

    So, for producing a streamlined standard header, it still leaves a lot
    to be desired. And for trying to flatten layers of macros and typedefs,
    to reveal the underlying types, it's not that great either.

    Purpose-built tools are always better, but dealing with C is not trivial anyway, and dealing with gnu- and gcc-specific features is even harder.



    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to David Brown on Sun Dec 31 15:45:26 2023
    On 31/12/2023 15:25, David Brown wrote:
    On 29/12/2023 23:40, Bart wrote:
    On 29/12/2023 20:23, Kaz Kylheku wrote:
    On 2023-12-29, Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:
    David Brown <david.brown@hesbynett.no> writes:
    A useful tool that someone might like to write for this particular
    situation would be a partial C preprocessor, letting you choose what >>>>> gets handled.  You could choose to expand the code here for, say,
    _GNU_SOURCE and _BSD_SOURCE - any use of these in #ifdef's and
    conditional compilation would be expanded according to whether you
    have defined the symbols or not, leaving an output that is easier to >>>>> understand while keeping most of the pre-processor stuff unchanged (so >>>>> not affecting #includes, and leaving #define'd macros and constants
    untouched and therefore more readable).

    The unifdef tool does some of this.  (I haven't used it much.)

    GNU cpp has an option which is something like this: -fdirectives-only.
    It causes it not to expand macros.

    It flattens include files, processes conditionals, and keeps #defines
    unchanged.

    However, it turns gcc's sys/stat.h from 300 lines into 3000 lines.

    If I apply it to my stat.h (also my stddef.h which it includes), which
    are 110 lines together, it produces 900 lines. Most of that consists
    of lots of built-in #defines with __ prefixes (each complete with a
    line saying it is built-in).

    When I use my own conversion tool (designed to turn C headers that
    define APIs into declarations in my language), the output is 65 lines.

    The gcc option does not expand typedefs or macros. So if there is a
    declaration using a type which uses both, that is unchanged, which is
    not helpful. (At least not if trying to create bindings for your FFI.)

    gcc with just -E will expand macros but still keep typedefs.


    Note that typedefs are part of the core C language, not the
    preprocessor, so there could not possibly be a cpp option to do anything
    with typedefs (the phrase "expand typedefs" is entirely wrong).

    I realise that you (and possibly others) might find it useful for a tool
    to replace typedef identifiers with their definitions, but it could only
    be done for some cases, and is not as simple as macro substitution.

    Take this program, which uses two nested typedefs and one macro:

    typedef short T;
    typedef T U;
    #define V U

    typedef struct R {
    V a, b, c;
    } S;

    Passed through 'gcc -E', it manages to expand the V in the struct with
    U. (-fdirectives-only doesn't even do that).

    So what are the types of 'a, b, c'? Across 1000s of line of code, they
    may need tracking down. At least, for someone not using your super-duper
    tools.

    If I use my compiler with 'mcc -mheaders', I get an output file that
    includes this:

    record R = $caligned
    i16 a
    i16 b
    i16 c
    end

    It gives all the information I might need. Including the fact that it
    uses default C alignment rules.

    Notice however the name of the record is R not S; here it needs a
    struct-tag to avoid an anonymous name. The typedef name is harder to use
    as it is replaced early on in compilation.

    Here, I'm effectively expanding a typedef. The output could just as
    equally have been C source code.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to BGB on Sun Dec 31 15:26:05 2023
    On 31/12/2023 05:46, BGB wrote:
    On 12/30/2023 8:18 PM, Bart wrote:
    On 31/12/2023 01:34, Lawrence D'Oliveiro wrote:
    On Sat, 30 Dec 2023 19:14:55 -0600, BGB wrote:

    Note that (unlike ELF on Linux), it is not currently possible to
    directly share global variables across DLL boundaries.

    Windows is broken in so many ways ... when you achieve something, it’s >>> like getting a bear to dance: it’s not that it dances badly, but that it >>> dances at all.
    I think that that limitation was specific to BGB's handling of DLLs;
    it was not made clear.


    Yes.

    In my case (for my custom target) I am using a modified version of
    PE/COFF (with some tweaks, *), but it has the issue that there is not currently (any) mechanism for sharing variables across DLL boundaries
    apart from getter/setter functions or similar.

    I find PE/COFF format a nightmare. I did eventually get my tools to
    generate first OBJ then EXE files. But when it came to DLL, there was
    something wrong in the files I generated that I couldn't fix.

    So for a couple of years, I created my own shared library format,
    utterly different from PE+, and about 10 times simpler.

    The shared library files were called ML, and I extended it to standalone executables called MX files.

    However ML libraries could only be used from my languages, and MX files
    needed a small conventional EXE loader to get started.

    Eventually I fixed the problems with DLL. (Partly it was that my
    generated code wasn't fully position-independent and only ran in
    low-memory below 2GB, but there was also a bug in the base-reloc tables.)

    Now, sadly, I will probably drop my ML/MX files, even though my MLs have advantages over DLLs. (Eg. they have the same environment as the host
    apps, so that they can share things like pointers to allocated memory
    and file handles. With DLL, a pointer malloc-ed in the host cannot be
    freed within the DLL and vice versa.)


    Each DLL exports certain symbols such as the addresses of functions
    and variables. So no reason you can't access a variable exported from
    any DLL, unless perhaps multiple instances of the same DLL have to
    share the same static data, but that sounds very unlikely, as little
    would work.


    Much past roughly Win9x or so, it has been possible to use "__declspec(dllimport)" on global variables in Windows (in an earlier
    era, it was not possible to use the __declspec's, but instead necessary
    to manage DLL import/exports by writing out lists in ".DEF" files).

    It isn't entirely transparent, but yes, on actual Windows, it is very
    much possible to share global variables across DLL boundaries.


    Just, this feature is not (yet) supported by my compiler. Personally, I
    don't see this as a huge loss (even if it did work; I personally see it
    as "poor coding practice").

    This is a language issue. Or, in C, it is compiler related.

    I've never been quite sure how you tell a C compiler to export a certain
    symbol when creating a DLL. Sometimes it just works; I think it just
    exports everything that is not static (it may depend on a compiler
    option too).

    And some compilers may need this __declspec business, but I've never
    bothered with it.

    Mine just exports all not-static names. So this program:

    int abc;
    static int def;

    void F(void) {}
    static void G(void) {}

    if compiled as: 'mcc -dll prog', produces a file prog.dll which, if I
    dump it, shows this export table:

    Export Directory

    0 00000000 0 Fun F
    1 00000000 0 Var abc

    (There's something in it that distinguishes functions from variables,
    but I can't remember the details.)

    In any case, in C it can be hit and miss. In my own language, it is more controlled: I used an 'export' prefix to export symbols from a program.

    (It also conventiently creates interface files to be able to use the DLL library from a program. The equivalent of prog.h for my example
    containing the API needed to use it. Rolling that out to C is not
    practical however as my 'export' applies also to things like types and
    enums.)

    This [somes] my experience of software originating in Linux. This is why
    Windows had to acquire CYGWIN then MSYS then WSL. You can't build the
    simplest program without involving half of Linux.

    Yes, and it is really annoying sometimes.


    For the most part, Linux software builds and works fairly well... if one
    is using a relatively mainline and relatively up-to-date Linux distro...


    But, if one is not trying to build in or for a typical Linux style / GNU based userland; it is straight up pain...

    Like, typically either the "./configure" script is going to go down in a crap-storm of error messages (say, if the shell is not "bash", or some commands it tries to use are absent or don't accept the same
    command-line arguments, etc); or libraries are going to be missing; or
    the build just ends up dying due to compiler errors (say, which headers
    exist are different, or their contents are different, ...).

    ./configure is an abomination anyway; I've seen 30,000-line scripts
    which take forever to run, and test things like whether 'printf' is
    supported.

    But the biggest problem with them is when someone expects a Windows user
    to use that same build process. Of course, ./configure is a Bash script
    using Linux utilities.

    It's like someone providing a .BAT file and expecting Linux users to do something with it.


    Within the code itself, it often doesn't take much looking to find one of:
      Pointer arithmetic on "void *";
      Various GCC specific "__attribute__((whatever))" modifiers;
      Blobs of GAS specific inline ASM;
      ...


    Whereas in more cross-platform code, one will usually find stuff like:
    #ifdef __GNUC__
      ... GCC specific stuff goes here ...
    #endif
    #ifdef _MSC_VER
      ... MSVC specific stuff goes here ...
    #endif
    ...

    Those conditional blocks never list my compiler, funnily enough. (#ifdef __MCC__ will do it.)


    My compiler uses sort of an intermediate C dialect, but is more
    conservative by default in some areas, such as treating things like TBAA
    as "opt-in" features, rather than "opt-out", ...

    Though, I did designate various cases as "no consistent or sensible
    behavior exists", so "whatever happens, happens". Separating out cases
    that are "technically undefined, but has a conventionally accepted
    behavior" (such as using pointer casts for type punning, etc), vs "no accepted behavior and any behavior that may result is effectively a dice roll..." (a lot of cases involving out-of-bounds memory access, etc).

    Some amount of the extensions have more MSVC-like syntax (albeit the ASM syntax itself is more derived from GAS style ASM syntax than Intel style syntax). Though, in particular, it is derived from "GAS SuperH" (which
    falls into a similar category as M68K and PDP-11 ASM syntax):
      R4  //this is a register
      @R4  //memory with address in R4
      (R4)  //same as @R4
      (R4,32)  //displacement
      32(R4)   //same as (R4,32)

    My C compiler is from 2017. Eventually I decided it was too
    non-conforming and buggy to be a serious tool. It became a private one
    (for example one special feature is being able to turn library APIs
    defined as C headers, into bindings for either of my own languages,
    although that can only do 90% of the work).

    Last autumn I upgraded it with a new backend. But I also got rid of all experimental features I'd played with.

    It may be a poor compiler but CLI-wise it's much better than gcc which continues to be a pain to use (still generating a.exe), and has odd bugs
    in its CLI.

    So, mine is still satisfying to have. I just call it a C-subset or
    C-dialect compiler to get over the non-conformance. But for building my
    own C code, it's my first choice.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Bart on Sun Dec 31 17:26:31 2023
    On 2023-12-31, Bart <bc@freeuk.cm> wrote:
    and file handles. With DLL, a pointer malloc-ed in the host cannot be
    freed within the DLL and vice versa.)

    What??? All DLLs are in the same address space. malloc and free are
    sister functions that typically live in the same DLL, and don't
    care what calls them.

    Maybe you're referring to one DLL's free not being able to handle
    pointers produced by another DLL's malloc.

    (That's not a problem caused by the DLL mechanism itself and would not
    go away if those were somehow statically linked together.)

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From James Kuyper@21:1/5 to David Brown on Sun Dec 31 12:43:52 2023
    On 12/31/23 08:40, David Brown wrote:
    ...
    In C, the "inline" qualifier is pretty much a message from the
    programmer saying "I think the resulting code would be more efficient
    if this is inlined by the optimiser".

    Actually, what the C standard says is "Making a function an
    inline function suggests that calls to the function be as fast as
    possible". The standard does not specify how this is to be achieved, it
    merely imposes some requirements that constrain how it could be
    achieved. Inlining a function call is just one way to do that.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to Bart on Sun Dec 31 18:33:58 2023
    Bart <bc@freeuk.cm> writes:
    On 31/12/2023 01:36, Lawrence D'Oliveiro wrote:
    On Sat, 30 Dec 2023 01:58:53 +0000, Bart wrote:

    So, why don't the vendors of the library do that exercise?

    Maybe because most of the “vendors” of proprietary libraries have gone >> extinct. What we have now is “developers” and “contributors” to open-
    source projects. And if you have a bright idea for how they can do things
    better, you are free to contribute it.

    I have plenty of ideas, but people are generally not interested.

    Perhaps they are not very good ideas, then....

    Frankly, your obsession with header files is puzzling. 99.9%
    percent of C/C++ programmers don't care.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to BGB on Sun Dec 31 18:37:52 2023
    BGB <cr88192@gmail.com> writes:
    On 12/30/2023 12:51 AM, Blue-Maned_Hawk wrote:
    Bart wrote:

    Why not have a dedicated header file that is the specific to a
    particular version of a C compiler for a given platform? That it can be
    streamlined for that purpose.

    In fact, this is similar to exactly what Plan 9 did: for include files
    that had arch-dependent content, it'd have a separate version of them for
    each arch, with an arch's versions of headers like these all stored in
    their own directory.


    Yeah, we don't actually need big monolithic C libraries, nor big
    monolithic C compilers, ...

    What monolithic C libraries are you referring to? My application
    links with a several dozen libraries, some at startup, some dynamically
    at run-time. Nothing monolithic there. POSIX only requires that
    -lc include the necessarily functionality for the POSIX API, it
    doesn't require that the implementation use a single library/shared
    object at run-time.


    Both GCC and CLANG do just as you describe here:

    Though, it seems like a different kind of strategy could be possible:
    Split compilers into frontends and backends (which may exist as >semi-independent projects).

    Frontend deals with source languages, compiles down to a common IR (with
    the IR taking the place of object files);
    Backend compiles this to the actual machine code, and does any linking, >before emitting the actual binary.

    See LLVM.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to Bart on Sun Dec 31 18:40:20 2023
    Bart <bc@freeuk.cm> writes:
    On 31/12/2023 15:25, David Brown wrote:

    I realise that you (and possibly others) might find it useful for a tool
    to replace typedef identifiers with their definitions, but it could only
    be done for some cases, and is not as simple as macro substitution.

    Take this program, which uses two nested typedefs and one macro:

    typedef short T;
    typedef T U;
    #define V U

    typedef struct R {
    V a, b, c;
    } S;

    Passed through 'gcc -E', it manages to expand the V in the struct with
    U. (-fdirectives-only doesn't even do that).

    So what are the types of 'a, b, c'? Across 1000s of line of code, they
    may need tracking down. At least, for someone not using your super-duper >tools.

    Perhaps you need to use better names than V, a, b, and c.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Bart on Sun Dec 31 18:44:41 2023
    On 2023-12-31, Bart <bc@freeuk.cm> wrote:
    Take this program, which uses two nested typedefs and one macro:

    typedef short T;
    typedef T U;
    #define V U

    typedef struct R {
    V a, b, c;
    } S;

    Passed through 'gcc -E', it manages to expand the V in the struct with
    U. (-fdirectives-only doesn't even do that).

    So what are the types of 'a, b, c'? Across 1000s of line of code, they
    may need tracking down. At least, for someone not using your super-duper tools.

    Super duper tools like, oh, Exuberant Ctags from 2011, packaged in Ubuntu:

    $ ctags --version
    Exuberant Ctags 5.9~svn20110310, Copyright (C) 1996-2009 Darren Hiebert
    Addresses: <dhiebert@users.sourceforge.net>, http://ctags.sourceforge.net
    Optional compiled features: +wildcards, +regex

    We run that and then super-duper editor Vim will use the tags
    file to jump to the definition of V, and of U.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to Kaz Kylheku on Sun Dec 31 19:37:59 2023
    On 31/12/2023 18:44, Kaz Kylheku wrote:
    On 2023-12-31, Bart <bc@freeuk.cm> wrote:
    Take this program, which uses two nested typedefs and one macro:

    typedef short T;
    typedef T U;
    #define V U

    typedef struct R {
    V a, b, c;
    } S;

    Passed through 'gcc -E', it manages to expand the V in the struct with
    U. (-fdirectives-only doesn't even do that).

    So what are the types of 'a, b, c'? Across 1000s of line of code, they
    may need tracking down. At least, for someone not using your super-duper
    tools.

    Super duper tools like, oh, Exuberant Ctags from 2011, packaged in Ubuntu:

    $ ctags --version
    Exuberant Ctags 5.9~svn20110310, Copyright (C) 1996-2009 Darren Hiebert
    Addresses: <dhiebert@users.sourceforge.net>, http://ctags.sourceforge.net
    Optional compiled features: +wildcards, +regex

    We run that and then super-duper editor Vim will use the tags
    file to jump to the definition of V, and of U.

    Actually ctags appears to be also part of Windows.

    I've played with it and the results are interesting.

    I've recently created something vaguely similar for my language, invoked
    via the compiler (mm -getst prog) which scans all modules and produces a
    list of references (currently only top-level names) to help find them
    from my IDE.

    I don't know how useful ctags might be in helping extract bindings from
    a complex set of API headers, but I've got that covered.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to Kaz Kylheku on Sun Dec 31 19:23:23 2023
    On 31/12/2023 17:26, Kaz Kylheku wrote:
    On 2023-12-31, Bart <bc@freeuk.cm> wrote:
    and file handles. With DLL, a pointer malloc-ed in the host cannot be
    freed within the DLL and vice versa.)

    What??? All DLLs are in the same address space. malloc and free are
    sister functions that typically live in the same DLL, and don't
    care what calls them.

    Maybe you're referring to one DLL's free not being able to handle
    pointers produced by another DLL's malloc.

    I'm referring to the possibilty that, if host and DLL both import say msvcrt.dll, that each may have its own instance of msvcrt.dll, with its
    own static data. That would also be the case with two DLLs.

    But I can't reproduce the kind of error that would cause.

    So I was either mistaken, or it's been fixed in the last decade, or
    maybe my original test failed for other reasons, eg. because of
    statically linked libraries not shared ones, or mixed compilers (and do libraries) were used.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to Kaz Kylheku on Sun Dec 31 20:00:29 2023
    Kaz Kylheku <433-929-6894@kylheku.com> writes:
    On 2023-12-31, Bart <bc@freeuk.cm> wrote:
    Take this program, which uses two nested typedefs and one macro:

    typedef short T;
    typedef T U;
    #define V U

    typedef struct R {
    V a, b, c;
    } S;

    Passed through 'gcc -E', it manages to expand the V in the struct with
    U. (-fdirectives-only doesn't even do that).

    So what are the types of 'a, b, c'? Across 1000s of line of code, they
    may need tracking down. At least, for someone not using your super-duper
    tools.

    Super duper tools like, oh, Exuberant Ctags from 2011, packaged in Ubuntu:

    $ ctags --version
    Exuberant Ctags 5.9~svn20110310, Copyright (C) 1996-2009 Darren Hiebert
    Addresses: <dhiebert@users.sourceforge.net>, http://ctags.sourceforge.net
    Optional compiled features: +wildcards, +regex

    We run that and then super-duper editor Vim will use the tags
    file to jump to the definition of V, and of U.

    Or the super-duper tool called cscope.

    $ find . -name '*.[chCHsS]*' -print |grep -v .svn |sort -u | cscope -q -b -k -f csc -i -

    Which the super-duper VIM editor will leverage for both
    tags support and cscope query support.

    $ vim
    :cs add csc
    :cs f 1 main

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Richard Damon@21:1/5 to Bart on Sun Dec 31 14:46:47 2023
    On 12/31/23 2:23 PM, Bart wrote:
    On 31/12/2023 17:26, Kaz Kylheku wrote:
    On 2023-12-31, Bart <bc@freeuk.cm> wrote:
    and file handles. With DLL, a pointer malloc-ed in the host cannot be
    freed within the DLL and vice versa.)

    What??? All DLLs are in the same address space. malloc and free are
    sister functions that typically live in the same DLL, and don't
    care what calls them.

    Maybe you're referring to one DLL's free not being able to handle
    pointers produced by another DLL's malloc.

    I'm referring to the possibilty that, if host and DLL both import say msvcrt.dll, that each may have its own instance of msvcrt.dll, with its
    own static data. That would also be the case with two DLLs.

    But I can't reproduce the kind of error that would cause.

    So I was either mistaken, or it's been fixed in the last decade, or
    maybe my original test failed for other reasons, eg. because of
    statically linked libraries not shared ones, or mixed compilers (and do libraries) were used.



    Sounds like either something used a static library or forced the system
    to link in two different copies of msvcrt.dll (perhaps by incorrectly
    including their own in an off-path directory)

    This wasn't that uncommon of a problem until people got burned enough by
    not following "the rules" that they started to do it right.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to David Brown on Sun Dec 31 21:44:23 2023
    On Sun, 31 Dec 2023 16:25:08 +0100, David Brown wrote:

    I realise that you (and possibly others) might find it useful for a tool
    to replace typedef identifiers with their definitions, but it could only
    be done for some cases, and is not as simple as macro substitution.

    String-based macros are nothing but trouble. Typedefs are scoped, string
    macros are not.

    If you want to see the right way to do macros, look at LISP, where they
    are token-based, and much more robust as as result. I think they even
    manage to apply scoping rules to macro definitions as well.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to Bart on Sun Dec 31 21:41:41 2023
    On Sun, 31 Dec 2023 02:06:37 +0000, Bart wrote:

    I have plenty of ideas, but people are generally not interested.

    Lessig’s Law: “the one who writes the code, makes the rules”.

    Nobody cares about “ideas”. “Ideas” are a dime a dozen. What matters is execution. Put your “idea” into working code, and that will prove your point.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to Bart on Sun Dec 31 22:00:49 2023
    On 31/12/2023 19:37, Bart wrote:
    On 31/12/2023 18:44, Kaz Kylheku wrote:
    On 2023-12-31, Bart <bc@freeuk.cm> wrote:
    Take this program, which uses two nested typedefs and one macro:

         typedef short T;
         typedef T U;
         #define V U

         typedef struct R {
             V a, b, c;
         } S;

    Passed through 'gcc -E', it manages to expand the V in the struct with
    U. (-fdirectives-only doesn't even do that).

    So what are the types of 'a, b, c'? Across 1000s of line of code, they
    may need tracking down. At least, for someone not using your super-duper >>> tools.

    Super duper tools like, oh, Exuberant Ctags from 2011, packaged in
    Ubuntu:

    $ ctags --version
    Exuberant Ctags 5.9~svn20110310, Copyright (C) 1996-2009 Darren Hiebert
       Addresses: <dhiebert@users.sourceforge.net>,
    http://ctags.sourceforge.net
       Optional compiled features: +wildcards, +regex

    We run that and then super-duper editor Vim will use the tags
    file to jump to the definition of V, and of U.

    Actually ctags appears to be also part of Windows.

    That's not the case, it just happened to be bundled with Windows.

    It's a 1.25MB file so fairly 'super', compared with my stuff. The option
    I said I'd added to my compiler was about 70 lines of extra code, but it
    looks to be a bit more sophisticated as the compiler has done the hard
    work, and it just dumps parts of the symbol table.

    We run that and then super-duper editor Vim will use the tags
    file to jump to the definition of V, and of U.

    How does Vim know where in the file to look? The TAGS files I've managed
    to produce doesn't have that info, and I can't see anything in the help
    to add it.

    How do you get it to look inside header files, or do those have to be
    submitted manually?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Tim Rentsch@21:1/5 to Spiros Bousbouras on Sun Dec 31 14:45:46 2023
    Spiros Bousbouras <spibou@gmail.com> writes:

    [... on #define _BSD_SOURCE, etc ...]

    By the way , this kind of question is more appropriate for comp.unix.programmer .

    For what it's worth, I found the discussion valuable. I
    look at comp.unix.programmer only sporadically, so I'm
    glad it was posted here.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to Bart on Sun Dec 31 23:46:08 2023
    On Sun, 31 Dec 2023 02:18:25 +0000, Bart wrote:

    This somes my experience of software originating in Linux. This is why Windows had to acquire CYGWIN then MSYS then WSL. You can't build the simplest program without involving half of Linux.

    On Linux, we have package managers that only pull in the needed
    dependencies. Windows just seems actively hostile to that kind of infrastructure management. If you meant “you can’t build the simplest program *on Windows* without involving half of Linux” ... well, that’s
    just a reflection on the deficiencies of Windows. On Linux, you already
    have the *whole* of Linux to start with.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Tim Rentsch@21:1/5 to Janis Papanagnou on Sun Dec 31 16:07:35 2023
    Janis Papanagnou <janis_papanagnou+ng@hotmail.com> writes:

    This is a CPP question that arose last month. It's not about an
    actual issue with the software, just out of curiosity and to be sure
    it works reliable (it seemingly does).

    In a C99 program on Linux (Ubuntu) I intended to use usleep() and
    then also strnlen().

    When I added usleep() and its include file I got an error and was
    asked to define the CPP tag '_BSD_SOURCE'. I did so, and because I
    wanted side effects of that tag kept as small as possible I
    prepended it just before the respective #include and put it at the
    end of my #include list

    ...other #includes...
    #define _BSD_SOURCE
    #include <unistd.h>

    But as got obvious *that* way there had been side-effects and I
    had to put the tag at the beginning of all include files (which
    astonished me)

    #define _BSD_SOURCE
    #include <unistd.h>
    ...other #includes here...

    For the strnlen() function I needed another CPP tag, '_GNU_SOURCE'.
    So now I have both CPP tag definitions before the includes

    I second the recommendations of Lowell Gilbert and others not to
    define _BSD_SOURCE or _GNU_SOURCE (especially not _GNU_SOURCE)
    but instead seek alternatives, which are readily available for
    the two functionalities being sought in this case.

    #define _GNU_SOURCE /* necessary for strnlen() in string.h */
    #define _BSD_SOURCE /* necessary for usleep() in unistd.h */
    ...all #includes here...

    For strnlen(), put an inline definition in a header file:

    #ifndef HAVE_strnlen_dot_h_header
    #define HAVE_strnlen_dot_h_header

    #include <stddef.h>

    static inline size_t
    strnlen( const char *s, size_t n ){
    extern void *memchr( const void *, int, size_t );
    const char *p = memchr( s, 0, n );
    return p ? (size_t){ p-s } : n;
    }

    #include <string.h>

    #endif

    Disclaimer: this code has been compiled but not tested.

    (If you want you could call this header file "string.h" and do
    a #include "string.h". I'm not advocating doing that, just
    pointing it out as an alternative. I expect some people like the
    idea, and others dislike it, and I don't want to get involved in
    a style war.)

    For usleep(), define an alternate function usnooze(), to be used
    in place of usleep(). In header file usnooze.h:

    extern int usnooze( unsigned long long );

    In source file usnooze.c:

    #ifndef _POSIX_C_SOURCE
    #define _POSIX_C_SOURCE 199309L
    #endif

    #include "usnooze.h"

    #include <errno.h>
    #include <time.h>

    int
    usnooze( unsigned long long snooze_time_in_microseconds ){
    typedef unsigned long long ULL;
    typedef struct timespec TS;

    ULL seconds = snooze_time_in_microseconds / 1000000;
    ULL nanoseconds = snooze_time_in_microseconds % 1000000 * 1000;
    TS need = { .tv_sec = seconds, .tv_nsec = nanoseconds };
    TS more;
    int rc;
    unsigned most = 1000;

    while( errno = 0, rc = nanosleep( & need, & more ), rc != 0 ){
    if( errno != EINTR || most-- == 0 ) return -1;
    need = more;
    }

    return 0;
    }

    The point of putting the definition of usnooze() in a separate file
    is so the needed #define _POSIX_C_SOURCE doesn't contaminate any
    more program source than it has to.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to Chris M. Thomasson on Mon Jan 1 00:12:14 2024
    On Sun, 31 Dec 2023 13:51:28 -0800, Chris M. Thomasson wrote:

    On 12/31/2023 1:44 PM, Lawrence D'Oliveiro wrote:

    If you want to see the right way to do macros, look at LISP, where they
    are token-based, and much more robust as as result. I think they even
    manage to apply scoping rules to macro definitions as well.

    Fwiw, check this out:

    [dead project with spam link omitted]

    Looks long defunct, which is just as well.

    I really did give string-based macros a try. I thought it was just down to deficiencies in the C/C++ preprocessor. So I did some work with GNU M4,
    which is about as powerful a string macro processor as you could want. And instead of being better, it was just as troublesome and fragile.

    The watchword now is “homoiconicity”. This means that the program has an AST representation in terms of objects in the language itself. LISP has
    this very naturally. And all your macro-type manipulations are done on
    this intermediate representation, not on the original program source.

    I found a way to do it Python, too, which I used for this project <https://gitlab.com/ldo/seaskirt> to generate both synchronous and
    asynchronous versions of the same API classes from common code.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to Bart on Mon Jan 1 02:00:09 2024
    On Mon, 1 Jan 2024 01:33:38 +0000, Bart wrote:

    On 31/12/2023 23:46, Lawrence D'Oliveiro wrote:

    If you meant “you can’t build the simplest
    program *on Windows* without involving half of Linux” ... well, that’s >> just a reflection on the deficiencies of Windows. On Linux, you already
    have the *whole* of Linux to start with.

    And developers feel it necessary to USE everything that it provides!

    It’s called “code reuse”. A well-designed package-management system just makes it so much easier to do.

    I've never managed to build the GMP library on Windows for example (it
    only comes as source code), because it requires that 30,000-line bash
    script which in turn needs sed and m4 and all the rest.

    Why? It's a numeric library. Why should it be dependent on OS?

    Those are just standard file-manipulation tools that any decent OS should provide.

    Or maybe Linux developers NEED all that hand-holding and have no idea
    how to build using a bare compiler.

    If only you could do that on Windows ... but no. Look at all the C runtime stuff needed just to build a simple “Hello World” program ... because Windows automatically assumes that every program must have a GUI.

    Remember that end-users building
    such projects are only doing a one-time build to get a working binary.

    They find it easier to do “apt-get install”, or a GUI wrapper around same, like Synaptic.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to Lawrence D'Oliveiro on Mon Jan 1 01:33:38 2024
    On 31/12/2023 23:46, Lawrence D'Oliveiro wrote:
    On Sun, 31 Dec 2023 02:18:25 +0000, Bart wrote:

    This somes my experience of software originating in Linux. This is why
    Windows had to acquire CYGWIN then MSYS then WSL. You can't build the
    simplest program without involving half of Linux.

    On Linux, we have package managers that only pull in the needed
    dependencies. Windows just seems actively hostile to that kind of infrastructure management. If you meant “you can’t build the simplest program *on Windows* without involving half of Linux” ... well, that’s just a reflection on the deficiencies of Windows. On Linux, you already
    have the *whole* of Linux to start with.

    And developers feel it necessary to USE everything that it provides!

    I've never managed to build the GMP library on Windows for example (it
    only comes as source code), because it requires that 30,000-line bash
    script which in turn needs sed and m4 and all the rest.

    Why? It's a numeric library. Why should it be dependent on OS?

    Or maybe Linux developers NEED all that hand-holding and have no idea
    how to build using a bare compiler. Remember that end-users building
    such projects are only doing a one-time build to get a working binary.

    I have sometimes made my own non-C projects available on Linux. I did
    that by transpiling to a single C source file. All you then need to
    build it is a C compiler. It would be almost as easy as building
    hello.c, except I suspect some don't even know that, since they are used
    to 'make'.

    On Linux, we have package managers that only pull in the needed dependencies. Windows just seems actively hostile to that kind of

    Yeah, I've seen them in action. Sometimes they work, sometimes not. On a Raspberry Pi, one 'apt-get install' went on for 90 minutes, then it
    crashed. Fortunately by then I'd forgotten what it was that I'd been
    trying to do.

    It seems peculiar to me that you take what I see as a drawback -
    excessive, gratuitous dependencies which also make the build process
    more complex and slower - and somehow turn that into an advantage.

    So any platform that doesn't include all that pointless crap must be at
    fault, rather than the developer who is inflicting those needless
    dependencies.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Tim Rentsch@21:1/5 to Keith Thompson on Sun Dec 31 18:32:49 2023
    Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:

    Janis Papanagnou <janis_papanagnou+ng@hotmail.com> writes:
    [...]

    BTW, is 'inline' meanwhile C standard? (I know that from C++ but
    haven't done much C for long now.)

    C added inline in C99 (the 1999 edition of the ISO C standard, the same
    one that removed implicit int).

    I think C and C++ have subtly different semantics for inline.

    I'm not a C++ expert, but as best I can tell C and C++ have
    markedly different semantics for what 'inline' does.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Tim Rentsch@21:1/5 to Keith Thompson on Sun Dec 31 18:31:00 2023
    Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:

    Tim Rentsch <tr.17687@z991.linuxsc.com> writes:

    Janis Papanagnou <janis_papanagnou+ng@hotmail.com> writes:

    This is a CPP question that arose last month. It's not about an
    actual issue with the software, just out of curiosity and to be sure
    it works reliable (it seemingly does).

    In a C99 program on Linux (Ubuntu) I intended to use usleep() and
    then also strnlen().

    When I added usleep() and its include file I got an error and was
    asked to define the CPP tag '_BSD_SOURCE'. I did so, and because I
    wanted side effects of that tag kept as small as possible I
    prepended it just before the respective #include and put it at the
    end of my #include list

    ...other #includes...
    #define _BSD_SOURCE
    #include <unistd.h>

    But as got obvious *that* way there had been side-effects and I
    had to put the tag at the beginning of all include files (which
    astonished me)

    #define _BSD_SOURCE
    #include <unistd.h>
    ...other #includes here...

    For the strnlen() function I needed another CPP tag, '_GNU_SOURCE'.
    So now I have both CPP tag definitions before the includes

    I second the recommendations of Lowell Gilbert and others not to
    define _BSD_SOURCE or _GNU_SOURCE (especially not _GNU_SOURCE)
    but instead seek alternatives, which are readily available for
    the two functionalities being sought in this case.

    #define _GNU_SOURCE /* necessary for strnlen() in string.h */
    #define _BSD_SOURCE /* necessary for usleep() in unistd.h */
    ...all #includes here...

    For strnlen(), put an inline definition in a header file:

    #ifndef HAVE_strnlen_dot_h_header
    #define HAVE_strnlen_dot_h_header

    #include <stddef.h>

    static inline size_t
    strnlen( const char *s, size_t n ){
    extern void *memchr( const void *, int, size_t );
    const char *p = memchr( s, 0, n );
    return p ? (size_t){ p-s } : n;
    }

    #include <string.h>

    #endif

    Disclaimer: this code has been compiled but not tested.

    strnlen() is specified by POSIX. It might make sense to
    re-implement it if your code needs to work on a non-POSIX system
    (that doesn't also provide it). Why would you want to do so
    otherwise?

    I'm trying to provide a helpful answer to the person I was
    responding to, not espouse a philosophical viewpoint. Why do you
    feel the need to start a style debate?

    memchr() is declared in <string.h>. Why would you duplicate its
    declaration rather than just using `#include <string.h>`?

    I had a specific reason for writing the code the way I did.
    It wasn't important to explain that so I didn't.

    For usleep(), define an alternate function usnooze(), to be used
    in place of usleep(). In header file usnooze.h:

    [snip]

    If your code doesn't need to be portable to systems that don't
    provide usleep(), you can just use usleep(). If it does, its
    probably better to modify the code so it uses nanosleep().

    Not everyone agrees with that opinion. Again, I'm just trying to
    provide an answer helpful to OP, not advance an agenda. Like I
    said in the part of my posting that you left out, I don't want to
    get involved in a style war. If OP wants to modify his code to
    use nanosleep(), I'm fine with that. If want wants to keep using
    usleep() or switch to using usnooze(), I'm fine with that too. I
    think it's more important in this case to provide options than to
    try to change someone's point of view.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Bart on Mon Jan 1 02:58:53 2024
    On 2023-12-31, Bart <bc@freeuk.cm> wrote:
    On 31/12/2023 19:37, Bart wrote:
    Actually ctags appears to be also part of Windows.

    That's not the case, it just happened to be bundled with Windows.

    What??? By what system vendor?

    You probably installed something that pulled in ctags.

    Ctags is of no use to the average Windows user, and I can practically
    guarantee you Microsoft has zero interest in it.

    Microsoft has replaced this sort of thing with the Language Server
    Protocol system, which is integrated into Visual Studio Code and all
    sots of language compilers and run times. (Speaking of which, even VSS
    doesn't come with Windows, to my knowledge.)

    How does Vim know where in the file to look? The TAGS files I've
    managed > to produce doesn't have that info, and I can't see anything
    in the help to add it.

    Entries look like this:

    addrinfo stdlib/socket.tl /^(defstruct addrinfo nil$/;" s

    symbol[Tab]path[Tab]search command

    The ;" is the start of a comment. The comment field contains extension
    fields. The letter s is being used to indicate that the identifier is
    the name of a structure (a Lisp one, in this case).

    If the search command is a regex search, that is advantageous; the
    tags file remains usable even when file contents are changing.
    It's possible to use hard coded line numbers.

    In Vim you can type :help tags-file-format to get documentation about
    this.

    How do you get it to look inside header files, or do those have to be submitted manually?

    ctags -R will process a tree recursively, including any C header files. However, things that are only declared and not defined in header files,
    like function declarations, are not indexed.

    Emacs uses a different tag format, by the way, which I referred to
    as etags, I think.

    In the TXR Lisp ctags generator I used some tricks. Look at this
    entry; it relates to the above addrinfo. canonname is a slot in
    this structure:

    canonname stdlib/socket.tl /^(defstruct addrinfo nil$/;/canonname/;" m struct:addrinfo

    The search command contains two searches! First it searches for the
    defstruct line. Then from that point it searches forward for canonname.

    This is very important. That file contains two structures which have
    a canonname slot; a sockaddr structure also has one:

    canonname stdlib/socket.tl /^(defstruct sockaddr nil$/;/canonname/;" m struct:sockaddr

    Vim will give you both choices and let you pick:

    :tj canonname
    canonname canonname_s
    # pri kind tag file
    1 F canonname socket.c
    ?
    canonname_s = intern(lit("canonname"), user_package);
    2 F m canonname stdlib/socket.tl
    struct:sockaddr
    (defstruct sockaddr nil$/;/canonname
    3 F m canonname stdlib/socket.tl
    struct:addrinfo
    (defstruct addrinfo nil$/;/canonname


    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to Chris M. Thomasson on Mon Jan 1 04:48:24 2024
    On Sun, 31 Dec 2023 20:06:38 -0800, Chris M. Thomasson wrote:

    On 12/31/2023 3:46 PM, Lawrence D'Oliveiro wrote:

    On Linux, we have package managers that only pull in the needed
    dependencies. Windows just seems actively hostile to that kind of
    infrastructure management. If you meant “you can’t build the simplest
    program *on Windows* without involving half of Linux” ... well, that’s >> just a reflection on the deficiencies of Windows. On Linux, you already
    have the *whole* of Linux to start with.

    Check this out:

    https://vcpkg.io/en/

    How wonderful. From Microsoft, yet it is only for C/C++? Not even
    supporting Microsoft’s flagship language, C#?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Janis Papanagnou@21:1/5 to Keith Thompson on Mon Jan 1 05:38:52 2024
    On 01.01.2024 04:18, Keith Thompson wrote:
    Kaz Kylheku <433-929-6894@kylheku.com> writes:
    On 2023-12-31, Bart <bc@freeuk.cm> wrote:
    [...]
    How do you get it to look inside header files, or do those have to be
    submitted manually?

    ctags -R will process a tree recursively, including any C header files.
    However, things that are only declared and not defined in header files,
    like function declarations, are not indexed.

    The exuberant-ctags version I have on Ubuntu and Cygwin doesn't seem to
    have an option to process a tree recursively, or to deal with
    directories at all. (I find that a little surprising; it would be
    useful functionality.) The -R option is documented as follows:

    -R, --no-regex
    Don't do any more regexp matching on the following files. May
    be freely intermixed with filenames and the --regex option.

    Does it have a '--recurse' option?

    My Ubuntu version says

    -R Equivalent to --recurse.

    (version Exuberant Ctags 5.9~svn20110310)

    Janis

    [...]

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Janis Papanagnou@21:1/5 to Tim Rentsch on Mon Jan 1 05:56:39 2024
    On 01.01.2024 01:07, Tim Rentsch wrote:
    [...]

    Thanks for your post and suggestions.

    Some have already been addressed (and some also answered) in
    this thread, but that could have easily been overlooked since
    the thread (unexpectedly) grew so large (and partly even OT).
    Some discussions got a bit heated; but it's not worth. Peace
    to all of you for the new year! :-)

    Janis

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to Chris M. Thomasson on Mon Jan 1 07:00:19 2024
    On Sun, 31 Dec 2023 22:57:12 -0800, Chris M. Thomasson wrote:

    It was just an example of some fairly hard core macro
    magic.

    String-based macros aren’t “magic”, they’re just sad.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Keith Thompson on Mon Jan 1 08:54:16 2024
    On 2024-01-01, Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:
    Kaz Kylheku <433-929-6894@kylheku.com> writes:
    On 2023-12-31, Bart <bc@freeuk.cm> wrote:
    [...]
    How do you get it to look inside header files, or do those have to be
    submitted manually?

    ctags -R will process a tree recursively, including any C header files.
    However, things that are only declared and not defined in header files,
    like function declarations, are not indexed.

    The exuberant-ctags version I have on Ubuntu and Cygwin doesn't seem to
    have an option to process a tree recursively, or to deal with
    directories at all. (I find that a little surprising; it would be
    useful functionality.) The -R option is documented as follows:

    -R, --no-regex
    Don't do any more regexp matching on the following files. May
    be freely intermixed with filenames and the --regex option.

    That's verbatim out of the etags documentation (a tags generator
    associated with Emacs), not Exuberant ctags.

    Etags provides two commands: etags and ctags. (I'm guessing, because the
    Emacs tag format is different from the ctags one, and the two programs
    generate different formats?)

    Thus what "ctags" refers to on a Debian-like system such as Ubuntu
    will be decided by the "alternatives" system.

    It may be that Emacs pulls in etags, and so you get a ctags command
    referring to that.

    In turn, I'm surprised to find that there is a ctags documented by POSIX.

    https://pubs.opengroup.org/onlinepubs/9699919799/utilities/ctags.html

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Lawrence D'Oliveiro on Mon Jan 1 09:18:28 2024
    On 2024-01-01, Lawrence D'Oliveiro <ldo@nz.invalid> wrote:
    On Sun, 31 Dec 2023 22:57:12 -0800, Chris M. Thomasson wrote:

    It was just an example of some fairly hard core macro
    magic.

    String-based macros aren’t “magic”, they’re just sad.

    Half the problem of C macros comes from the C language.

    The C language has too much syntax, which causes friction
    when you use the C preprocessor.

    This really hit home for me when I developed the cppawk
    project.

    cppawk is a shell program that wraps C preprocessing around awk.

    Awk has a C-inspired syntax, but there is a lot less of it;
    for instance, no type declarations, and semicolons mandatory.
    This simplicity makes preprocessing a lot smoother.

    In cppawk, I was able to develop an iteration macro which
    supports a vocabulary of clauses. Furthermore, the vocabulary
    is application-extensible, very easily.
    There are two variants: loop and loop_nest for parallel
    and cross-product iteration. In loop_nest you can use
    the parallel() combinator to combine certain clauses
    to go in parallel with each other.

    Here are test cases for the loop macro:

    https://www.kylheku.com/cgit/cppawk/tree/testcases-iter#n67

    Some of the test cases exercise user-defined clauses,
    like a "first_then_until" clause which initializes
    a variable with an arbitrary expression, and steps
    it with another arbitrary expression, until a certain
    condition:

    #include <iter.h>

    #define __init_first_then_until(var, first, then, until) (var = (first)) #define __test_first_then_until(var, first, then, until) (!(until))
    #define __prep_first_then_until(var, first, then, until) 1
    #define __fini_first_then_until(var, first, then, until) 1
    #define __step_first_then_until(var, first, then, until) (var = (then))

    BEGIN {
    loop (first_then_until(i, 1, i * 2, i >= 60),
    first_then_until(s, "x", s s, 0))
    {
    print i, s
    }
    }

    Output:

    1 x
    2 xx
    4 xxxx
    8 xxxxxxxx
    16 xxxxxxxxxxxxxxxx
    32 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

    By defining five macros: __init_X, __test_X, __prep_X, __fini_X
    and __step_X, you define a new clause X. The man page details how.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to James Kuyper on Mon Jan 1 12:57:46 2024
    On 31/12/2023 18:43, James Kuyper wrote:
    On 12/31/23 08:40, David Brown wrote:
    ...
    In C, the "inline" qualifier is pretty much a message from the
    programmer saying "I think the resulting code would be more efficient
    if this is inlined by the optimiser".

    Actually, what the C standard says is "Making a function an
    inline function suggests that calls to the function be as fast as
    possible". The standard does not specify how this is to be achieved, it merely imposes some requirements that constrain how it could be
    achieved. Inlining a function call is just one way to do that.


    Yes, that's what the standard says. The choice of "inline" as the
    keyword is copied from C++, rather than meaning specifically that the
    function should be inlined by the optimiser.

    But most C programmers don't read the standards, and I think many will
    assume they are asking for "inlining", rather than more generically "as
    fast as possible". Usually - but not always - "as fast as possible"
    will imply inlining optimisations anyway.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to Lawrence D'Oliveiro on Mon Jan 1 11:56:00 2024
    On 01/01/2024 02:00, Lawrence D'Oliveiro wrote:
    On Mon, 1 Jan 2024 01:33:38 +0000, Bart wrote:

    On 31/12/2023 23:46, Lawrence D'Oliveiro wrote:

    If you meant “you can’t build the simplest
    program *on Windows* without involving half of Linux” ... well, that’s >>> just a reflection on the deficiencies of Windows. On Linux, you already
    have the *whole* of Linux to start with.

    And developers feel it necessary to USE everything that it provides!

    It’s called “code reuse”. A well-designed package-management system just
    makes it so much easier to do.

    I've never managed to build the GMP library on Windows for example (it
    only comes as source code), because it requires that 30,000-line bash
    script which in turn needs sed and m4 and all the rest.

    Why? It's a numeric library. Why should it be dependent on OS?

    Those are just standard file-manipulation tools that any decent OS should provide.

    What's that got to do with building able to build programs easily?

    I have an interpreter app 'qc' which is 37 modules and 39Kloc. It's not
    in C, and I usually build it on Windows like this:

    c:\qx>mm qc

    It takes 0.1 seconds. I don't support Linux directly, but I can port it
    by transpiling to C first, which takes 90ms:

    c:\qc>mc -c -linux qc

    Now I can build it under WSL:

    root@xxx:/mnt/c/qx# gcc qc.c -oqc -fno-builtin -lm -ldl

    And run it, but it needs the '-nosys' option as its std libraries make
    use of WinAPI:

    root@xxx:/mnt/c/qx# ./qc -nosys hello
    Hello, World! 1-Jan-2024 11:43:54

    Here is the demo program it runs:

    root@xxx:/mnt/c/qx# cat hello.q
    println "Hello, World!", $date, $time

    And these are the specs for those files:

    c:\qx>dir qc.c qc
    01/01/2024 11:40 1,392,581 qc.c
    01/01/2024 11:42 846,488 qc

    Quite a substantial program that can be built effortlessly on either
    Windows or Linux. Using Tiny C, it apparently compiles it in 75ms.

    Try the same exercise with any equivalentally-sized program originating
    on Linux. That is end, end up with only a C file (even multiple C files)
    that I can build with a bare compiler.

    That seems to be beyond most Linux developers.


    Or maybe Linux developers NEED all that hand-holding and have no idea
    how to build using a bare compiler.

    If only you could do that on Windows ... but no. Look at all the C runtime stuff needed just to build a simple “Hello World” program ... because Windows automatically assumes that every program must have a GUI.

    It sounds like you've either never used Windows or had one bad
    experience, as this is incorrect.

    If I do this on Windows:

    mcc hello.c

    it produces a file hello.exe which is 2.5KB. (Tcc manages it in 2.0KB!)

    So what's the fuss?


    Remember that end-users building
    such projects are only doing a one-time build to get a working binary.

    They find it easier to do “apt-get install”, or a GUI wrapper around same,
    like Synaptic.

    Windows applications have for long been a ready-to-run binary.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Scott Lurndal on Mon Jan 1 13:09:11 2024
    On 31/12/2023 19:33, Scott Lurndal wrote:
    Bart <bc@freeuk.cm> writes:
    On 31/12/2023 01:36, Lawrence D'Oliveiro wrote:
    On Sat, 30 Dec 2023 01:58:53 +0000, Bart wrote:

    So, why don't the vendors of the library do that exercise?

    Maybe because most of the “vendors” of proprietary libraries have gone >>> extinct. What we have now is “developers” and “contributors” to open-
    source projects. And if you have a bright idea for how they can do things >>> better, you are free to contribute it.

    I have plenty of ideas, but people are generally not interested.

    Perhaps they are not very good ideas, then....

    Frankly, your obsession with header files is puzzling. 99.9%
    percent of C/C++ programmers don't care.

    Bart needs to care about the library header files when he is working on
    his C implementation, and it is understandable that he finds existing
    headers frustrating when he is trying to turn them into something his C implementation can use. But you are absolutely correct that the huge
    majority of C programmers don't care what is in these headers. So as I
    see it, the strange thing about Bart's view of library headers is not
    that /he/ is obsessed with them or frustrated by them, but that he
    apparently feels other people should also be bothered by them.

    The glibc (and other C library) developers have a responsibility to make headers that work correctly for C programmers, according to the C
    standards and their own documentation of extensions, and without
    significant measurable unnecessary inefficiency - beyond that, they can organise things in whatever way suits them and whatever way they find
    easiest. They have no responsibility to make life easier for people
    making libraries, headers or compilers other than those they choose to
    work with. Bart is right to think they /could/ have made it vastly
    easier to use their headers with other tools, such as his compiler - but
    wrong to think they /should/ have done so, and wrong to think that more
    than a tiny proportion of C programmers are bothered about it.

    C++ programmers (and C++ compiler writers) /do/ care a bit more about
    headers, though not the ones from the C standard library. C++ headers
    are often very big and are a significant part of compile and build
    times. Thus compilers often support pre-compiled headers, and one of
    the prime motivations of C++ modules in newer C++ standards is to be
    more efficient and cleaner than header files.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to Scott Lurndal on Mon Jan 1 12:49:52 2024
    On 31/12/2023 18:33, Scott Lurndal wrote:
    Bart <bc@freeuk.cm> writes:
    On 31/12/2023 01:36, Lawrence D'Oliveiro wrote:
    On Sat, 30 Dec 2023 01:58:53 +0000, Bart wrote:

    So, why don't the vendors of the library do that exercise?

    Maybe because most of the “vendors” of proprietary libraries have gone >>> extinct. What we have now is “developers” and “contributors” to open-
    source projects. And if you have a bright idea for how they can do things >>> better, you are free to contribute it.

    I have plenty of ideas, but people are generally not interested.

    Perhaps they are not very good ideas, then....

    Frankly, your obsession with header files is puzzling. 99.9%
    percent of C/C++ programmers don't care.

    Well, they should care more.

    Even considering only C, using libraries like SDL2, Windows, GTK looks
    simple enough; you just write #include <header.h>, but behind that
    header could be 100s of nested header files and 100s of 1000s of lines
    of code.

    I recently had to build a C program of 34 modules, which only totalled
    8Kloc (with an executable of 150KB), but it took even my compiler 1.5
    seconds. What was going on?

    It turned out each module was processing the headers for SDL2, and many
    of those headers were including other SDL headers. In all, 7-8000
    #includes were being processed, and SDL2 is one of the smaller such
    libraries.

    The information in those SDL headers could be summarised in one header
    file of 3000 lines. Why on earth would all those sub-headers need to be
    exposed to a user-program anyway?

    And here I've only looked at the impact on compilation times. Not on
    creating bindings for other languages.

    Instead of looking at this problem and coming to a conclusion like mine,
    people prefer to solve by piling on more complexity, or using faster
    machines, or multiple cores, or creating clever build processes to try
    and avoid compiling, or invent 'precompiled headers', or any number of
    fanciful ideas rather than tackling the elephant in the room.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Lawrence D'Oliveiro on Mon Jan 1 15:44:42 2024
    On 31/12/2023 22:44, Lawrence D'Oliveiro wrote:
    On Sun, 31 Dec 2023 16:25:08 +0100, David Brown wrote:

    I realise that you (and possibly others) might find it useful for a tool
    to replace typedef identifiers with their definitions, but it could only
    be done for some cases, and is not as simple as macro substitution.

    String-based macros are nothing but trouble.

    What a strange thing to say.

    Macros based on textual substitution have their advantages and their disadvantages. It is a reasonable generalisation to say you should
    prefer alternatives when available, such as inline functions, const
    objects, enum constants, typedefs, etc., rather than macro equivalents.
    But there are plenty of situations where C's pre-processor macros are
    extremely useful in writing good, clear and maintainable code.

    Typedefs are scoped, string
    macros are not.

    True. Sometimes that is an advantage, sometimes a disadvantage.

    Like any powerful tool, macros can be abused or misused, leading to
    poorer results - but that does not mean they are "nothing but trouble".

    Other programming languages might have different ways of doing things,
    again with advantages and disadvantages.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Bart on Mon Jan 1 15:38:19 2024
    On 31/12/2023 16:45, Bart wrote:
    On 31/12/2023 15:25, David Brown wrote:
    On 29/12/2023 23:40, Bart wrote:
    On 29/12/2023 20:23, Kaz Kylheku wrote:
    On 2023-12-29, Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:
    David Brown <david.brown@hesbynett.no> writes:
    A useful tool that someone might like to write for this particular >>>>>> situation would be a partial C preprocessor, letting you choose what >>>>>> gets handled.  You could choose to expand the code here for, say, >>>>>> _GNU_SOURCE and _BSD_SOURCE - any use of these in #ifdef's and
    conditional compilation would be expanded according to whether you >>>>>> have defined the symbols or not, leaving an output that is easier to >>>>>> understand while keeping most of the pre-processor stuff unchanged >>>>>> (so
    not affecting #includes, and leaving #define'd macros and constants >>>>>> untouched and therefore more readable).

    The unifdef tool does some of this.  (I haven't used it much.)

    GNU cpp has an option which is something like this: -fdirectives-only. >>>> It causes it not to expand macros.

    It flattens include files, processes conditionals, and keeps #defines
    unchanged.

    However, it turns gcc's sys/stat.h from 300 lines into 3000 lines.

    If I apply it to my stat.h (also my stddef.h which it includes),
    which are 110 lines together, it produces 900 lines. Most of that
    consists of lots of built-in #defines with __ prefixes (each complete
    with a line saying it is built-in).

    When I use my own conversion tool (designed to turn C headers that
    define APIs into declarations in my language), the output is 65 lines.

    The gcc option does not expand typedefs or macros. So if there is a
    declaration using a type which uses both, that is unchanged, which is
    not helpful. (At least not if trying to create bindings for your FFI.)

    gcc with just -E will expand macros but still keep typedefs.


    Note that typedefs are part of the core C language, not the
    preprocessor, so there could not possibly be a cpp option to do
    anything with typedefs (the phrase "expand typedefs" is entirely wrong).

    I realise that you (and possibly others) might find it useful for a
    tool to replace typedef identifiers with their definitions, but it
    could only be done for some cases, and is not as simple as macro
    substitution.

    Take this program, which uses two nested typedefs and one macro:

       typedef short T;
       typedef T U;
       #define V U

       typedef struct R {
           V a, b, c;
       } S;

    Passed through 'gcc -E', it manages to expand the V in the struct with
    U. (-fdirectives-only doesn't even do that).

    V is a macro, so it is expanded by the preprocessor.
    "-fdirectives-only" instructs cpp that it should not expand macros. So
    far, this is all obvious and correct.


    So what are the types of 'a, b, c'? Across 1000s of line of code, they
    may need tracking down. At least, for someone not using your super-duper tools.

    As I said, I appreciate that it could be useful for you to have a tool
    that "unpacks" typedefs. But that would not be related to a
    preprocessor, and it could not be done by textual substitution (as
    macros are handled by the C preprocessor).

    In particular, you could replace all uses of "T" with "short", once you
    have taken scoping into account (something not needed for preprocessor
    macros). And you could replace "U" with "short". But you could /not/
    replace "S" with "struct R { short a, b, c; }" without changing the
    meaning of the code.

    typedef's of function types and function pointer types get complicated
    in other ways - handling these is not at all like macro expansion.


    If I use my compiler with 'mcc -mheaders', I get an output file that
    includes this:

        record R = $caligned
            i16 a
            i16 b
            i16 c
        end

    It gives all the information I might need. Including the fact that it
    uses default C alignment rules.

    Notice however the name of the record is R not S; here it needs a
    struct-tag to avoid an anonymous name. The typedef name is harder to use
    as it is replaced early on in compilation.

    Here, I'm effectively expanding a typedef. The output could just as
    equally have been C source code.


    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Blue-Maned_Hawk@21:1/5 to Lawrence D'Oliveiro on Mon Jan 1 15:45:51 2024
    Lawrence D'Oliveiro wrote:

    On Mon, 1 Jan 2024 01:33:38 +0000, Bart wrote:
    I've never managed to build the GMP library on Windows for example (it
    only comes as source code), because it requires that 30,000-line bash
    script which in turn needs sed and m4 and all the rest.

    Why? It's a numeric library. Why should it be dependent on OS?

    Those are just standard file-manipulation tools that any decent OS
    should provide.

    The phrase “all the rest” is ambiguous, and if a C program requires more than the CPP (which is more powerful than people give it credit for…), chances are that something's already gone pretty wrong.




    --
    Blue-Maned_Hawk│shortens to Hawk│/blu.mɛin.dʰak/ │he/him/his/himself/Mr.
    blue-maned_hawk.srht.site
    Now hiring waiter. Must be expert in railway salvage operations.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to David Brown on Mon Jan 1 15:54:30 2024
    On 01/01/2024 14:44, David Brown wrote:
    On 31/12/2023 22:44, Lawrence D'Oliveiro wrote:
    On Sun, 31 Dec 2023 16:25:08 +0100, David Brown wrote:

    I realise that you (and possibly others) might find it useful for a tool >>> to replace typedef identifiers with their definitions, but it could only >>> be done for some cases, and is not as simple as macro substitution.

    String-based macros are nothing but trouble.

    What a strange thing to say.

    Macros based on textual substitution have their advantages and their disadvantages.  It is a reasonable generalisation to say you should
    prefer alternatives when available, such as inline functions, const
    objects, enum constants, typedefs, etc., rather than macro equivalents.
    But there are plenty of situations where C's pre-processor macros are extremely useful in writing good, clear and maintainable code.


    The cases where macros are used sensibly would be better replaced by apt language features (D does this for example as it is no preprocessor).

    But I tend to come across the ones where macros are overused or abused.
    Some people seem to delight in doing so.

    When working the Lua sources a few months ago, that makes heavy uses of
    macros defined in lower case which look just like functions calls.

    One complex expression that my compiler had trouble with, when expanded,
    resulted in a line hundreds of character long, and using 11 levels of
    nested parentheses, the most I've ever come across.

    I really doubt that the authors would have written code that convoluted
    if macros had not been available.







    Typedefs are scoped, string
    macros are not.

    True.  Sometimes that is an advantage, sometimes a disadvantage.

    When it is an advantage?

    My systems language only acquired macros with parameters a year or two
    ago. They can only be used to expand to well-formed terms of
    expressions, and are used very sparingly.

    They have normal scoping, so can be shadowed or have distinct versions
    in different scopes and functions can define their own macros; they can
    be imported or exported just like functions and variables.

    The sorts of macros that C has seem stone age by comparison. And crude,
    in being able to define random bits of syntax, or synthesise tokens from multiple parts.

    Some people will obviously love that, but imagine trying to search for
    some token with a text editor, but it won't find it because it only
    exists after preprocessing.


    Like any powerful tool, macros can be abused or misused, leading to
    poorer results - but that does not mean they are "nothing but trouble".

    I wouldn't use the work 'powerful'; 'dangerous' and 'crazy' might be
    more apt.

    Imagine a CPP that could also map one letter another; more 'powerful',
    or just more 'crazy'?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to BGB on Mon Jan 1 20:13:06 2024
    On 01/01/2024 19:06, BGB wrote:
    On 1/1/2024 5:56 AM, Bart wrote:

    Quite a substantial program that can be built effortlessly on either
    Windows or Linux. Using Tiny C, it apparently compiles it in 75ms.

    Try the same exercise with any equivalentally-sized program
    originating on Linux. That is end, end up with only a C file (even
    multiple C files) that I can build with a bare compiler.

    That seems to be beyond most Linux developers.


    For a lot of stuff, it is surprising what one can get done with a
    one-liner shell script or batch file:
      gcc -o prog_name sources... options...
    Or:
      cl /Feprog_name sources... options...


    The approaches I use are mainly:

    1 Submit all the files, libraries and options on one line like
    your example. Using up/down keys to recall commands.

    2 Put the command line in (1) into a shell script

    3 Put the same information, usually on multiple lines into an
    '@' file, then build using 'gcc @file' for example.

    The above are fine as instructions for someoneelse to do a one-off build
    of your project. But what I normally use for development:

    4 I use my 60KB console IDE and a project file descripting the modules
    similar to the @ file. Now I can browse/edit/compile individual
    files, and link and run the lot.

    The problem with 1/2/3 when using a slow compiler like gcc is that you
    have to compile everything. With (4) I can compile only what's relevant.
    Since I will be very familiar with the project, I will know the main dependencies and will know when I need to build everything.


    And, then 'make' is still plenty usable.

    If you understand make, that's fine. I generally prefer my option (4),
    which I've used, in various forms, for decades.

    I wouldn't however inflict a makefile-based build on someone; I might
    supply (3) and a one-line build instruction. Unless it's a generated C
    then it will be one file anyway, or it might even be just *.c.


    The more complex build systems often don't really help, they often make
    the problem worse, and are needlessly overkill for most programs.

    They very often don't work, especially outside their comfort zone of Linux.

    Yeah, and if one does a console program, the code is basically identical either way:
      #include <stdio.h>
      int main(int argc, char *argv[])
      {
         printf("Hello World\n");
         return(0);
      }

    If building it from the command line with MSVC:
      cl helloworld.c

    Not a big issue...



    GUI is a pain, but this is true regardless of OS.


    Would be good though if there were a "good" portable way to do cross
    platform GUI programs, but alas.

    A common strategy is for a program does all the UI drawing itself, can
    avoid need for platform-specific window-management code by using SDL or similar.

    I've given up on GUI from a compiled language. I use it only from my interpreted code using library that works on top of WinAPI. It was
    supposed to be adaptable to X11, but I never got around to it, and that
    looks as terrible as WinAPI anyway.

    However SDL seems a reasonable compromise. It's a LOT smaller than GTK
    (which is really a collection of libraries; I think it comprises 50
    different DLLs). There are smaller ones around, but it general it's too
    much of a pain. You're also never going to produce something as flashy
    as even the tackiest Android app.


    One other drawback of GTK is that ...

    ... it's a monster.

    (When I did try to test my compiler on it a few years ago - the API and
    DLLs, not the source codes; the headers are complicated enough - I found
    that one of its structs used bitfields, which are
    implementation-dependent, in a public API.

    That means the size of the struct, which is critical, depended on how
    the compiler dealt with bitfields. I think it mainly works by luck since
    most use gcc or compatible, that matches what was used to build the
    library.)

    We don't need OS repos, because program installers can be casually
    downloaded off the internet.

    I guess, technically there is now the "Microsoft Store" (which is sort
    of like "Google Play" on Android), but haven't really used it for much.

    Good luck in getting your app into one of those stores.

    It's getting harder to provide binaries to Windows users, first because
    your machine now needs to be unlocked to run arbitrary EXE files (even
    MS apps like command.exe are otherwise banned!).

    But also because a random EXE will tricker AV warnings or even
    quarantine your program.

    I don't know how programs such as C compilers that you download get
    around such checks.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to Bart on Mon Jan 1 21:38:13 2024
    On Mon, 1 Jan 2024 11:56:00 +0000, Bart wrote:

    On 01/01/2024 02:00, Lawrence D'Oliveiro wrote:

    Those are just standard file-manipulation tools that any decent OS
    should provide.

    What's that got to do with building able to build programs easily?

    You have effectively answered that question yourself. Why were you trying
    to build GNU GMP on Windows? Isn’t there something equally capable and yet more, shall we say, “native” to Windows, that you can build and use in a more “Windows-native” fashion?

    Obviously the answer is no. And why? Because the Windows environment is
    simply not conducive to the development of such software. Which is why the software that exists is primarily oriented towards Linux-compatible
    systems, and why you struggle to get it going on Windows.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to BGB on Mon Jan 1 21:39:19 2024
    On Mon, 1 Jan 2024 13:06:34 -0600, BGB wrote:

    The more complex build systems often don't really help, they often make
    the problem worse, and are needlessly overkill for most programs.

    Easy for the armchair programmer to say. All you have to do is look at the build scripts yourself, and show us how you can simplify them. Put your
    money where your mouth is, in effect.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to David Brown on Mon Jan 1 21:45:09 2024
    On Mon, 1 Jan 2024 15:44:42 +0100, David Brown wrote:

    Macros based on textual substitution have their advantages and their disadvantages.

    They may have some advantage over not having macro-substitution facilities
    at all, but they have no important advantage over token-based macro
    systems.

    I mentioned “homoiconicity” elsewhere: that is the right and robust way to do it, letting you achieve useful and powerful things without running the
    real risk that it will all come down around your ears like a house of
    cards.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to Chris M. Thomasson on Mon Jan 1 21:40:57 2024
    On Sun, 31 Dec 2023 23:00:16 -0800, Chris M. Thomasson wrote:

    On 12/31/2023 8:48 PM, Lawrence D'Oliveiro wrote:

    On Sun, 31 Dec 2023 20:06:38 -0800, Chris M. Thomasson wrote:

    On 12/31/2023 3:46 PM, Lawrence D'Oliveiro wrote:

    On Linux, we have package managers that only pull in the needed
    dependencies. Windows just seems actively hostile to that kind of
    infrastructure management. If you meant “you can’t build the simplest >>>> program *on Windows* without involving half of Linux” ... well,
    that’s just a reflection on the deficiencies of Windows. On Linux,
    you already have the *whole* of Linux to start with.

    Check this out:

    https://vcpkg.io/en/

    How wonderful. From Microsoft, yet it is only for C/C++? Not even
    supporting Microsoft’s flagship language, C#?

    I don't think it supports C# at all, highly doubt it.

    I’m not sure what your point is, but all you are doing is reinforcing
    mine: that Linux systems offer integrated package management that works
    across *all* installed software, regardless of what language (or mixture
    of languages) it might be written in.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Lawrence D'Oliveiro on Mon Jan 1 23:08:26 2024
    On 2024-01-01, Lawrence D'Oliveiro <ldo@nz.invalid> wrote:
    On Mon, 1 Jan 2024 15:44:42 +0100, David Brown wrote:

    Macros based on textual substitution have their advantages and their
    disadvantages.

    They may have some advantage over not having macro-substitution facilities
    at all, but they have no important advantage over token-based macro
    systems.

    No important advantages isn't the same as no advantages.

    When we choose an alternative which has the better set of advantages,
    and only unimportant disadvantages, we mustn't pretend that those
    disadvantages didn't exist.

    I mentioned “homoiconicity” elsewhere: that is the right and robust way to
    do it, letting you achieve useful and powerful things without running the real risk that it will all come down around your ears like a house of
    cards.

    Homoiconicity refers to a situation in which a language run-time stores programs in textual source code form, so they can be edited at run-time.

    Unix shells are homoiconic because you can dump the functions with
    "set", and copy and paste those printed definitions to edit them.

    (Comments are not retained and the code is reformatted from some
    internal tokenized form.)

    The code/data correspondence in Lisps is something other than
    homoiconicity.

    Common Lisp has a homoiconic feature: it describes is a function *ed*
    which can be used to invoke a resident editor, if one is provided. It
    is implementation-dependent; implementations don't have to provide this.
    It can be given a function name, in which case the text of the function
    is edited. How that text is obtained is implementation-defined.
    It cannot work for compiled functions, unless the source is retained
    somewhere from which text can be recovered.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to Lawrence D'Oliveiro on Mon Jan 1 22:51:52 2024
    On 01/01/2024 21:38, Lawrence D'Oliveiro wrote:
    On Mon, 1 Jan 2024 11:56:00 +0000, Bart wrote:

    On 01/01/2024 02:00, Lawrence D'Oliveiro wrote:

    Those are just standard file-manipulation tools that any decent OS
    should provide.

    What's that got to do with building able to build programs easily?

    You have effectively answered that question yourself. Why were you trying
    to build GNU GMP on Windows? Isn’t there something equally capable and yet more, shall we say, “native” to Windows, that you can build and use in a more “Windows-native” fashion?

    Obviously the answer is no. And why? Because the Windows environment is simply not conducive to the development of such software.

    Why not?

    The software in question is a bunch of C files with a smattering of .s
    assembly files for a range of specific architectures.

    I will tell you one reason: whoever was in charge made the brain-dead
    decision to employ auto-config tools which are prone to generating
    ginormous, Linux-specific shell scripts of 10s of thousands of lines.

    There's no intrinsic reason why such software can't be built anywhere
    where a 32-bit 'int' type exists.

    (In the end I developed my own library. It consists of a .h file and a
    .c file, and can be built anywhere. It needs only a compiler.)

    Which is why the
    software that exists is primarily oriented towards Linux-compatible
    systems, and why you struggle to get it going on Windows.

    Some kinds of program such as ones that only read or write consoles and
    files can be universally portable; or all they are C's file functions.

    But this particular one doesn't even have any I/O; it is a library! So
    it ought to be even more portable.

    I once took another program that only came packaged in a similar manner.
    I could built it under Linux using ./configure, a process which took 5
    minutes.

    But there, I managed to extract the dozen or so C files that formed the
    main program. Compiling them to an executable on the same machine, under Windows, took about 1 second.

    So WTF was the point of the rest of it?

    Too many people, including you it seems, take too much at face value.
    You just don't ask enough questions.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to Bart on Mon Jan 1 23:10:05 2024
    On Mon, 1 Jan 2024 22:51:52 +0000, Bart wrote:

    On 01/01/2024 21:38, Lawrence D'Oliveiro wrote:

    Obviously the answer is no. And why? Because the Windows environment is
    simply not conducive to the development of such software.

    Why not?

    You have the answer right in front of your nose: look at the source of the software itself and figure it out.

    The software in question is a bunch of C files with a smattering of .s assembly files for a range of specific architectures.

    If you can come up with a simpler build system, why not publish your
    version? And everybody else will adopt it because it will be so much
    simpler than the present arrangement.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to Bart on Tue Jan 2 00:05:57 2024
    On Mon, 1 Jan 2024 23:45:18 +0000, Bart wrote:

    On 01/01/2024 23:10, Lawrence D'Oliveiro wrote:

    You have the answer right in front of your nose: look at the source of
    the software itself and figure it out.

    So you don't know. You choose to believe that it HAS to be done that
    way.

    Out of curiosity, I *did* have a look at the build system for libgmp. The “configure” file is over 30,000 lines, but that is generated (via m4 macro expansion) from a much smaller (and easier to read) “configure.ac” of
    about 4,000 lines. And the contents of the latter file are quite
    interesting.

    First of all, it includes code for building on a whole lot of proprietary
    Unix systems with non-GCC compilers, some of which maybe don’t quite
    conform to official C/C++ standards. You would think all those systems are extinct now, but clearly there are users who still care about them.

    And that’s not counting the different ways GMP offers for you to build it. For example, do you want profiling? Omit procedure call frames? Static or shared library? Do you want to build the test programs? (Yes, the project includes its own test suite.) Do you want the test programs to use GNU
    readline to get their input? (Sounds like a handy option to me, at least
    you have the choice.)

    That is an amazingly long list of CPU architectures on which you can build
    it, all the way up to IBM mainframes. And it is in the nature of a library
    like this, doing CPU-intensive things, that it will need to take account
    of CPU-specific quirks in order to get maximum efficiency. Hence a whole
    load of special cases, particularly in code generation, for all these architectures.

    Look at the ABI options: maybe you want a 32-bit build (where the CPU
    supports it) rather than a 64-bit one; on some architectures, things get a
    bit more complicated than just those two options.

    And so on and so on. Feel free to tell us which parts of these you would
    get rid of, without antagonizing users who might depend on them.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to Lawrence D'Oliveiro on Mon Jan 1 23:45:18 2024
    On 01/01/2024 23:10, Lawrence D'Oliveiro wrote:
    On Mon, 1 Jan 2024 22:51:52 +0000, Bart wrote:

    On 01/01/2024 21:38, Lawrence D'Oliveiro wrote:

    Obviously the answer is no. And why? Because the Windows environment is
    simply not conducive to the development of such software.

    Why not?

    You have the answer right in front of your nose: look at the source of the software itself and figure it out.

    So you don't know. You choose to believe that it HAS to be done that
    way. I guess if that configure file was 300,000 lines instead of 30,000,
    or even 3 million lines and ran for hours, you'd still go along with it?

    I'm curious; at what point of ridiculousness would even you start to
    question the disproportionality of the process?

    The software in question is a bunch of C files with a smattering of .s
    assembly files for a range of specific architectures.

    If you can come up with a simpler build system, why not publish your
    version? And everybody else will adopt it because it will be so much
    simpler than the present arrangement.

    Sure. But the problem is extracting the requisite information, namely,
    the names and locations of all the source files needed for my platform.

    Plus I need the actual source files.

    With over-elaborate build systems, the information is buried somewhere
    inside makefiles. Often the makefiles don't even exist, they have to be synthesised.

    They also like to make some key part of the sources, say a 5-line
    config.h file, be synthesised by the build process.

    So they make it hard. Even if I can extract that information, that is no
    good; it HAS to be done by the suppliers of the software, otherwise with
    each new version I have to repeat the process.

    ========================

    Here however is somewhere where I've done just that, although it is
    trivial compared with most projects I've come across; the makefile
    actually exists, and is only 200 lines. The motivation here was in
    having some content to test my C compiler on.

    The project is Lua 5.4. I use an @ file containing this list of modules:

    lua.c
    lapi.c
    lcode.c
    lctype.c
    ldebug.c
    ldo.c
    ldump.c
    lfunc.c
    lgc.c
    llex.c
    lmem.c
    lobject.c
    lopcodes.c
    lparser.c
    lstate.c
    lstring.c
    ltable.c
    ltm.c
    lundump.c
    lvm.c
    lzio.c
    lauxlib.c
    lbaselib.c
    lcorolib.c
    ldblib.c
    liolib.c
    lmathlib.c
    loadlib.c
    loslib.c
    lstrlib.c
    ltablib.c
    lutf8lib.c
    linit.c

    I compile the project using:

    c:\luac>mcc @lua
    Compiling 33 files to lua.exe

    or using:

    c:\luac>tcc @lua

    or using:

    c:\luac>gcc @lua -olua.exe

    or even on Linux using:

    root@xxx:/mnt/c/luac# gcc @luafiles -olua -lm

    (The @ file name had to be changed as it clashes with the Linux executable.)

    It's that simple. (This builds lua.exe; to build lua.dll needs one file different in the list, and an extra option to the compiler.)

    However you are supposed to use a makefile that looks like that below.
    Which looks simpler?

    (You might notice the makefile relies heavily on .o files. My C compiler doesn't use .o files, and if it did, they'd be called .obj. Notice also
    my list doesn't show .h files; they are not necessary.)

    -----------------------------------------------
    # Makefile for building Lua
    # See ../doc/readme.html for installation and customization instructions.

    # == CHANGE THE SETTINGS BELOW TO SUIT YOUR ENVIRONMENT
    =======================

    # Your platform. See PLATS for possible values.
    PLAT= guess

    CC= gcc -std=gnu99
    CFLAGS= -O2 -Wall -Wextra -DLUA_COMPAT_5_3 $(SYSCFLAGS) $(MYCFLAGS)
    LDFLAGS= $(SYSLDFLAGS) $(MYLDFLAGS)
    LIBS= -lm $(SYSLIBS) $(MYLIBS)

    AR= ar rcu
    RANLIB= ranlib
    RM= rm -f
    UNAME= uname

    SYSCFLAGS=
    SYSLDFLAGS=
    SYSLIBS=

    MYCFLAGS=
    MYLDFLAGS=
    MYLIBS=
    MYOBJS=

    # Special flags for compiler modules; -Os reduces code size.
    CMCFLAGS=

    # == END OF USER SETTINGS -- NO NEED TO CHANGE ANYTHING BELOW THIS LINE
    =======

    PLATS= guess aix bsd c89 freebsd generic ios linux linux-readline macosx
    mingw posix solaris

    LUA_A= liblua.a
    CORE_O= lapi.o lcode.o lctype.o ldebug.o ldo.o ldump.o lfunc.o lgc.o
    llex.o lmem.o lobject.o lopcodes.o lparser.o lstate.o lstring.o ltable.o
    ltm.o lundump.o lvm.o lzio.o
    LIB_O= lauxlib.o lbaselib.o lcorolib.o ldblib.o liolib.o lmathlib.o
    loadlib.o loslib.o lstrlib.o ltablib.o lutf8lib.o linit.o
    BASE_O= $(CORE_O) $(LIB_O) $(MYOBJS)

    LUA_T= lua
    LUA_O= lua.o

    LUAC_T= luac
    LUAC_O= luac.o

    ALL_O= $(BASE_O) $(LUA_O) $(LUAC_O)
    ALL_T= $(LUA_A) $(LUA_T) $(LUAC_T)
    ALL_A= $(LUA_A)

    # Targets start here.
    default: $(PLAT)

    all: $(ALL_T)

    o: $(ALL_O)

    a: $(ALL_A)

    $(LUA_A): $(BASE_O)
    $(AR) $@ $(BASE_O)
    $(RANLIB) $@

    $(LUA_T): $(LUA_O) $(LUA_A)
    $(CC) -o $@ $(LDFLAGS) $(LUA_O) $(LUA_A) $(LIBS)

    $(LUAC_T): $(LUAC_O) $(LUA_A)
    $(CC) -o $@ $(LDFLAGS) $(LUAC_O) $(LUA_A) $(LIBS)

    test:
    ./$(LUA_T) -v

    clean:
    $(RM) $(ALL_T) $(ALL_O)

    depend:
    @$(CC) $(CFLAGS) -MM l*.c

    echo:
    @echo "PLAT= $(PLAT)"
    @echo "CC= $(CC)"
    @echo "CFLAGS= $(CFLAGS)"
    @echo "LDFLAGS= $(LDFLAGS)"
    @echo "LIBS= $(LIBS)"
    @echo "AR= $(AR)"
    @echo "RANLIB= $(RANLIB)"
    @echo "RM= $(RM)"
    @echo "UNAME= $(UNAME)"

    # Convenience targets for popular platforms.
    ALL= all

    help:
    @echo "Do 'make PLATFORM' where PLATFORM is one of these:"
    @echo " $(PLATS)"
    @echo "See doc/readme.html for complete instructions."

    guess:
    @echo Guessing `$(UNAME)`
    @$(MAKE) `$(UNAME)`

    AIX aix:
    $(MAKE) $(ALL) CC="xlc" CFLAGS="-O2 -DLUA_USE_POSIX -DLUA_USE_DLOPEN" SYSLIBS="-ldl" SYSLDFLAGS="-brtl -bexpall"

    bsd:
    $(MAKE) $(ALL) SYSCFLAGS="-DLUA_USE_POSIX -DLUA_USE_DLOPEN" SYSLIBS="-Wl,-E"

    c89:
    $(MAKE) $(ALL) SYSCFLAGS="-DLUA_USE_C89" CC="gcc -std=c89"
    @echo ''
    @echo '*** C89 does not guarantee 64-bit integers for Lua.'
    @echo '*** Make sure to compile all external Lua libraries'
    @echo '*** with LUA_USE_C89 to ensure consistency'
    @echo ''

    FreeBSD NetBSD OpenBSD freebsd:
    $(MAKE) $(ALL) SYSCFLAGS="-DLUA_USE_LINUX -DLUA_USE_READLINE -I/usr/include/edit" SYSLIBS="-Wl,-E -ledit" CC="cc"

    generic: $(ALL)

    ios:
    $(MAKE) $(ALL) SYSCFLAGS="-DLUA_USE_IOS"

    Linux linux: linux-noreadline

    linux-noreadline:
    $(MAKE) $(ALL) SYSCFLAGS="-DLUA_USE_LINUX" SYSLIBS="-Wl,-E -ldl"

    linux-readline:
    $(MAKE) $(ALL) SYSCFLAGS="-DLUA_USE_LINUX -DLUA_USE_READLINE" SYSLIBS="-Wl,-E -ldl -lreadline"

    Darwin macos macosx:
    $(MAKE) $(ALL) SYSCFLAGS="-DLUA_USE_MACOSX -DLUA_USE_READLINE" SYSLIBS="-lreadline"

    mingw:
    $(MAKE) "LUA_A=lua54.dll" "LUA_T=lua.exe" \
    "AR=$(CC) -shared -o" "RANLIB=strip --strip-unneeded" \
    "SYSCFLAGS=-DLUA_BUILD_AS_DLL" "SYSLIBS=" "SYSLDFLAGS=-s" lua.exe
    $(MAKE) "LUAC_T=luac.exe" luac.exe

    posix:
    $(MAKE) $(ALL) SYSCFLAGS="-DLUA_USE_POSIX"

    SunOS solaris:
    $(MAKE) $(ALL) SYSCFLAGS="-DLUA_USE_POSIX -DLUA_USE_DLOPEN -D_REENTRANT" SYSLIBS="-ldl"

    # Targets that do not create files (not all makes understand .PHONY).
    .PHONY: all $(PLATS) help test clean default o a depend echo

    # Compiler modules may use special flags.
    llex.o:
    $(CC) $(CFLAGS) $(CMCFLAGS) -c llex.c

    lparser.o:
    $(CC) $(CFLAGS) $(CMCFLAGS) -c lparser.c

    lcode.o:
    $(CC) $(CFLAGS) $(CMCFLAGS) -c lcode.c

    # DO NOT DELETE

    lapi.o: lapi.c lprefix.h lua.h luaconf.h lapi.h llimits.h lstate.h \
    lobject.h ltm.h lzio.h lmem.h ldebug.h ldo.h lfunc.h lgc.h lstring.h \
    ltable.h lundump.h lvm.h
    lauxlib.o: lauxlib.c lprefix.h lua.h luaconf.h lauxlib.h
    lbaselib.o: lbaselib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h
    lcode.o: lcode.c lprefix.h lua.h luaconf.h lcode.h llex.h lobject.h \
    llimits.h lzio.h lmem.h lopcodes.h lparser.h ldebug.h lstate.h ltm.h \
    ldo.h lgc.h lstring.h ltable.h lvm.h
    lcorolib.o: lcorolib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h
    lctype.o: lctype.c lprefix.h lctype.h lua.h luaconf.h llimits.h
    ldblib.o: ldblib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h
    ldebug.o: ldebug.c lprefix.h lua.h luaconf.h lapi.h llimits.h lstate.h \
    lobject.h ltm.h lzio.h lmem.h lcode.h llex.h lopcodes.h lparser.h \
    ldebug.h ldo.h lfunc.h lstring.h lgc.h ltable.h lvm.h
    ldo.o: ldo.c lprefix.h lua.h luaconf.h lapi.h llimits.h lstate.h \
    lobject.h ltm.h lzio.h lmem.h ldebug.h ldo.h lfunc.h lgc.h lopcodes.h \
    lparser.h lstring.h ltable.h lundump.h lvm.h
    ldump.o: ldump.c lprefix.h lua.h luaconf.h lobject.h llimits.h lstate.h \
    ltm.h lzio.h lmem.h lundump.h
    lfunc.o: lfunc.c lprefix.h lua.h luaconf.h ldebug.h lstate.h lobject.h \
    llimits.h ltm.h lzio.h lmem.h ldo.h lfunc.h lgc.h
    lgc.o: lgc.c lprefix.h lua.h luaconf.h ldebug.h lstate.h lobject.h \
    llimits.h ltm.h lzio.h lmem.h ldo.h lfunc.h lgc.h lstring.h ltable.h
    linit.o: linit.c lprefix.h lua.h luaconf.h lualib.h lauxlib.h
    liolib.o: liolib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h
    llex.o: llex.c lprefix.h lua.h luaconf.h lctype.h llimits.h ldebug.h \
    lstate.h lobject.h ltm.h lzio.h lmem.h ldo.h lgc.h llex.h lparser.h \
    lstring.h ltable.h
    lmathlib.o: lmathlib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h
    lmem.o: lmem.c lprefix.h lua.h luaconf.h ldebug.h lstate.h lobject.h \
    llimits.h ltm.h lzio.h lmem.h ldo.h lgc.h
    loadlib.o: loadlib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h
    lobject.o: lobject.c lprefix.h lua.h luaconf.h lctype.h llimits.h \
    ldebug.h lstate.h lobject.h ltm.h lzio.h lmem.h ldo.h lstring.h lgc.h \
    lvm.h
    lopcodes.o: lopcodes.c lprefix.h lopcodes.h llimits.h lua.h luaconf.h
    loslib.o: loslib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h
    lparser.o: lparser.c lprefix.h lua.h luaconf.h lcode.h llex.h lobject.h \
    llimits.h lzio.h lmem.h lopcodes.h lparser.h ldebug.h lstate.h ltm.h \
    ldo.h lfunc.h lstring.h lgc.h ltable.h
    lstate.o: lstate.c lprefix.h lua.h luaconf.h lapi.h llimits.h lstate.h \
    lobject.h ltm.h lzio.h lmem.h ldebug.h ldo.h lfunc.h lgc.h llex.h \
    lstring.h ltable.h
    lstring.o: lstring.c lprefix.h lua.h luaconf.h ldebug.h lstate.h \
    lobject.h llimits.h ltm.h lzio.h lmem.h ldo.h lstring.h lgc.h
    lstrlib.o: lstrlib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h
    ltable.o: ltable.c lprefix.h lua.h luaconf.h ldebug.h lstate.h lobject.h \
    llimits.h ltm.h lzio.h lmem.h ldo.h lgc.h lstring.h ltable.h lvm.h
    ltablib.o: ltablib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h
    ltm.o: ltm.c lprefix.h lua.h luaconf.h ldebug.h lstate.h lobject.h \
    llimits.h ltm.h lzio.h lmem.h ldo.h lgc.h lstring.h ltable.h lvm.h
    lua.o: lua.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h
    luac.o: luac.c lprefix.h lua.h luaconf.h lauxlib.h ldebug.h lstate.h \
    lobject.h llimits.h ltm.h lzio.h lmem.h lopcodes.h lopnames.h lundump.h lundump.o: lundump.c lprefix.h lua.h luaconf.h ldebug.h lstate.h \
    lobject.h llimits.h ltm.h lzio.h lmem.h ldo.h lfunc.h lstring.h lgc.h \
    lundump.h
    lutf8lib.o: lutf8lib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h
    lvm.o: lvm.c lprefix.h lua.h luaconf.h ldebug.h lstate.h lobject.h \
    llimits.h ltm.h lzio.h lmem.h ldo.h lfunc.h lgc.h lopcodes.h lstring.h \
    ltable.h lvm.h ljumptab.h
    lzio.o: lzio.c lprefix.h lua.h luaconf.h llimits.h lmem.h lstate.h \
    lobject.h ltm.h lzio.h

    # (end of Makefile)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to Chris M. Thomasson on Tue Jan 2 00:06:47 2024
    On Mon, 1 Jan 2024 15:49:43 -0800, Chris M. Thomasson wrote:

    My point is that microsoft offers vcpkg. That's all.

    True. That is really all that it offers.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to Lawrence D'Oliveiro on Tue Jan 2 01:14:16 2024
    On 02/01/2024 00:05, Lawrence D'Oliveiro wrote:
    On Mon, 1 Jan 2024 23:45:18 +0000, Bart wrote:

    On 01/01/2024 23:10, Lawrence D'Oliveiro wrote:

    You have the answer right in front of your nose: look at the source of
    the software itself and figure it out.

    So you don't know. You choose to believe that it HAS to be done that
    way.

    Out of curiosity, I *did* have a look at the build system for libgmp. The “configure” file is over 30,000 lines, but that is generated (via m4 macro
    expansion) from a much smaller (and easier to read) “configure.ac” of about 4,000 lines. And the contents of the latter file are quite
    interesting.

    First of all, it includes code for building on a whole lot of proprietary Unix systems with non-GCC compilers, some of which maybe don’t quite conform to official C/C++ standards. You would think all those systems are extinct now, but clearly there are users who still care about them.

    And that’s not counting the different ways GMP offers for you to build it. For example, do you want profiling? Omit procedure call frames? Static or shared library? Do you want to build the test programs? (Yes, the project includes its own test suite.) Do you want the test programs to use GNU readline to get their input? (Sounds like a handy option to me, at least
    you have the choice.)

    That is an amazingly long list of CPU architectures on which you can build it, all the way up to IBM mainframes. And it is in the nature of a library like this, doing CPU-intensive things, that it will need to take account
    of CPU-specific quirks in order to get maximum efficiency. Hence a whole
    load of special cases, particularly in code generation, for all these architectures.

    Yeah, that's usually the job of the compiler, the one that exists on my machine, and which knows how to generate code for it.



    Look at the ABI options: maybe you want a 32-bit build (where the CPU supports it) rather than a 64-bit one; on some architectures, things get a bit more complicated than just those two options.

    And so on and so on. Feel free to tell us which parts of these you would
    get rid of, without antagonizing users who might depend on them.

    I would have no interest in building it, I wanted a ready-made DLL, for
    that little-known niche platform known as 'Windows'.

    However, there is no reliable repository of such libraries, so I can't
    use it as dependency.

    I could create my own, but it was a palaver, involving installing, at
    the time MSYS2. It took forever, but then it failed.

    So, yes, I want the set of files specific to my platform; I don't care
    about all the obscure ones it might support.

    This does not sound unreasonable: after all people commonly download
    binaries which are specific to a particular machine and processor. Those
    will have had some specific process applied to generate them.

    Why not a similar process for a source bundle which is one step away
    from a binary?

    I don't even care if all the source files I don't need are bundled. It's
    easy enough to tell the build process which version I need, if it can't
    work it out.

    (Earlier I gave an example of my 'QC' interpreter which can run on
    either OS. Actually that is a special version that is 100% HLL.

    The version I normally use is called 'QQ' and used ASM-accelerated
    elements, just like bits of GMP. That is specific to x64 and WinABI. It
    can't be transpiled to C.

    But if somebody really wants to run my interpreter and doesn't have
    Windows, they can still do so.)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to Bart on Tue Jan 2 01:58:01 2024
    On Tue, 2 Jan 2024 01:14:16 +0000, Bart wrote:

    I would have no interest in building it, I wanted a ready-made DLL, for
    that little-known niche platform known as 'Windows'.

    That’s too bad. Not getting the service you paid for? Demand your money
    back!

    However, there is no reliable repository of such libraries, so I can't
    use it as dependency.

    Why is it nobody will provide you with one? Not even a multi-billion-
    dollar corporation with the resources of Microsoft can be bothered, it
    seems, for this not-at-all “little-known niche platform” you are so fond of. Why not? And after all the money you have paid them to get this
    wonderful Windows platform! It’s like you didn’t get your money’s worth!

    So, yes, I want the set of files specific to my platform; I don't care
    about all the obscure ones it might support.

    This does not sound unreasonable ...

    Fine. Do it yourself. And then you will discover whether that’s an “unreasonable” amount of work or not.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to BGB on Tue Jan 2 02:34:57 2024
    On Mon, 1 Jan 2024 20:20:08 -0600, BGB wrote:

    And, if the Makefile isn't some mountain of auto-generated crap, then
    people can fix it (without too much effort) if something is broken.

    Remember the GNU definition of “source code”: it is whatever the preferred format is for doing development on the project. If they won’t give you
    that, then the project isn’t “open source”.

    If the Makefile is indeed a “mountain of auto-generated crap”, then you shouldn’t be fiddling with it (if only for your own sanity): instead, find the source file(s) used to generate it, and fiddle them instead.

    Of these, CMake is at least competent at being cross-platform (much
    better than autotools in this regard).

    And CMake is a popular example of something that will indeed produce
    Makefiles (actually a whole swarm of them) that are collectively a
    “mountain of auto-generated crap”.

    And if you take the concept further, assuming that your Makefile-
    equivalent is always going to be auto-generated, then you can get rid of a
    lot of the niceties (and complications, and overhead) that aid manual
    writing of same. And the result would be something called “Ninja”.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to Kaz Kylheku on Tue Jan 2 06:47:41 2024
    On Tue, 2 Jan 2024 06:23:03 -0000 (UTC), Kaz Kylheku wrote:

    On 2024-01-02, Lawrence D'Oliveiro <ldo@nz.invalid> wrote:

    First of all, it includes code for building on a whole lot of
    proprietary Unix systems with non-GCC compilers, some of which maybe
    don’t quite conform to official C/C++ standards.

    When people use Autoconf to configure their program for building, the generated script includes cruft for platforms on which they never tested
    the program, and never will, and on which it won't work for reasons not related to those tests.

    This is a GNU project, so you can be sure they have done pretty good
    testing on those platforms and those options. If they were no longer
    supported, they would have been dropped from the code. This isn’t like proprietary software, which tends to accumulate cruft that nobody dares
    touch because they no longer understand what it does.

    Look at the ABI options: maybe you want a 32-bit build (where the CPU
    supports it) rather than a 64-bit one; on some architectures, things
    get a bit more complicated than just those two options.

    These kinds of options can be passed in by CFLAGS, LDFLAGS and LDLIBS. Support those three variables, and that's it.

    If you have a look, that’s what the script does. Only you just have to specify one option, and it generates the right value for *all* the
    relevant variables, instead of you having to specify them individually.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Lawrence D'Oliveiro on Tue Jan 2 06:23:03 2024
    On 2024-01-02, Lawrence D'Oliveiro <ldo@nz.invalid> wrote:
    On Mon, 1 Jan 2024 23:45:18 +0000, Bart wrote:

    On 01/01/2024 23:10, Lawrence D'Oliveiro wrote:

    You have the answer right in front of your nose: look at the source of
    the software itself and figure it out.

    So you don't know. You choose to believe that it HAS to be done that
    way.

    Out of curiosity, I *did* have a look at the build system for libgmp. The “configure” file is over 30,000 lines, but that is generated (via m4 macro
    expansion) from a much smaller (and easier to read) “configure.ac” of about 4,000 lines. And the contents of the latter file are quite
    interesting.

    First of all, it includes code for building on a whole lot of proprietary Unix systems with non-GCC compilers, some of which maybe don’t quite conform to official C/C++ standards. You would think all those systems are extinct now, but clearly there are users who still care about them.

    When people use Autoconf to configure their program for building, the
    generated script includes cruft for platforms on which they never tested
    the program, and never will, and on which it won't work for reasons not
    related to those tests.

    It is 30,000 lines of mostly garbage code, like "junk DNA" in a genetic
    code.

    Look at the ABI options: maybe you want a 32-bit build (where the CPU supports it) rather than a 64-bit one; on some architectures, things get a bit more complicated than just those two options.

    These kinds of options can be passed in by CFLAGS, LDFLAGS and LDLIBS.
    Support those three variables, and that's it.

    Users who come up with some combination of code generation options that
    you've never tried, on a platform you don't have, are on their own.

    You can upstream a patch from them to get something working, but they
    have to test every subsequent release themselves.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Bart on Tue Jan 2 09:11:11 2024
    On 01/01/2024 13:49, Bart wrote:
    On 31/12/2023 18:33, Scott Lurndal wrote:
    Bart <bc@freeuk.cm> writes:
    On 31/12/2023 01:36, Lawrence D'Oliveiro wrote:
    On Sat, 30 Dec 2023 01:58:53 +0000, Bart wrote:

    So, why don't the vendors of the library do that exercise?

    Maybe because most of the “vendors” of proprietary libraries have gone >>>> extinct. What we have now is “developers” and “contributors” to open-
    source projects. And if you have a bright idea for how they can do
    things
    better, you are free to contribute it.

    I have plenty of ideas, but people are generally not interested.

    Perhaps they are not very good ideas, then....

    Frankly, your obsession with header files is puzzling.   99.9%
    percent of C/C++ programmers don't care.

    Well, they should care more.

    Even considering only C, using libraries like SDL2, Windows, GTK looks
    simple enough; you just write #include <header.h>, but behind that
    header could be 100s of nested header files and 100s of 1000s of lines
    of code.

    That's true. And it's true that other languages have better ways of
    handling such things (and no doubt some have worse ways). We all know
    the inconveniences and risks involved for C headers - the need to prefix identifiers so that "sdl2_init" won't conflict with "gtk_init", and the
    selfish disaster of windows.h with macros like "max". No one here will
    tell you that they think C is a perfect language, and we know this is an
    area where C has more issues than languages with larger structured units (namespaces, modules, units - whatever the language calls them).

    However, none of that is the issue here. C programmers care about the
    effect of including a header - what symbols and macros it defines, and
    any conflicts that result. What they do not care about is what you are complaining about - /how/ those effects are achieved. C programmers,
    other than the maintainers of the headers and anyone trying to port non-portable contents to other compilers or systems (such as yourself),
    don't care if the header takes 100 lines or a million lines spread over
    a thousand files. And nor should they care - it is only the resulting
    effects that matter.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Bart on Tue Jan 2 11:42:42 2024
    On 01/01/2024 16:54, Bart wrote:
    On 01/01/2024 14:44, David Brown wrote:
    On 31/12/2023 22:44, Lawrence D'Oliveiro wrote:
    On Sun, 31 Dec 2023 16:25:08 +0100, David Brown wrote:

    I realise that you (and possibly others) might find it useful for a
    tool
    to replace typedef identifiers with their definitions, but it could
    only
    be done for some cases, and is not as simple as macro substitution.

    String-based macros are nothing but trouble.

    What a strange thing to say.

    Macros based on textual substitution have their advantages and their
    disadvantages.  It is a reasonable generalisation to say you should
    prefer alternatives when available, such as inline functions, const
    objects, enum constants, typedefs, etc., rather than macro
    equivalents. But there are plenty of situations where C's
    pre-processor macros are extremely useful in writing good, clear and
    maintainable code.


    The cases where macros are used sensibly would be better replaced by apt language features (D does this for example as it is no preprocessor).

    Except when they can't.

    Now, D (like C++) can certainly do some things without a preprocessor
    that need the preprocessor in C. Classic examples are generic
    functions, such as a type-adaptive "max" function. In C, you need to
    use a macro for this - in D or C++ you'd use a template. And some cases
    of conditional compilation in C can be replaced by "static if" in D or
    "if constexpr" in C++. And in general, I believe most people will agree
    that if you can use "core language" features rather than the
    preprocessor to achieve your purpose in a good, clear manner, then it is preferable to use the core language.

    However, there are things that you can't do - or can't do well - without
    a preprocessor. And you either live without these things, or you use a preprocessor. (And people do sometimes use a C preprocessor with their
    D code.)

    I don't have any experience working in D, though I have researched the
    language - it has some nice features, and it was willing to break more compatibility with C in pursuit of a cleaner language than C++ was. But there's a lot it can't do for my needs, and C++ is a far better choice
    for my work. However, apart from D's use of modules rather than an
    "include" mechanism (C++ has modules now, but they are not common in
    practice as yet), I don't believe there are any features of D that
    replace C preprocessor uses where there is not a direct equivalent in
    C++. So I believe that my use of the C preprocessor in my C++
    programming is a reasonable model for where I think the preprocessor is
    useful in C++, and where I would miss it if I were to program in D.

    I'm excluding includes and their guards, and anything relating to mixing
    C and C++ (even though that is a very important feature in my work, and
    it relies heavily on the preprocessor).

    I use macros for things like "Assert" and "Panic", where the controlling expression gets "stringified" and the function name, file and line
    numbers are included in the message that is printed and stored in logs.
    You can't do that without a preprocessor, unless the language supports a
    level of reflection well beyond the very limited forms found in D and
    C++ (and even the proposals for C++).

    I use macros for giving neater names to things that can't be made as
    functions, constants, etc., such as gcc __attributes__ or C++
    attributes, or lists of pragmas, especially in connection with
    conditional compilation to adapt to different compilers or platforms.

    I use macros if I need to replace something temporarily, such as for
    debugging or tracing part of a program.

    I use the preprocessor for generating unique names for things that need
    unique names for the language syntax, but for which I will never refer
    to by name.

    The most complex preprocessor stuff I have is probably use of so-called "x-macros". I've used these to build simple command-line interfaces
    (with commands, sub-commands, and parameters, along with help text) and hierarchical menu systems. Even with templates and compile-time
    functions in D and C++, you are faced with a lot of duplication and
    run-time generation of structures if you don't use the preprocessor.

    These are all - without any doubt - "sensible" uses of the preprocessor
    that are not replaced at all in D or the C++ core language. Of course
    it is always possible to live without them, but why would anyone want to
    if the preprocessor is the best tool for the job?


    But I tend to come across the ones where macros are overused or abused.
    Some people seem to delight in doing so.

    Any feature that can be used, can be overused or abused. And there will
    always be people who delight in writing smart-arse code, as well as
    people who write bad code unintentionally. Macros are in no way
    special, and banning them does not make code better.


    When working the Lua sources a few months ago, that makes heavy uses of macros defined in lower case which look just like functions calls.


    That's fine, if they also act just like function calls. The all-caps convention is - IMHO - a terrible idea, and makes code unnecessarily
    hard to read. All-caps names should be reserved for macros that /don't/
    act the way they appear - not for simple constant defines, or
    function-like macros that act like functions. They should be used as a
    warning that something special is going on.

    One complex expression that my compiler had trouble with, when expanded,
     resulted in a line hundreds of character long, and using 11 levels of nested parentheses, the most I've ever come across.


    That's great - something that can help you find bugs or unnecessary
    limits in your compiler.

    I really doubt that the authors would have written code that convoluted
    if macros had not been available.


    They didn't write convoluted code - they use the tools the C language
    provides to write code that is the best they could, for whatever value
    of "best" they were using (most efficient, most maintainable, most
    portable, etc.).


    Typedefs are scoped, string
    macros are not.

    True.  Sometimes that is an advantage, sometimes a disadvantage.

    When it is an advantage?

    If you want to have something that works outside of scopes, being
    unscoped is an advantage. It does not happen often, but it happens.
    Maybe you've got functions that needs a lot of calculations, using lines
    that follow a similar pattern. Putting those patterns in a macro saves repetition in the source code, reduces the risk of errors, and makes the
    whole thing clearer. But if the identifiers in the pattern have
    different scopes when they are used (perhaps they are in different
    functions), you can take advantage of macros' independence of scopes to
    avoid having to pass local data as parameters.


    My systems language only acquired macros with parameters a year or two
    ago. They can only be used to expand to well-formed terms of
    expressions, and are used very sparingly.

    They have normal scoping, so can be shadowed or have distinct versions
    in different scopes and functions can define their own macros; they can
    be imported or exported just like functions and variables.


    So why not just use functions? Or implement more advanced core language features, like templates?

    The point of C preprocessor macros - the reason that they are useful in
    ways that cannot be handled by core language features - is that they are
    purely textual. They exist outside of scoping, and language syntax.
    They can have unmatched brackets, or construct identifiers on the fly,
    or do all kinds of manipulation of code. That allows for very powerful
    uses - and, of course, abuses.

    It is certainly the case that some common uses of macros in C have been
    made redundant by better language features in C++, D, and even in later versions of C. Most common uses of #define'd constants are better
    handled by "static const" or "enum". Most function-like macros are
    better handled by static inline functions, or C++/D templates. Ugly
    C90/C99 style static_assert macros are best done with real
    _Static_assert from C11. Many "tricks" that previously needed macros to
    get efficient code generation are made unnecessary by modern optimising compilers.

    So if you compare decades-old C code with modern C++, you should see a
    dramatic reduction in macros and pre-processor usage. Once C++ modules
    gain common usage, another big pre-processor usage will go. But there
    will still be uses for it, and situations where it really does give the
    best way to write the source code.

    The sorts of macros that C has seem stone age by comparison. And crude,
    in being able to define random bits of syntax, or synthesise tokens from multiple parts.

    You say "crude", others say "powerful" and "flexible". The others would
    be right.


    Some people will obviously love that, but imagine trying to search for
    some token with a text editor, but it won't find it because it only
    exists after preprocessing.


    Imagine /not/ having macros, and having to type out those tokens again
    and again, when a macro could be defined once. Then imagine wanting to
    change the tokens, and having to do so everywhere in the code instead of
    once in the definition.

    It is not often that you get a free lunch - power and flexibility in one
    way will often limit things in other ways. There are always tradeoffs,
    with all features, in all languages - I would have thought you might
    have learned that by now.


    Like any powerful tool, macros can be abused or misused, leading to
    poorer results - but that does not mean they are "nothing but trouble".

    I wouldn't use the work 'powerful'; 'dangerous' and 'crazy' might be
    more apt.

    You are scared to learn, and scared that other people see and do things differently, with the result that you limit yourself pointlessly.


    Imagine a CPP that could also map one letter another; more 'powerful',
    or just more 'crazy'?


    There /are/ preprocessors that can deal with single letters. They can
    be useful when dealing with code in different character sets, such as converting back and forth between non-ASCII unicode characters and \u sequences.

    It is poor /uses/ of the C preprocessor that is "crazy" - not the
    preprocessor or good uses of it. The same applies to every other
    feature of every (real) programming language ever made.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to Lawrence D'Oliveiro on Tue Jan 2 12:24:45 2024
    On 02/01/2024 06:47, Lawrence D'Oliveiro wrote:
    On Tue, 2 Jan 2024 06:23:03 -0000 (UTC), Kaz Kylheku wrote:

    On 2024-01-02, Lawrence D'Oliveiro <ldo@nz.invalid> wrote:

    First of all, it includes code for building on a whole lot of
    proprietary Unix systems with non-GCC compilers, some of which maybe
    don’t quite conform to official C/C++ standards.

    When people use Autoconf to configure their program for building, the
    generated script includes cruft for platforms on which they never tested
    the program, and never will, and on which it won't work for reasons not
    related to those tests.

    This is a GNU project, so you can be sure they have done pretty good
    testing on those platforms and those options. If they were no longer supported, they would have been dropped from the code. This isn’t like proprietary software, which tends to accumulate cruft that nobody dares
    touch because they no longer understand what it does.

    Look at the ABI options: maybe you want a 32-bit build (where the CPU
    supports it) rather than a 64-bit one; on some architectures, things
    get a bit more complicated than just those two options.

    These kinds of options can be passed in by CFLAGS, LDFLAGS and LDLIBS.
    Support those three variables, and that's it.

    If you have a look, that’s what the script does. Only you just have to specify one option, and it generates the right value for *all* the
    relevant variables, instead of you having to specify them individually.


    You're going to defend this to the death aren't you? Be funny if at some
    point some GMP II was produced whose main new benefit was a vastly
    simplified build!

    I tried to build gmp today using WSL. The process took just under five
    minutes. However, where and what is the output? For all the verbosity,
    that simple fact is omitted.

    By doing searches, I found a bunch of libxxx.a files with today's date
    in various locations.

    So the outputs are archive files for gcc, I guess intended for static
    linking. And full of code using SYS V ABI. But never mind that tiny detail.

    The INSTALL file talks about reading detailed instructions in gmp_info,
    but this file is gobbledygook. You need to view the instructions using a program called 'info' - a Linux utility that doesn't exist on Windows.

    So I need Linux even just to look at a bunch of instructions?

    Anyway, now that some extra files are in place, I had a go trying to
    compile some individual files. Here there is another suprise:

    c:\gmp2>dir gmp-mparam.h
    02/01/2024 11:56 <JUNCTION> gmp-mparam.h [...]

    One of the header files it needs is this mysterious 'junction' file. I
    can't even see inside it:

    c:\gmp2>type gmp-mparam.h
    The file cannot be accessed by the system.

    WTH? I can do 'cat' in WSL and redirect it to a normal file, but this is
    well weird.



    The following are the checks reported by ./configure. I can report that
    I have gcc installed (on this Linux system), and that it has 'string.h'.
    Phew! God knows what would have happened if I'd attempted to build and
    string.h was missing.

    So, what does it do with that info? What actually happens if string
    /was/ missing? What, God forbid, if 'gcc -E' was not supported?

    And why aren't these checks everytime you build /any/ program?

    Oh, and I apparently don't have Bison or Lex installed. Now /that/ was
    worth checking.

    What a total waste of time. Even if it was somehow justified, if I then
    tried to build another app with its own configure script, it's going to
    do all those same checks again, even though nothing as changed.

    (If you try and argue that they /might/ have changed, well, they have
    changed between running ./configure, and starting 'make'?)

    GMP, when it's actually made available, is actually an incredibly fast
    library. Some talented people went into creating it. I can't say the
    same for those responsible for the build system who seem to have made it
    as complicated and convoluted as they possibly can.



    =============================================


    checking build system type... zen-pc-linux-gnu
    checking host system type... zen-pc-linux-gnu
    checking for a BSD-compatible install... /usr/bin/install -c
    checking whether build environment is sane... yes
    checking for a thread-safe mkdir -p... /usr/bin/mkdir -p
    checking for gawk... gawk
    checking whether make sets $(MAKE)... yes
    checking whether make supports nested variables... yes
    checking whether to enable maintainer-specific portions of Makefiles... no checking ABI=64
    checking compiler gcc -O2 -pedantic -fomit-frame-pointer -m64 ... yes
    checking compiler gcc -O2 -pedantic -fomit-frame-pointer -m64
    -mtune=znver1... yes
    checking compiler gcc -O2 -pedantic -fomit-frame-pointer -m64
    -mtune=znver1 -march=znver1... yes
    checking for gcc... gcc
    checking whether the C compiler works... yes
    checking for C compiler default output file name... a.out
    checking for suffix of executables...
    checking whether we are cross compiling... no
    checking for suffix of object files... o
    checking whether we are using the GNU C compiler... yes
    checking whether gcc accepts -g... yes
    checking for gcc option to accept ISO C89... none needed
    checking whether gcc understands -c and -o together... yes
    checking for gcc option to accept ISO C99... none needed
    checking how to run the C preprocessor... gcc -E
    checking build system compiler gcc... yes
    checking for build system preprocessor... gcc -E
    checking for build system executable suffix...
    checking whether build system compiler is ANSI... yes
    checking for build system compiler math library... -lm
    checking for grep that handles long lines and -e... /usr/bin/grep
    checking for egrep... /usr/bin/grep -E
    using ABI="64"
    CC="gcc"
    CFLAGS="-O2 -pedantic -fomit-frame-pointer -m64 -mtune=znver1 -march=znver1"
    CPPFLAGS=""
    MPN_PATH=" x86_64/zen x86_64 generic"
    checking whether assembler supports --noexecstack option... yes
    checking for ar... ar
    checking for BSD- or MS-compatible name lister (nm)... /usr/bin/nm -B
    checking the name lister (/usr/bin/nm -B) interface... BSD nm
    checking how to print strings... printf
    checking for a sed that does not truncate output... /usr/bin/sed
    checking for fgrep... /usr/bin/grep -F
    checking for ld used by gcc... /usr/bin/ld
    checking if the linker (/usr/bin/ld) is GNU ld... yes
    checking whether ln -s works... yes
    checking the maximum length of command line arguments... 1572864
    checking how to convert zen-pc-linux-gnu file names to zen-pc-linux-gnu format... func_convert_file_noop
    checking how to convert zen-pc-linux-gnu file names to toolchain
    format... func_convert_file_noop
    checking for /usr/bin/ld option to reload object files... -r
    checking for objdump... objdump
    checking how to recognize dependent libraries... pass_all
    checking for dlltool... dlltool
    checking how to associate runtime and link libraries... printf %s\n
    checking for archiver @FILE support... @
    checking for strip... strip
    checking for ranlib... ranlib
    checking command to parse /usr/bin/nm -B output from gcc object... ok
    checking for sysroot... no
    checking for a working dd... /usr/bin/dd
    checking how to truncate binary pipes... /usr/bin/dd bs=4096 count=1
    checking for mt... mt
    checking if mt is a manifest tool... no
    checking for ANSI C header files... yes
    checking for sys/types.h... yes
    checking for sys/stat.h... yes
    checking for stdlib.h... yes
    checking for string.h... yes
    checking for memory.h... yes
    checking for strings.h... yes
    checking for inttypes.h... yes
    checking for stdint.h... yes
    checking for unistd.h... yes
    checking for dlfcn.h... yes
    checking for objdir... .libs
    checking if gcc supports -fno-rtti -fno-exceptions... no
    checking for gcc option to produce PIC... -fPIC -DPIC
    checking if gcc PIC flag -fPIC -DPIC works... yes
    checking if gcc static flag -static works... yes
    checking if gcc supports -c -o file.o... yes
    checking if gcc supports -c -o file.o... (cached) yes
    checking whether the gcc linker (/usr/bin/ld) supports shared
    libraries... yes
    checking whether -lc should be explicitly linked in... no
    checking dynamic linker characteristics... GNU/Linux ld.so
    checking how to hardcode library paths into programs... immediate
    checking whether stripping libraries is possible... yes
    checking if libtool supports shared libraries... yes
    checking whether to build shared libraries... yes
    checking whether to build static libraries... yes
    checking for ANSI C header files... (cached) yes
    checking whether time.h and sys/time.h may both be included... yes
    checking fcntl.h usability... yes
    checking fcntl.h presence... yes
    checking for fcntl.h... yes
    checking float.h usability... yes
    checking float.h presence... yes
    checking for float.h... yes
    checking invent.h usability... no
    checking invent.h presence... no
    checking for invent.h... no
    checking langinfo.h usability... yes
    checking langinfo.h presence... yes
    checking for langinfo.h... yes
    checking locale.h usability... yes
    checking locale.h presence... yes
    checking for locale.h... yes
    checking nl_types.h usability... yes
    checking nl_types.h presence... yes
    checking for nl_types.h... yes
    checking sys/attributes.h usability... no
    checking sys/attributes.h presence... no
    checking for sys/attributes.h... no
    checking sys/iograph.h usability... no
    checking sys/iograph.h presence... no
    checking for sys/iograph.h... no
    checking sys/mman.h usability... yes
    checking sys/mman.h presence... yes
    checking for sys/mman.h... yes
    checking sys/param.h usability... yes
    checking sys/param.h presence... yes
    checking for sys/param.h... yes
    checking sys/processor.h usability... no
    checking sys/processor.h presence... no
    checking for sys/processor.h... no
    checking sys/pstat.h usability... no
    checking sys/pstat.h presence... no
    checking for sys/pstat.h... no
    checking sys/sysinfo.h usability... yes
    checking sys/sysinfo.h presence... yes
    checking for sys/sysinfo.h... yes
    checking sys/syssgi.h usability... no
    checking sys/syssgi.h presence... no
    checking for sys/syssgi.h... no
    checking sys/systemcfg.h usability... no
    checking sys/systemcfg.h presence... no
    checking for sys/systemcfg.h... no
    checking sys/time.h usability... yes
    checking sys/time.h presence... yes
    checking for sys/time.h... yes
    checking sys/times.h usability... yes
    checking sys/times.h presence... yes
    checking for sys/times.h... yes
    checking for sys/resource.h... yes
    checking for sys/sysctl.h... yes
    checking for machine/hal_sysinfo.h... no
    checking whether fgetc is declared... yes
    checking whether fscanf is declared... yes
    checking whether optarg is declared... yes
    checking whether ungetc is declared... yes
    checking whether vfprintf is declared... yes
    checking whether sys_errlist is declared... yes
    checking whether sys_nerr is declared... yes
    checking return type of signal handlers... void
    checking for intmax_t... yes
    checking for long double... yes
    checking for long long... yes
    checking for ptrdiff_t... yes
    checking for quad_t... yes
    checking for uint_least32_t... yes
    checking for intptr_t... yes
    checking for working volatile... yes
    checking for C/C++ restrict keyword... __restrict
    checking whether gcc __attribute__ ((const)) works... yes
    checking whether gcc __attribute__ ((malloc)) works... yes
    checking whether gcc __attribute__ ((mode (XX))) works... yes
    checking whether gcc __attribute__ ((noreturn)) works... yes
    checking whether gcc hidden aliases work... yes
    checking for inline... inline
    checking for cos in -lm... yes
    checking for working alloca.h... yes
    checking for alloca (via gmp-impl.h)... yes
    checking how to allocate temporary memory... alloca
    checking whether byte ordering is bigendian... no
    checking format of `double' floating point... IEEE little endian
    checking for alarm... yes
    checking for attr_get... no
    checking for clock... yes
    checking for cputime... no
    checking for getpagesize... yes
    checking for getrusage... yes
    checking for gettimeofday... yes
    checking for getsysinfo... no
    checking for localeconv... yes
    checking for memset... yes
    checking for mmap... yes
    checking for mprotect... yes
    checking for nl_langinfo... yes
    checking for obstack_vprintf... yes
    checking for popen... yes
    checking for processor_info... no
    checking for pstat_getprocessor... no
    checking for raise... yes
    checking for read_real_time... no
    checking for sigaction... yes
    checking for sigaltstack... yes
    checking for sigstack... yes
    checking for syssgi... no
    checking for strchr... yes
    checking for strerror... yes
    checking for strnlen... yes
    checking for strtol... yes
    checking for strtoul... yes
    checking for sysconf... yes
    checking for sysctl... yes
    checking for sysctlbyname... no
    checking for times... yes
    checking for library containing clock_gettime... none required
    checking for vsnprintf... yes
    checking whether vsnprintf works... yes
    checking whether sscanf needs writable input... no
    checking for struct pst_processor.psp_iticksperclktick... no
    checking for suitable m4... m4
    checking if m4wrap produces spurious output... no
    checking how to switch to text section... .text
    checking how to switch to data section... .data
    checking for assembler label suffix... :
    checking for assembler global directive... .globl
    checking for assembler global directive attribute...
    checking if globals are prefixed by underscore... no
    checking how to switch to read-only data section... .section .rodata checking for assembler .type directive... .type $1,@$2
    checking for assembler .size directive... .size $1,$2
    checking for assembler local label prefix... .L
    checking for assembler byte directive... .byte
    checking how to define a 32-bit word... .long
    checking if .align assembly directive is logarithmic... no
    checking if the .align directive accepts an 0x90 fill in .text... yes
    checking if the assembler knows about the mulx instruction... yes
    checking for assembler COFF type directives... no
    checking size of void *... 8
    checking size of unsigned short... 2
    checking size of unsigned... 4
    checking size of unsigned long... 8
    checking size of mp_limb_t... 8
    checking for stack_t... yes
    checking for tputs in -lncurses... yes
    checking for readline in -lreadline... yes
    checking readline/readline.h usability... yes
    checking readline/readline.h presence... yes
    checking for readline/readline.h... yes
    checking readline/history.h usability... yes
    checking readline/history.h presence... yes
    checking for readline/history.h... yes
    checking readline detected... yes
    checking for bison... no
    checking for byacc... no
    checking for flex... no
    checking for lex... no

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Blue-Maned_Hawk@21:1/5 to Bart on Tue Jan 2 15:10:16 2024
    Bart wrote:

    One complex expression that my compiler had trouble with, when expanded,
    resulted in a line hundreds of character long, and using 11 levels of nested parentheses, the most I've ever come across.

    Happen to remember particularly which that one was you?



    --
    Blue-Maned_Hawk│shortens to Hawk│/blu.mɛin.dʰak/ │he/him/his/himself/Mr.
    blue-maned_hawk.srht.site
    A resident complained about a neighbor blowing a duck horn. The police informed him it was a crow.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Blue-Maned_Hawk@21:1/5 to David Brown on Tue Jan 2 15:04:02 2024
    David Brown wrote:

    When working the Lua sources a few months ago, that makes heavy uses of
    macros defined in lower case which look just like functions calls.


    That's fine, if they also act just like function calls.

    There'sn't any such thing.



    --
    Blue-Maned_Hawk│shortens to Hawk│/blu.mɛin.dʰak/ │he/him/his/himself/Mr.
    blue-maned_hawk.srht.site
    20% of the things cause 80% of the bad.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to David Brown on Tue Jan 2 16:12:32 2024
    On 02/01/2024 10:42, David Brown wrote:
    On 01/01/2024 16:54, Bart wrote:


    I use macros for things like "Assert" and "Panic", where the controlling expression gets "stringified" and the function name, file and line
    numbers are included in the message that is printed and stored in logs.
    You can't do that without a preprocessor, unless the language supports a level of reflection well beyond the very limited forms found in D and
    C++ (and even the proposals for C++).

    I use macros for giving neater names to things that can't be made as functions, constants, etc., such as gcc __attributes__ or C++
    attributes, or lists of pragmas, especially in connection with
    conditional compilation to adapt to different compilers or platforms.

    I use macros if I need to replace something temporarily, such as for debugging or tracing part of a program.

    I use the preprocessor for generating unique names for things that need unique names for the language syntax, but for which I will never refer
    to by name.

    The most complex preprocessor stuff I have is probably use of so-called "x-macros".  I've used these to build simple command-line interfaces
    (with commands, sub-commands, and parameters, along with help text) and hierarchical menu systems.

    X-macro are ugly. They are unreadable. At best, if someone has already
    done the hard work of setting up a working set of macros, then you will
    be able to see how to modify, add or delete entries.

    For the purposes of creating parallel sets of enums and associated data,
    I use special syntax which makes for a clearer and simpler feature.

    Have you ever considered that if C didn't have macros, or they weren't powerful, then it could have evolved superior, built-in alternatives?


    One complex expression that my compiler had trouble with, when
    expanded,   resulted in a line hundreds of character long, and using
    11 levels of nested parentheses, the most I've ever come across.


    That's great - something that can help you find bugs or unnecessary
    limits in your compiler.

    It wasn't a compiler limit - it was mine! The resulting expression was completely unreadable. In the end I resorted to looking at the AST as
    that was clearer than C source code.

    If you want to have something that works outside of scopes, being
    unscoped is an advantage.  It does not happen often, but it happens.
    Maybe you've got functions that needs a lot of calculations, using lines
    that follow a similar pattern.  Putting those patterns in a macro saves repetition in the source code, reduces the risk of errors, and makes the whole thing clearer.  But if the identifiers in the pattern have
    different scopes when they are used (perhaps they are in different functions), you can take advantage of macros' independence of scopes to
    avoid having to pass local data as parameters.

    The scope I'm talking about is the name of the macro, not that of the
    macro parameters.


    They have normal scoping, so can be shadowed or have distinct versions
    in different scopes and functions can define their own macros; they
    can be imported or exported just like functions and variables.


    So why not just use functions?

    There are a few uses where functions won't work or would be inefficient.
    My macros are easier to implement than inlined functions.

    Half my use-cases involve inline assembly. Others involve creating
    aliases for things like module names:

    module mm_mcldecls as md

    Now I can use md.F to disambiguate F instead of mm_mcldecls.F (when F is exported by more than one module). Here the macro mechanism is used
    internally.

    The alternative here would be a special-purpose 'alias' feature but this
    seems to work too.

      Or implement more advanced core language
    features, like templates?

    The point of C preprocessor macros - the reason that they are useful in
    ways that cannot be handled by core language features - is that they are purely textual.  They exist outside of scoping, and language syntax.
    They can have unmatched brackets, or construct identifiers on the fly,
    or do all kinds of manipulation of code.  That allows for very powerful
    uses - and, of course, abuses.

    Even when not being abused, their very use can cause problems, for
    example when they appear in APIs for libraries that could be used across
    an FFI.

    Macros are a C language artefact, and what they expand to is arbitrary
    C syntax that can be meaningless elsewhere.

    (When I processed the GTK headers from 350,000 lines of C to 25,000
    lines in my syntax, the last 4,000 lines were C macros that weren't identifiable as simple named literals (eg #define A 100) and that would
    need dealing with manually.)


    It is certainly the case that some common uses of macros in C have been
    made redundant by better language features in C++, D, and even in later versions of C.  Most common uses of #define'd constants are better
    handled by "static const" or "enum".  Most function-like macros are
    better handled by static inline functions, or C++/D templates.  Ugly
    C90/C99 style static_assert macros are best done with real
    _Static_assert from C11.  Many "tricks" that previously needed macros to
    get efficient code generation are made unnecessary by modern optimising compilers.

    So if you compare decades-old C code with modern C++, you should see a dramatic reduction in macros and pre-processor usage.
    I haven't noticed. Things are bad enough now; are you saying they were
    worse?

    You say "crude", others say "powerful" and "flexible".  The others would
    be right.

    It's crude for many reasons, here's one:

    #define x y

    That is intended to replace instances of the top level name 'x' with
    'y'. But in C, it also replaces 'x' in 'p.x' with 'p.y'.

    In fact, any macro name you create is at risk of clashing with struct
    member names. Such names are usually considered safe in a private
    namespace; not in C!


    Some people will obviously love that, but imagine trying to search for
    some token with a text editor, but it won't find it because it only
    exists after preprocessing.


    Imagine /not/ having macros, and having to type out those tokens again
    and again, when a macro could be defined once.  Then imagine wanting to change the tokens, and having to do so everywhere in the code instead of
    once in the definition.

    I don't like macros, even the advanced ones in modern languages. I
    consider them an anti-feature. And I did without them for decades, other
    than for a time, simple ones with no parameters were used.

    The parameterised ones I have now are an experiment. But every time I
    use one, I get the same feeling as when I write 'goto'.

    It is not often that you get a free lunch - power and flexibility in one
    way will often limit things in other ways.  There are always tradeoffs,
    with all features, in all languages - I would have thought you might
    have learned that by now.

    It's not a free lunch. A full CPP is quite difficult to implement,
    possibly harder than C itself. The one I have is perhaps 90% there; it
    will not do esoteric stuff.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Blue-Maned_Hawk@21:1/5 to Lawrence D'Oliveiro on Tue Jan 2 15:15:47 2024
    Lawrence D'Oliveiro wrote:

    On Sun, 31 Dec 2023 22:57:12 -0800, Chris M. Thomasson wrote:

    It was just an example of some fairly hard core macro magic.

    String-based macros aren’t “magic”, they’re just sad.

    Preëmptive apologies for GitHub link, but: <https://github.com/Hirrolot/ datatype99>

    See also (relevance partial only): <https://www.libcello.org/>



    --
    Blue-Maned_Hawk│shortens to Hawk│/blu.mɛin.dʰak/ │he/him/his/himself/Mr.
    blue-maned_hawk.srht.site
    There is nothing to catch in the magma pipe.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to All on Tue Jan 2 16:38:37 2024
    On 02/01/2024 15:10, Blue-Maned_Hawk wrote:
    Bart wrote:

    One complex expression that my compiler had trouble with, when expanded,
    resulted in a line hundreds of character long, and using 11 levels of
    nested parentheses, the most I've ever come across.

    Happen to remember particularly which that one was you?




    No. You can have a look yourself if you like, just apply -E to the sources.

    Here's one with quite a long expansion but not too deeply nested. This
    is the line inside lvm.c at line 1463:

    op_arith(L, l_addi, luai_numadd);

    The expansion is:

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

    This is the definition of that macro:

    #define op_arith(L,iop,fop) { \
    TValue *v1 = vRB(i); \
    TValue *v2 = vRC(i); \
    op_arith_aux(L, v1, v2, iop, fop); }

    were 'op_arith_aux' is another macro. Actually, probably everything is.

    This looks fun to try to understand, debug, or port.

    Here you might well ask why inlined functions weren't better suited.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Bart on Tue Jan 2 18:34:12 2024
    On 02/01/2024 17:12, Bart wrote:
    On 02/01/2024 10:42, David Brown wrote:
    On 01/01/2024 16:54, Bart wrote:


    I use macros for things like "Assert" and "Panic", where the
    controlling expression gets "stringified" and the function name, file
    and line numbers are included in the message that is printed and
    stored in logs. You can't do that without a preprocessor, unless the
    language supports a level of reflection well beyond the very limited
    forms found in D and C++ (and even the proposals for C++).

    I use macros for giving neater names to things that can't be made as
    functions, constants, etc., such as gcc __attributes__ or C++
    attributes, or lists of pragmas, especially in connection with
    conditional compilation to adapt to different compilers or platforms.

    I use macros if I need to replace something temporarily, such as for
    debugging or tracing part of a program.

    I use the preprocessor for generating unique names for things that
    need unique names for the language syntax, but for which I will never
    refer to by name.

    The most complex preprocessor stuff I have is probably use of
    so-called "x-macros".  I've used these to build simple command-line
    interfaces (with commands, sub-commands, and parameters, along with
    help text) and hierarchical menu systems.

    X-macro are ugly. They are unreadable.

    Speaking as someone who has used them in real code, rather than someone
    with a pathological hatred of macros and who prefers knee-jerk reaction
    to their uses instead of applying a few minutes objective thought,
    "x-macros" can make code significantly simpler, clearer, and much easier
    to maintain correctly.

    At best, if someone has already
    done the hard work of setting up a working set of macros, then you will
    be able to see how to modify, add or delete entries.

    They are not hard. Perhaps you don't actually understand what is
    usually meant by "x-macros" ?


    For the purposes of creating parallel sets of enums and associated data,
    I use special syntax which makes for a clearer and simpler feature.

    And that is, obviously, utterly useless - because it is not C or even a commonly supported extension. Oh, and even if it /were/ part of C, it
    would not help because that's not what I was doing.


    Have you ever considered that if C didn't have macros, or they weren't powerful, then it could have evolved superior, built-in alternatives?


    The C preprocessor is part of C - it is already built in.

    But I have already mentioned, several times, how other language features
    of C and C++ are better choices than macros for the uses that they
    cover. Or does your desperate need to rant against C and macros blind
    you to reading other people's posts? (I'd appreciate an answer here -
    it will let me know if there is any point in trying to educate you or
    answer your questions.)



    One complex expression that my compiler had trouble with, when
    expanded,   resulted in a line hundreds of character long, and using
    11 levels of nested parentheses, the most I've ever come across.


    That's great - something that can help you find bugs or unnecessary
    limits in your compiler.

    It wasn't a compiler limit - it was mine! The resulting expression was completely unreadable.

    I wonder if the code authors also found this expanded expression
    unreadable. I guess not - because they used macros so that they could
    write it in a clear and understandable way, and never have to look at
    the expansion! You don't seem to comprehend the point of macros at all.

    In the end I resorted to looking at the AST as
    that was clearer than C source code.


    Why not just look at the code as written, if you want to understand it?
    Or would doing something that sensible put a damper on your rants?

    If you want to have something that works outside of scopes, being
    unscoped is an advantage.  It does not happen often, but it happens.
    Maybe you've got functions that needs a lot of calculations, using
    lines that follow a similar pattern.  Putting those patterns in a
    macro saves repetition in the source code, reduces the risk of errors,
    and makes the whole thing clearer.  But if the identifiers in the
    pattern have different scopes when they are used (perhaps they are in
    different functions), you can take advantage of macros' independence
    of scopes to avoid having to pass local data as parameters.

    The scope I'm talking about is the name of the macro, not that of the
    macro parameters.


    I was talking about the scope of parameters and any identifiers in the
    macro definition.

    For the name of the macro itself, I can agree that I don't see any
    advantage in it being scope-free. I can't think off-hand of a situation
    where it would be particularly useful to define a macro inside a block
    scope in a function and have it work outside that scope too. (It's
    obvious why macro names cannot be scoped.)


    They have normal scoping, so can be shadowed or have distinct
    versions in different scopes and functions can define their own
    macros; they can be imported or exported just like functions and
    variables.


    So why not just use functions?

    There are a few uses where functions won't work or would be inefficient.

    If functions are inefficient, that is a weakness in your compilation.
    Don't you do any kind of inlining or inter-procedural optimisations? I
    realise these are not easy to implement, but the potential gains are significant.

    My macros are easier to implement than inlined functions.


    So they are like a limited form of inline functions? Okay, that might
    be useful to get better code from a weaker compiler, but that's hardly a benefit compared to real macros.

    Half my use-cases involve inline assembly.

    Why can't that be in inlined functions? Again, you are not showing that
    these limited macros have any benefits compared to what is found in C,
    you are merely saying you have something that partially counters the limitations of your tools.

    (And to be clear - I fully appreciate that making advanced compiler optimisations is very difficult, and this is a good solution for you
    when making everything yourself. But it is not a positive feature or
    selling point for your language - it is a workaround for practical limitations.)

    Others involve creating
    aliases for things like module names:

        module mm_mcldecls as md

    Now I can use md.F to disambiguate F instead of mm_mcldecls.F (when F is exported by more than one module). Here the macro mechanism is used internally.

    Aliases in module imports are common (and useful) in many languages.
    They are not "macros" in any sense.


    The alternative here would be a special-purpose 'alias' feature but this seems to work too.


    It is completely irrelevant how this is all implemented internally. It
    doesn't matter if it is done using the same bit of code that handles
    your limited macros, or if it is something else entirely. It only
    matters how it works to the language user - and it's a module alias, not
    a macro.

      Or implement more advanced core language features, like templates?

    The point of C preprocessor macros - the reason that they are useful
    in ways that cannot be handled by core language features - is that
    they are purely textual.  They exist outside of scoping, and language
    syntax. They can have unmatched brackets, or construct identifiers on
    the fly, or do all kinds of manipulation of code.  That allows for
    very powerful uses - and, of course, abuses.

    Even when not being abused, their very use can cause problems, for
    example when they appear in APIs for libraries that could be used across
    an FFI.


    C code is written for use in C. Limitations or quirks of other
    languages that try to interface to that code are not the responsibility
    of the C language.

    People writing code in one language with the intention of having it
    reusable in other languages have a responsibility to try to make this
    simpler. (Just as people writing reusable library code have a
    responsibility to document the code more than they might for a
    self-contained project.)

    Macros are a C language artefact, and what they expand to is arbitrary C syntax that can be meaningless elsewhere.

    Yes - C code is C code. It is not Pascal, or Fortran, or Ada. Should
    this be a surprise?


    (When I processed the GTK headers from 350,000 lines of C to 25,000
    lines in my syntax, the last 4,000 lines were C macros that weren't identifiable as simple named literals (eg #define A 100) and that would
    need dealing with manually.)


    It is certainly the case that some common uses of macros in C have
    been made redundant by better language features in C++, D, and even in
    later versions of C.  Most common uses of #define'd constants are
    better handled by "static const" or "enum".  Most function-like macros
    are better handled by static inline functions, or C++/D templates.
    Ugly C90/C99 style static_assert macros are best done with real
    _Static_assert from C11.  Many "tricks" that previously needed macros
    to get efficient code generation are made unnecessary by modern
    optimising compilers.

    So if you compare decades-old C code with modern C++, you should see a
    dramatic reduction in macros and pre-processor usage.
    I haven't noticed. Things are bad enough now; are you saying they were
    worse?


    You already have a habit of cherry-picking example code that is as "Bart-unfriendly" as you can find. And of course you'd really get your knickers in a twist if you looked at modern C++ code, even if it had
    little or no macros.

    You say "crude", others say "powerful" and "flexible".  The others
    would be right.

    It's crude for many reasons, here's one:

       #define x y

    That is intended to replace instances of the top level name 'x' with
    'y'. But in C, it also replaces 'x' in 'p.x' with 'p.y'.


    Yes. If you don't like that, don't make such a macro.

    In fact, any macro name you create is at risk of clashing with struct
    member names. Such names are usually considered safe in a private
    namespace; not in C!

    C has quite limited namespaces, and you have to be careful to avoid
    clashes. Live with it - other people do. (Better namespaces is one of
    the reasons I prefer C++.)



    Some people will obviously love that, but imagine trying to search
    for some token with a text editor, but it won't find it because it
    only exists after preprocessing.


    Imagine /not/ having macros, and having to type out those tokens again
    and again, when a macro could be defined once.  Then imagine wanting
    to change the tokens, and having to do so everywhere in the code
    instead of once in the definition.

    I don't like macros, even the advanced ones in modern languages. I
    consider them an anti-feature. And I did without them for decades, other
    than for a time, simple ones with no parameters were used.

    The parameterised ones I have now are an experiment. But every time I
    use one, I get the same feeling as when I write 'goto'.

    It is not often that you get a free lunch - power and flexibility in
    one way will often limit things in other ways.  There are always
    tradeoffs, with all features, in all languages - I would have thought
    you might have learned that by now.

    It's not a free lunch.

    Exactly my point. (I can see how my wording could be misinterpreted
    here as suggesting that macros /are/ a free lunch. Sorry about that.)

    A full CPP is quite difficult to implement,
    possibly harder than C itself.

    I don't have your experience, but I find that /very/ hard to believe. I
    should imagine it's primarily a matter of working through the
    "translation phases" and "preprocessing directives" described in the C standards, step by step. There's plenty of scope for making things
    harder by aiming for greater efficiency and/or fewer passes through the
    code, but that's a matter of choice, and it pales in comparison to the
    scope for complications and optimisations in the main part of the
    compilation.

    The one I have is perhaps 90% there; it
    will not do esoteric stuff.


    Such as?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Lawrence D'Oliveiro on Tue Jan 2 18:16:36 2024
    On 2023-12-31, Lawrence D'Oliveiro <ldo@nz.invalid> wrote:
    On Sun, 31 Dec 2023 16:25:08 +0100, David Brown wrote:

    I realise that you (and possibly others) might find it useful for a tool
    to replace typedef identifiers with their definitions, but it could only
    be done for some cases, and is not as simple as macro substitution.

    String-based macros are nothing but trouble. Typedefs are scoped, string macros are not.

    If you want to see the right way to do macros, look at LISP, where they
    are token-based, and much more robust as as result. I think they even
    manage to apply scoping rules to macro definitions as well.

    By the way, I'm not sure what system you are referring to by
    "string-based macros", but in case you might be thinking of C
    preprocessing, be advised that it's token-based.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to Kaz Kylheku on Tue Jan 2 19:05:06 2024
    On Tue, 2 Jan 2024 18:16:36 -0000 (UTC), Kaz Kylheku wrote:

    By the way, I'm not sure what system you are referring to by
    "string-based macros", but in case you might be thinking of C
    preprocessing, be advised that it's token-based.

    Now go learn what “homoiconicity” means.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to Bart on Tue Jan 2 19:04:16 2024
    On Tue, 2 Jan 2024 12:24:45 +0000, Bart wrote:

    You're going to defend this to the death aren't you? Be funny if at some point some GMP II was produced whose main new benefit was a vastly
    simplified build!

    Well, you can go back to dreaming if you wish. It’s not like this project
    was just created yesterday: if it was going to pay more serious attention
    to Microsoft Windows, it would have done so already. Clearly the need
    isn’t there.

    By doing searches, I found a bunch of libxxx.a files with today's date
    in various locations.

    I would say you forgot to do the “make install” bit. That would put the build products in some sensible place, like /usr/local.

    So the outputs are archive files for gcc, I guess intended for static linking.

    As I mentioned, the build script has options for both static and dynamic builds. Maybe you chose the wrong one. Tip: try “./configure --help” for a quick summary of the build options.

    The INSTALL file talks about reading detailed instructions in gmp_info,
    but this file is gobbledygook. You need to view the instructions using a program called 'info' - a Linux utility that doesn't exist on Windows.

    Again, that’s the fault of Windows for being such a poverty-stricken OS.

    Remember, Windows is designed as a platform on which users will not do
    their own programming, but instead they will buy programs from software
    vendor organizations. All the tooling is centred around that, and is built
    on the assumption that Microsoft will provide the core development tools.

    So I need Linux even just to look at a bunch of instructions?

    You need Linux to do any kind of serious open-source or cross-platform development work, yes.

    What, God forbid, if 'gcc -E' was not supported?

    What, indeed. I _did_ mention, did I not, that this project has a lot of support for obsolete, proprietary Unix systems with their own peculiar
    quirks. Certainly enough users must still care about those for this
    support for them to be retained in the project. Yet there are not enough
    of these complaining Windows users (or should I say “sufferers”) for them to be catered to.

    And why aren't these checks everytime you build /any/ program?

    Different projects have different requirements. GNU autoconf has a whole standard library of things to check for (and configure your build for),
    and you can add new ones by writing suitable m4 definitions.

    Here, you can read about it yourself: <https://www.gnu.org/savannah-checkouts/gnu/autoconf/manual/autoconf-2.71/ autoconf.html>.

    What a total waste of time.

    A good description of Microsoft Windows generally. That’s why those of us
    who want to be productive tend to avoid it.

    GMP, when it's actually made available, is actually an incredibly fast library. Some talented people went into creating it. I can't say the
    same for those responsible for the build system who seem to have made it
    as complicated and convoluted as they possibly can.

    You yourself said, it only took 5 minutes to build--on Linux.

    As the Dilbert cartoon goes: “Here’s a nickel, kid. Get yourself a *real* computer.”

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Janis Papanagnou@21:1/5 to Bart on Tue Jan 2 20:23:24 2024
    On 02.01.2024 17:38, Bart wrote:
    On 02/01/2024 15:10, Blue-Maned_Hawk wrote:
    Bart wrote:

    One complex expression that my compiler had trouble with, when expanded, >>> resulted in a line hundreds of character long, and using 11 levels of >>> nested parentheses, the most I've ever come across.

    Happen to remember particularly which that one was you?




    No. You can have a look yourself if you like, just apply -E to the sources.

    Here's one with quite a long expansion but not too deeply nested. This
    is the line inside lvm.c at line 1463:

    op_arith(L, l_addi, luai_numadd);

    The expansion is:

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

    This is the definition of that macro:

    #define op_arith(L,iop,fop) { \
    TValue *v1 = vRB(i); \
    TValue *v2 = vRC(i); \
    op_arith_aux(L, v1, v2, iop, fop); }

    were 'op_arith_aux' is another macro. Actually, probably everything is.

    This looks fun to try to understand, debug, or port.

    Here you might well ask why inlined functions weren't better suited.

    Cpp macros, inline functions, etc. - it's somehow funny, but probably explainable because we're in a NG topical to the C language.

    During the 1980's (when I learned quite some different language types)
    we were taught that [these sorts of] optimizations are (or should be)
    the task of the compilers not of the programmers; programmers should
    (WRT optimizations) focus on the algorithmic complexity.

    Here in CLC I'll probably get lapidated for (still) considering C as a
    perfect [almost] machine independent assembler language (sort of). :-)

    In our role as C programmers we have to live with what we've got, and
    tools (or later language extensions) help us...

    That said; during my early K&R C era we had 'register' declarations,
    but I rarely saw them, they seem to have quickly vanished from usage.
    Now I've heard that 'inline' optimizations have been introduced in C.
    Isn't that considered a task for the compiler? (I mean there's already
    the -O option levels available. But, OTOH, I occasionally hear about
    problems with some -O levels...)

    Janis

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to Lawrence D'Oliveiro on Tue Jan 2 20:05:11 2024
    Lawrence D'Oliveiro <ldo@nz.invalid> writes:
    On Mon, 1 Jan 2024 11:56:00 +0000, Bart wrote:

    On 01/01/2024 02:00, Lawrence D'Oliveiro wrote:

    Those are just standard file-manipulation tools that any decent OS
    should provide.

    What's that got to do with building able to build programs easily?

    You have effectively answered that question yourself. Why were you trying
    to build GNU GMP on Windows? Isn’t there something equally capable and yet >more, shall we say, “native” to Windows, that you can build and use in a >more “Windows-native” fashion?

    And the 'G' in GMP stands for GNU. Expecting FSF to do anything
    to support windows is insane.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Janis Papanagnou@21:1/5 to Lawrence D'Oliveiro on Tue Jan 2 20:54:15 2024
    On 02.01.2024 20:35, Lawrence D'Oliveiro wrote:
    On Tue, 2 Jan 2024 20:23:24 +0100, Janis Papanagnou wrote:

    That said; during my early K&R C era we had 'register' declarations, but
    I rarely saw them, they seem to have quickly vanished from usage. Now
    I've heard that 'inline' optimizations have been introduced in C. Isn't
    that considered a task for the compiler?

    Kids’ stuff. Want to see how a _real_ compiler does it? <https://gcc.gnu.org/onlinedocs/gcc-13.2.0/gcc/Attribute-Syntax.html>

    What shall I infer from that statement and link? - Mind to elaborate?

    My original question was related to compiler (as opposed to programmer)
    doing optimizations. I recall from decades ago that compilers will do optimizations e.g. on the attributed syntax tree level, while 'register'
    or 'inline' seem very primitive constructs (on a comparable low level).
    So I expressed my astonishment that 'inline' had been later introduced
    in C, and I wonder why. (Note that the other poster also mentioned it
    as a preferred way to replace [parameterized] macros, if I interpreted
    him correctly.)

    Janis

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to Janis Papanagnou on Tue Jan 2 19:35:10 2024
    On Tue, 2 Jan 2024 20:23:24 +0100, Janis Papanagnou wrote:

    That said; during my early K&R C era we had 'register' declarations, but
    I rarely saw them, they seem to have quickly vanished from usage. Now
    I've heard that 'inline' optimizations have been introduced in C. Isn't
    that considered a task for the compiler?

    Kids’ stuff. Want to see how a _real_ compiler does it? <https://gcc.gnu.org/onlinedocs/gcc-13.2.0/gcc/Attribute-Syntax.html>

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to Bart on Tue Jan 2 20:11:27 2024
    Bart <bc@freeuk.cm> writes:
    On 02/01/2024 06:47, Lawrence D'Oliveiro wrote:
    On Tue, 2 Jan 2024 06:23:03 -0000 (UTC), Kaz Kylheku wrote:

    On 2024-01-02, Lawrence D'Oliveiro <ldo@nz.invalid> wrote:

    First of all, it includes code for building on a whole lot of
    proprietary Unix systems with non-GCC compilers, some of which maybe
    don’t quite conform to official C/C++ standards.

    When people use Autoconf to configure their program for building, the
    generated script includes cruft for platforms on which they never tested >>> the program, and never will, and on which it won't work for reasons not
    related to those tests.

    This is a GNU project, so you can be sure they have done pretty good
    testing on those platforms and those options. If they were no longer
    supported, they would have been dropped from the code. This isn’t like
    proprietary software, which tends to accumulate cruft that nobody dares
    touch because they no longer understand what it does.

    Look at the ABI options: maybe you want a 32-bit build (where the CPU
    supports it) rather than a 64-bit one; on some architectures, things
    get a bit more complicated than just those two options.

    These kinds of options can be passed in by CFLAGS, LDFLAGS and LDLIBS.
    Support those three variables, and that's it.

    If you have a look, that’s what the script does. Only you just have to
    specify one option, and it generates the right value for *all* the
    relevant variables, instead of you having to specify them individually.


    You're going to defend this to the death aren't you? Be funny if at some >point some GMP II was produced whose main new benefit was a vastly
    simplified build!

    You still haven't provided a better system that accomplishes
    everthing the existing system does.

    Quit bitching about it and _do_ something about it. It's an open
    source system, you are free to contribute a new build system for
    it (which clearly needs to support all the capabilities of the
    existing system otherwise you'll never have your suggestions
    accepted).

    Don't, however, expect anyone to cater to you.


    I tried to build gmp today using WSL. The process took just under five >minutes. However, where and what is the output? For all the verbosity,
    that simple fact is omitted.

    You presumably specified that on the ./configure with the --prefix
    option. Or did you forget to RTFM first?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to Scott Lurndal on Tue Jan 2 20:43:24 2024
    On 02/01/2024 20:11, Scott Lurndal wrote:
    Bart <bc@freeuk.cm> writes:

    You're going to defend this to the death aren't you? Be funny if at some
    point some GMP II was produced whose main new benefit was a vastly
    simplified build!

    You still haven't provided a better system that accomplishes
    everthing the existing system does.

    I need some basic information about what's what about in the 26MB of
    content in 4000 files spread over 190 directories.

    But it's clear that it's not interested in making things simple. (The
    nearest they've come is with the 'mini-gmp' version. But since the
    performance of that is on a par with my own library, I'll stick with
    mine, which also has a higher spec.)

    I have done this exercise with projects like LIBJPEG, LUA5.4 and
    TCC0.9.27. Long ago I did it with SEED7.

    Once I can extract the necessary info - IT ALWAYS COMES DOWN TO A LIST
    OF FILES TO SUBMIT TO A COMPILER FGS - then I can whizz through it easily.


    Quit bitching about it and _do_ something about it.

    Funnily enough, I did. I wrote my own library, which I know is always available, and has some unique features, eg. it uses decimal, and has a
    single integer/float type.

    I once linked to a C port of it.

    It's not fast, but it's mine and it's in my language. (It accounts for
    12KB of my interpreter.)


    It's an open
    source system, you are free to contribute a new build system for
    it (which clearly needs to support all the capabilities of the
    existing system otherwise you'll never have your suggestions
    accepted).

    Don't, however, expect anyone to cater to you.

    There must be one person on the planet who understands how to produce a
    Windows DLL of this thing. And funnily enough, a single 64-bit DLL is
    all any Windows user on the planet needs (that and either gmp.h or language-neutral docs).


    I tried to build gmp today using WSL. The process took just under five
    minutes. However, where and what is the output? For all the verbosity,
    that simple fact is omitted.

    You presumably specified that on the ./configure with the --prefix
    option. Or did you forget to RTFM first?


    I following these instructions:

    --------------
    Here are some brief instructions on how to install GMP. First you need to compile. Since you're impatient, try this

    ./configure
    make
    make check <= VERY IMPORTANT!!

    I did the first two line before looking for the binaries.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to David Brown on Tue Jan 2 20:24:21 2024
    On 02/01/2024 17:34, David Brown wrote:
    On 02/01/2024 17:12, Bart wrote:

    X-macro are ugly. They are unreadable.

    Speaking as someone who has used them in real code, rather than someone
    with a pathological hatred of macros and who prefers knee-jerk reaction
    to their uses instead of applying a few minutes objective thought,
    "x-macros" can make code significantly simpler, clearer, and much easier
    to maintain correctly.


    I'm sorry, but I *DO* find them utterly impossible. This is a simple
    example from Stackoverflow of someone wanting to define a set of enums
    with an accompanying table, whose problem was in ensuring the two were
    kept in sync.

    The X-macro solution was this, adapted from the first answer here (https://stackoverflow.com/questions/6635851/real-world-use-of-x-macros); assume those functions are in scope:

    -------

    #define STATE_TABLE \
    ENTRY(STATE0, func0) \
    ENTRY(STATE1, func1) \
    ENTRY(STATE2, func2) \
    ENTRY(STATE3, func3) \

    enum
    {
    #define ENTRY(a,b) a,
    STATE_TABLE
    #undef ENTRY
    NUM_STATES
    };

    void* jumptable[NUM_STATES] =
    {
    #define ENTRY(a,b) b,
    STATE_TABLE
    #undef ENTRY
    };
    -------


    With my feature, it is merely this:

    global enumdata []ref void jumptable =
    (state1, func1), # comment 1
    (state2, func2),
    (state3, func3), # comment 3
    (state4, func4),
    end

    Notice:

    (1) I don't need any of those weird macros.

    (2) I don't need those backslashes

    (3) I can add comments to any entry in the table.

    (4) The 'global' attribute means both enumeration names and the array
    are exported to other modules. Doing the same in C is tricky.

    But I'm wasting my time because you are never going to admit my feature
    is superior to X-macros for this purpose.

    (The author of this example goes on to say how it can also be used to
    define a set of function prototypes. I can't do that with my feature.
    But then I don't need function prototypes!)

    At best, if someone has already done the hard work of setting up a
    working set of macros, then you will be able to see how to modify, add
    or delete entries.

    They are not hard.

    Maybe not for you, but it would take me ages to come up with the right incantations to make it work.

    For the purposes of creating parallel sets of enums and associated
    data, I use special syntax which makes for a clearer and simpler feature.

    And that is, obviously, utterly useless - because it is not C or even a commonly supported extension.  Oh, and even if it /were/ part of C, it
    would not help because that's not what I was doing.

    I've given an example of a use-case for C macros which have a special
    purpose feature in other languages. So of cause it's not in C.


    Have you ever considered that if C didn't have macros, or they weren't
    powerful, then it could have evolved superior, built-in alternatives?


    The C preprocessor is part of C - it is already built in.

    You missed my point. Take a tiny feature like being able to easily get
    the size of a fixed-length array. You commonly see macro like this:

    #define LENGTH(a) (sizeof(a)/size(a[0]))

    What incentive is there to properly add a built-in way to do that, when
    it can be done, badly, and in 1000 different ways by each user, in a
    one-line macro?

    Another example is GETBIT(a, n).

    It wasn't a compiler limit - it was mine! The resulting expression was
    completely unreadable.

    I wonder if the code authors also found this expanded expression
    unreadable.

    Look at the example I posted in reply to Blue-Maned_Hawk.

    In particular consider my last comment.


    I guess not - because they used macros so that they could
    write it in a clear and understandable way, and never have to look at
    the expansion!  You don't seem to comprehend the point of macros at all.

    In the end I resorted to looking at the AST as that was clearer than C
    source code.


    Why not just look at the code as written, if you want to understand it?

    In my case I was debugging my C compiler. Then you need to examine the
    actual expanded code in detail. Excessive use of macros makes that much
    harder.

    That example came from Lua 5.4, an interpeter whose performance on a par
    with my own product running from HLL-only code. That doesn't doesn't use deeply-nested macro like this, and it doesn't appear to suffer,.


    Or would doing something that sensible put a damper on your rants?

    Look, I'm written a C preprocessor (I suspect you haven't). If I wanted,
    I could adapt it to work on my own languages.

    But I /don't/ want to. Obviously I see something undesirable about them
    (I don't like metaprogramming in general; people get too carried away
    with it and want to show off their skills).

    Why can't you accept that?


    Half my use-cases involve inline assembly.

    Why can't that be in inlined functions?

    That doesn't work in inline assembly.

    Again, you are not showing that
    these limited macros have any benefits compared to what is found in C,

    That's true. I can't write something like:

    #define M a+b)*c

    (M;


    Macros are a C language artefact, and what they expand to is arbitrary
    C syntax that can be meaningless elsewhere.

    Yes - C code is C code.  It is not Pascal, or Fortran, or Ada.  Should
    this be a surprise?

    It's inconvenient. This is a macro from SDL2:

    #define SDL_CompilerBarrier() \
    { SDL_SpinLock _tmp = 0; SDL_AtomicLock(&_tmp);
    SDL_AtomicUnlock(&_tmp); }

    Rather than declaring a function which resides in a language-neutral
    DLL, it declares it here, in the form of actual C code.

    Even given a language with an FFI that is capable of calling external
    functions compiled as C, what are they supposed to do with this?

    That example was in a conditional block; I don't know the context. But
    here's a juicy one that is the result of an attempt to automatically
    translate the C SDL2 API to my syntax:

    global macro SDL_FOURCC(A,B,C,D) = ((SDL_static_cast(Uint32,SDL_static_cast(Uint8,(A)))<<0)|(SDL_static_cast(Uint32,SDL_static_cast(Uint8,(B)))<<8)|(SDL_static_cast(Uint32,SDL_static_cast(Uint8,(C)))<<16)|(SDL_static_cast(Uint32,SDL_static_cast(Uint8,(D)))<<24))

    This also contains arbitrary C code; it needs a much more sophisticated
    tool than one that just does declaration. Basically a transpiler from
    C to the language one is writing bindinds for.


    You already have a habit of cherry-picking example code that is as "Bart-unfriendly" as you can find.

    Try testing your own C compiler on just about any codebase. You don't
    have to look far for extreme examples.

    I don't have your experience, but I find that /very/ hard to believe.  I should imagine it's primarily a matter of working through the
    "translation phases" and "preprocessing directives" described in the C standards, step by step.

    The C standard has very little to say about. I once found a very, very
    long article which went into considerably more detail, now lost.

    But even 1/3 through I decided that it was impossible.

    In 2017 when I created my CPP, many compilers would give different
    results with various edge cases of macros. Which were right and which
    were wrong? You'd think there'd be a definitive reference for it.

    Here's an example I've just found:

    #include <stdio.h>
    int main(void) {

    #define FOO
    #define BAR defined(FOO)
    #if BAR
    puts("BAR");
    #else
    puts("FOO");
    #endif
    }

    Most compilers will display BAR; MSVC I think is the only one showing
    FOO. (Some smaller compilers I tried on godbolt.org failed to compile it.)

    Mine showed BAR (obviously!)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Lawrence D'Oliveiro on Tue Jan 2 21:45:31 2024
    On 2024-01-02, Lawrence D'Oliveiro <ldo@nz.invalid> wrote:
    On Tue, 2 Jan 2024 18:16:36 -0000 (UTC), Kaz Kylheku wrote:

    By the way, I'm not sure what system you are referring to by
    "string-based macros", but in case you might be thinking of C
    preprocessing, be advised that it's token-based.

    Now go learn what “homoiconicity” means.

    Homoiconicity is a term that was introduced in the TRAC project
    in the 1960's, referring to procedures being retained in source
    form in the image, so they could be edited online.

    (I actually made updates to the Wikipedia page on that.
    Until fairly recently it contained a huge gaffe, whereby the term
    "homoiconic" was misattributed to Doug McIlroy. This was due to
    referencing an end note in a certain book, where the person writing the
    article conflated two consecutive unrelated endnotes as being a
    single note.)


    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From tTh@21:1/5 to Bart on Wed Jan 3 00:24:41 2024
    On 1/2/24 21:24, Bart wrote:

    You missed my point. Take a tiny feature like being able to easily get
    the size of a fixed-length array. You commonly see macro like this:

       #define LENGTH(a) (sizeof(a)/size(a[0]))

    And why is that bad? That's a real question.

    --
    +---------------------------------------------------------------------+
    | https://wiki.interhacker.space/index.php?title=Techno-futilit%C3%A9 | +---------------------------------------------------------------------+

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to Bart on Tue Jan 2 23:55:11 2024
    Bart <bc@freeuk.cm> writes:
    On 02/01/2024 20:11, Scott Lurndal wrote:
    Bart <bc@freeuk.cm> writes:

    You're going to defend this to the death aren't you? Be funny if at some >>> point some GMP II was produced whose main new benefit was a vastly
    simplified build!

    You still haven't provided a better system that accomplishes
    everthing the existing system does.

    I need some basic information about what's what about in the 26MB of
    content in 4000 files spread over 190 directories.

    But it's clear that it's not interested in making things simple. (The
    nearest they've come is with the 'mini-gmp' version. But since the >performance of that is on a par with my own library, I'll stick with
    mine, which also has a higher spec.)

    I have done this exercise with projects like LIBJPEG, LUA5.4 and
    TCC0.9.27. Long ago I did it with SEED7.

    Once I can extract the necessary info - IT ALWAYS COMES DOWN TO A LIST
    OF FILES TO SUBMIT TO A COMPILER FGS - then I can whizz through it easily.


    Quit bitching about it and _do_ something about it.

    Funnily enough, I did. I wrote my own library, which I know is always >available, and has some unique features, eg. it uses decimal, and has a >single integer/float type.

    I once linked to a C port of it.

    It's not fast, but it's mine and it's in my language. (It accounts for
    12KB of my interpreter.)


    It's an open
    source system, you are free to contribute a new build system for
    it (which clearly needs to support all the capabilities of the
    existing system otherwise you'll never have your suggestions
    accepted).

    Don't, however, expect anyone to cater to you.

    There must be one person on the planet who understands how to produce a >Windows DLL of this thing. And funnily enough, a single 64-bit DLL is
    all any Windows user on the planet needs (that and either gmp.h or >language-neutral docs).


    I tried to build gmp today using WSL. The process took just under five
    minutes. However, where and what is the output? For all the verbosity,
    that simple fact is omitted.

    You presumably specified that on the ./configure with the --prefix
    option. Or did you forget to RTFM first?


    I following these instructions:

    --------------
    Here are some brief instructions on how to install GMP. First you need to >compile. Since you're impatient, try this

    ./configure
    make
    make check <= VERY IMPORTANT!!

    I did the first two line before looking for the binaries.


    Try RTFM:

    ./configure --help

    always useful.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to Scott Lurndal on Wed Jan 3 02:08:40 2024
    On 02/01/2024 23:55, Scott Lurndal wrote:
    Bart <bc@freeuk.cm> writes:
    On 02/01/2024 20:11, Scott Lurndal wrote:
    Bart <bc@freeuk.cm> writes:

    You're going to defend this to the death aren't you? Be funny if at some >>>> point some GMP II was produced whose main new benefit was a vastly
    simplified build!

    You still haven't provided a better system that accomplishes
    everthing the existing system does.

    I need some basic information about what's what about in the 26MB of
    content in 4000 files spread over 190 directories.

    But it's clear that it's not interested in making things simple. (The
    nearest they've come is with the 'mini-gmp' version. But since the
    performance of that is on a par with my own library, I'll stick with
    mine, which also has a higher spec.)

    I have done this exercise with projects like LIBJPEG, LUA5.4 and
    TCC0.9.27. Long ago I did it with SEED7.

    Once I can extract the necessary info - IT ALWAYS COMES DOWN TO A LIST
    OF FILES TO SUBMIT TO A COMPILER FGS - then I can whizz through it easily. >>

    Quit bitching about it and _do_ something about it.

    Funnily enough, I did. I wrote my own library, which I know is always
    available, and has some unique features, eg. it uses decimal, and has a
    single integer/float type.

    I once linked to a C port of it.

    It's not fast, but it's mine and it's in my language. (It accounts for
    12KB of my interpreter.)


    It's an open
    source system, you are free to contribute a new build system for
    it (which clearly needs to support all the capabilities of the
    existing system otherwise you'll never have your suggestions
    accepted).

    Don't, however, expect anyone to cater to you.

    There must be one person on the planet who understands how to produce a
    Windows DLL of this thing. And funnily enough, a single 64-bit DLL is
    all any Windows user on the planet needs (that and either gmp.h or
    language-neutral docs).


    I tried to build gmp today using WSL. The process took just under five >>>> minutes. However, where and what is the output? For all the verbosity, >>>> that simple fact is omitted.

    You presumably specified that on the ./configure with the --prefix
    option. Or did you forget to RTFM first?


    I following these instructions:

    --------------
    Here are some brief instructions on how to install GMP. First you need to >> compile. Since you're impatient, try this

    ./configure
    make
    make check <= VERY IMPORTANT!!

    I did the first two line before looking for the binaries.


    Try RTFM:

    ./configure --help

    always useful.

    As I said, I read the file called INSTALL and I did /exactly/ what it said.

    You're telling me, when I'm questioning why I have to use build
    instructions contained in a 10,000-page manual rather than a few sides
    of A4 (that I can only use in a different country) that I should try
    reading the guide to that manual!

    You guys don't seem to get it.

    The end-product will be a binary between 0.5 and 1.0MB. It is not a
    170MB web browser.

    I actually gave up being able to use this library years ago, so no
    longer need it. If people really don't care about providing ready-to-use libraries, then fuck 'em.

    I now use my own library, which I can build into a DLL binary, like this:

    c:\mx>mm -dll bignum
    Compiling bignum.m------ to bignum.dll
    Writing exports file to bignum.q

    It takes 50msec. The secondary file it writes is a set of bindings to
    use it from one of my other languages.

    This stuff needs to be as effortless as this.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to tTh on Wed Jan 3 02:41:59 2024
    On Wed, 3 Jan 2024 00:24:41 +0100, tTh wrote:

    On 1/2/24 21:24, Bart wrote:

       #define LENGTH(a) (sizeof(a)/size(a[0]))

    And why is that bad? That's a real question.

    Ignoring the typo? Not typesafe. If you pass something that it’s not expecting, you will likely not get a nice, clear error message, but a
    cryptic, not to say, irrelevant, one.

    Also, would it be better as

    #define LENGTH(a) (sizeof(a)/sizeof((a)[0]))

    ? Answers on a postcard, please.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to Bart on Wed Jan 3 02:40:06 2024
    On Wed, 3 Jan 2024 02:08:40 +0000, Bart wrote:

    You guys don't seem to get it.

    Another thing we do is, not to bother repeating work that others have
    already done.

    sudo apt-get install libgmp-dev

    Job done.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to tTh on Wed Jan 3 03:29:54 2024
    On 2024-01-02, tTh <tth@none.invalid> wrote:
    On 1/2/24 21:24, Bart wrote:

    You missed my point. Take a tiny feature like being able to easily get
    the size of a fixed-length array. You commonly see macro like this:

       #define LENGTH(a) (sizeof(a)/size(a[0]))

    And why is that bad? That's a real question.

    It fails miserably on pointers, and silently, if you don't have a newer
    gcc that has a diagnostic specifically for this case.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to tTh on Wed Jan 3 11:55:33 2024
    On 02/01/2024 23:24, tTh wrote:
    On 1/2/24 21:24, Bart wrote:

    You missed my point. Take a tiny feature like being able to easily get
    the size of a fixed-length array. You commonly see macro like this:

        #define LENGTH(a) (sizeof(a)/size(a[0]))

       And why is that bad? That's a real question.

    Where do I start?

    * Why is it necessary for a million programmers to each come up with
    their own solutions for something so basic? Eg. they will all use
    diferent names.

    * What happens when you merge or copy&paste code form different sources,
    which use different macros for the same thing?

    How does it work in practice: does everyone have a personal header full
    of this stuff (bits missing from the language)? That will make it
    awkward to post short programs to forums as now they have this extra dependendcy.

    Personally I can never bothered to write such macros in throwaway
    programs, I just either write the whole thing in situ, or hard-code the
    size if I know what it is anyway:

    char* names[] = {"one", "two", "three", "four"};

    for (int i=0; i<4; ++i)
    puts(names[i]);

    Rather than write:

    for (int i=0; i<sizeof(names)/sizeof(names[0]); ++i) puts(names[i]);


    It's all just so messy and annoying.

    How hard is it to make it built-in to the language? I tried it just now
    and it was about 20 lines of code to allow me to instead write:

    char* names[] = {"one", "two", "three", "four"};

    for (int i=0; i<lengthof(names); ++i)
    puts(names[i]);

    It took about 20 minutes. This could have been done in 1972.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to Lawrence D'Oliveiro on Wed Jan 3 12:10:51 2024
    On 03/01/2024 02:40, Lawrence D'Oliveiro wrote:
    On Wed, 3 Jan 2024 02:08:40 +0000, Bart wrote:

    You guys don't seem to get it.

    Another thing we do is, not to bother repeating work that others have
    already done.

    sudo apt-get install libgmp-dev

    Job done.

    OK, I've done that. But it didn't take 5 minutes as it did to compile it.

    So I guess it is installing ready-built binaries?

    In that case, why are people having a go at me for suggesting they
    should be available to Windows users? Why are Linux users so blessed?

    I notice that it no longer matters whether string.h is available. (Nor apparently, the exact capabilities of my processor.)

    Why it was ever an issue not clear. But it seems it only matters when
    building this product.

    Once it is a binary, then it doesn't matter. All the more reason why
    such fussy bits of software should be preprocessed centrally, so that I
    either get a binary, or an easy-to-compile blob of C code.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to Bart on Wed Jan 3 13:03:18 2024
    On 03/01/2024 12:10, Bart wrote:
    On 03/01/2024 02:40, Lawrence D'Oliveiro wrote:
    On Wed, 3 Jan 2024 02:08:40 +0000, Bart wrote:

    You guys don't seem to get it.

    Another thing we do is, not to bother repeating work that others have
    already done.

    sudo apt-get install libgmp-dev

    Job done.

    OK, I've done that. But it didn't take 5 minutes as it did to compile it.

    So I guess it is installing ready-built binaries?

    In that case, why are people having a go at me for suggesting they
    should be available to Windows users? Why are Linux users so blessed?


    It gets more puzzling: on Linux for which the build process was designed
    to run on, and where it is likely to work, users don't even need to
    bother to do it, as there may be a ready-to-use version available.

    But on Windows, where it is impossible to build without using so many
    Linux elements (even 'info' to read the docs, and remember that weird 'JUNCTION' header?), there is no official binary. (Just lots of dubious gmp.dlls with unfeasibly small sizes from dodgy-looking DLL sites.)

    It's almost like they did it on purpose. Then I came across this comment
    from 2016:

    "Worth noting that there's quite a bit of beef between GMP and MPIR.

    The GMP devs hate Windows and anything Microsoft. It's quite obvious
    from the tone of language in their mailing lists.

    This hostility towards Windows is why MPIR was made. So then the GMP
    devs started hating on MPIR probably because it became a competitor."

    This is just sad. It does sound as though the GMP people crammed in as
    many Linux dependencies as possible just to spite MS.

    I remember similar attitudes from the creators of Zig: they deliberately
    chose not to support source files with CRLF line endings, because they
    hated MS. Although later they did allow them.

    (I remember needing to write a CRLF to LF conversion tool just to be
    able to compile hello.zig.)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to Bart on Wed Jan 3 15:33:22 2024
    Bart <bc@freeuk.cm> writes:
    On 03/01/2024 02:40, Lawrence D'Oliveiro wrote:
    On Wed, 3 Jan 2024 02:08:40 +0000, Bart wrote:

    You guys don't seem to get it.

    Another thing we do is, not to bother repeating work that others have
    already done.

    sudo apt-get install libgmp-dev

    Job done.

    OK, I've done that. But it didn't take 5 minutes as it did to compile it.

    So I guess it is installing ready-built binaries?

    In that case, why are people having a go at me for suggesting they
    should be available to Windows users? Why are Linux users so blessed?

    Because linux is open source and the GNU team doesn't support closed-source source operating environments like Windows.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to Bart on Wed Jan 3 15:32:09 2024
    Bart <bc@freeuk.cm> writes:
    On 02/01/2024 23:24, tTh wrote:
    On 1/2/24 21:24, Bart wrote:

    You missed my point. Take a tiny feature like being able to easily get
    the size of a fixed-length array. You commonly see macro like this:

        #define LENGTH(a) (sizeof(a)/size(a[0]))

       And why is that bad? That's a real question.

    Where do I start?

    * Why is it necessary for a million programmers to each come up with
    their own solutions for something so basic? Eg. they will all use
    diferent names.

    Why is it necessary to exaggerate?

    I've not seen that particular macro in the last forty years, myself.

    LENGTH isn't even the proper macro name in this case; QUANTITY or NUM_ELEMENTS would likely be more appropriate.

    I have, of course, seen the idiom used often - it's quite normal and
    standard.

    e.g.
    /*
    * Table of Pentium MSR's to allow the Guest to access directly.
    *
    * XXX - Note that if the guest is allowed to access these MSR's
    * directly, then they must be preserved and restored when the
    * guest is migrated to a different core.
    */
    static int pentium_msr_passthrough[] = {
    MSR_SYSENTER_CS,
    MSR_SYSENTER_EIP,
    MSR_SYSENTER_ESP,
    };
    static const size_t PENTIUM_MSR_PT_CNT =
    sizeof(pentium_msr_passthrough) / sizeof(pentium_msr_passthrough[0]);


    /*
    * Table of K6 MSR's to allow the guest to access directly.
    *
    * See caveat above.
    */
    static int k6_msr_passthrough[] = {
    MSR_KERN_GS_BASE,
    MSR_USER_GS_BASE,
    MSR_FS_BASE,
    MSR_STAR,
    MSR_LSTAR,
    MSR_CSTAR,
    MSR_SFMASK,
    MSR_TSC_AUX,
    };

    static const unsigned int K6_MSR_PT_CNT =
    sizeof(k6_msr_passthrough) / sizeof(k6_msr_passthrough[0]);

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to Scott Lurndal on Wed Jan 3 17:14:30 2024
    On 03/01/2024 15:32, Scott Lurndal wrote:
    Bart <bc@freeuk.cm> writes:
    On 02/01/2024 23:24, tTh wrote:
    On 1/2/24 21:24, Bart wrote:

    You missed my point. Take a tiny feature like being able to easily get >>>> the size of a fixed-length array. You commonly see macro like this:

        #define LENGTH(a) (sizeof(a)/size(a[0]))

       And why is that bad? That's a real question.

    Where do I start?

    * Why is it necessary for a million programmers to each come up with
    their own solutions for something so basic? Eg. they will all use
    diferent names.

    Why is it necessary to exaggerate?

    How do you know whether I'm exaggerating or not? There have surely been
    many millions of people who've programmed in C over the years.

    And a large proportion may have needed the length of an array whose
    dimensions are not set by a constant, but by the number of data elements provided.

    (Even in the former case, if you have int A[N] and B[N], I think it is
    better to have 'LENGTH(A)' within the subsequent code, rather than a
    bare 'N' which could mean anything: is it the length or A, B, or does it
    mean N by itself? What happens if you change it to A[M]?)

    So there could well have been million people who have created such a
    macro, or at least, have had to do it a million times in all.

    And if not, the rest would have to write code like this:

    static const size_t PENTIUM_MSR_PT_CNT =
    sizeof(pentium_msr_passthrough) / sizeof(pentium_msr_passthrough[0]);

    If you have two similar-sounding arrays, it is easy to mix them up.

    Or, somebody reading this code will have to double-check it, to ensure
    tha names are identical, to be sure that it /is/ the idiom to get an
    array length.

    With my suggested feature, and in my style, it would look like this:

    enum {PENTIUM_MSR_PT_CNT = lengthof(pentium_msr_passthru)};

    (Although this would limit the number of entries in that array to about
    2 billion, which would make for a rather large source file.)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Bart on Wed Jan 3 17:41:31 2024
    On 02/01/2024 21:24, Bart wrote:
    On 02/01/2024 17:34, David Brown wrote:
    On 02/01/2024 17:12, Bart wrote:

    X-macro are ugly. They are unreadable.

    Speaking as someone who has used them in real code, rather than
    someone with a pathological hatred of macros and who prefers knee-jerk
    reaction to their uses instead of applying a few minutes objective
    thought, "x-macros" can make code significantly simpler, clearer, and
    much easier to maintain correctly.


    I'm sorry, but I *DO* find them utterly impossible. This is a simple
    example from Stackoverflow of someone wanting to define a set of enums
    with an accompanying table, whose problem was in ensuring the two were
    kept in sync.

    The X-macro solution was this, adapted from the first answer here (https://stackoverflow.com/questions/6635851/real-world-use-of-x-macros); assume those functions are in scope:

    -------

    #define STATE_TABLE \
            ENTRY(STATE0, func0) \
            ENTRY(STATE1, func1) \
            ENTRY(STATE2, func2) \
            ENTRY(STATE3, func3) \

    enum
    {
    #define ENTRY(a,b) a,
        STATE_TABLE
    #undef ENTRY
        NUM_STATES
    };

    void* jumptable[NUM_STATES] =
    {
    #define ENTRY(a,b) b,
        STATE_TABLE
    #undef ENTRY
    };
    -------

    (Why did you change the type from "p_func_t" to "void*" ? Was it just
    to annoy myself and other C programmers with a pointless and constraint-violating cast of a function pointer to "void*" ? Just add a suitable typedef - "typedef void(*p_func_t)(void);" )


    That's a fairly ugly way to express x-macros. It is neater to have the extractor macro as a parameter to the list macro. Of course, you'd know
    that if you read the stack overflow link you gave, since it comes
    slightly below the code you copied. But presumably you preferred to use
    the ugliest version that you could pretend was hard to understand (it
    isn't), rather than using the improved version.

    I personally like to use a slightly different syntax, but that's just
    detail and style choice.


    #define DoStateList(DO) \
    DO(state0, func0) /* Comment */ \
    DO(state1, func1) \
    DO(state2, func2) \
    DO(state3, func3) \

    #define GetState(state, func) state,
    #define GetFunc(state, func) func,
    #define Counter(state, func) +1

    enum { num_of_states = DoStateList(Counter) };

    enum States {
    DoStateList(GetState)
    };

    p_func_t jump_table[] = {
    DoStateList(GetFunc)
    };



    Note that the states can have comments. You do need a backslash at the
    end of each line, and that means comments must be /* */ style, not // style.

    It is, of course, quite possible to have generic "Get1", "Get2", etc.,
    macros rather than "GetState", "GetFunc".


    And since we have the x-macro in place, we can use it for more things.
    Why manually declare "extern void func0(void);", etc., when you can do
    it in two lines for all the functions?

    #define DeclareFunc(state, func) extern void func(void); DoStateList(DeclareFunc)

    Maybe you want a switch rather than a jump table - that could give more inlining opportunities, and is a common choice for the structure of
    things like simple VM's and bytecode interpreters:

    #define SwitchEntry(state, func) case state : func(); break;
    void jump(enum States s) {
    switch (s) {
    DoStateList(SwitchEntry);
    }
    }


    There are lots of possibilities here. It's not perfect, and some
    complex cases might need a bit of testing debugging (godbolt.org with
    the "-E" compiler flag is extremely convenient for this), but testing
    and debugging is part of a programmer's daily life.

    Overall, it's a code generation technique that can be useful whenever
    you need repetitive code patterns. It can also be useful for some kinds
    of compile-time calculations (such as setting up tables for CRC
    functions.) I would not use it when higher level features could do a
    better job - if C++ templates and constexpr functions are clearer, I'd
    use them. And sometimes they are not enough, or not clear enough, and
    I'd make a Python script to generate the C code. But that applies to everything in programming, really - you should always look for the best
    tool for the job.




    With my feature, it is merely this:

        global enumdata []ref void jumptable =
            (state1, func1),        # comment 1
            (state2, func2),
            (state3, func3),        # comment 3
            (state4, func4),
        end


    That is an very niche and very limited use-case. Did you seriously add
    a special feature to your language based on one example in one stack
    overflow question? X macros are just an imaginative way to use a
    standard feature of C (and C++), giving vastly more flexibility than
    your single-use language feature. I have difficulty seeing how it was
    worth the effort putting that feature in your tools.


    Notice:

    (1) I don't need any of those weird macros.

    They are quite simple. You pretend not to understand them, but this is
    /not/ hard.


    (2) I don't need those backslashes

    The backslashes are a minor inconvenience. I'd prefer if they were unnecessary. It would be nice if C had something like a "#begindef" and "#enddef" pair for making multi-line macros without backslashes.


    (3) I can add comments to any entry in the table.

    So can I.


    (4) The 'global' attribute means both enumeration names and the array
        are exported to other modules. Doing the same in C is tricky.

    It is not tricky in C - if you want the enumerated type to be accessible
    from other units, put it in a header. If you want the functions that
    are called to be internal to an implementation file and not mentioned in
    the header, just have the state names in the header and use token
    pasting to make function names automatically - something like
    "state0_handler" - in the implementation file. Simple, consistent,
    clear, and it is almost impossible to change the states out of sync with
    the handlers without leading to a compile-time error. And /that/ is the
    main purpose - not reducing typing, or looking neat.

    (I do not think C's way of handling import/export of symbols is ideal.
    It might be that your language's way is better, in at least some ways.
    But since this is C, we do it the C way.)


    But I'm wasting my time because you are never going to admit my feature
    is superior to X-macros for this purpose.

    It's not. Your feature is fine for a very limited use-case, but it is
    not close to the flexibility of x-macros.



    Let's try a more advanced example.



    #include <stdio.h>
    #include <string.h>
    #include <stdbool.h>


    #define REMOVE_COMMAS_(_1, _2, _3, _4, _5, _6, _7, _8, ...) \
    _1 _2 _3 _4 _5 _6 _7 _8
    #define REMOVE_COMMAS(...) REMOVE_COMMAS_(__VA_ARGS__, ,,,,,,,,)


    #define DoCommandList(DO, P, Pvoid) \
    DO(show, "Show item i", (i), P(int, i)) \
    DO(run, "Run item i with val x", (i, x), P(int, i), P(double, x)) \
    DO(hcf, "Stops the whole system and burns everything", (), Pvoid) \
    DO(help, "Shows help for all the commands", (), Pvoid) \


    #define DeclareFunc(cmd, help_text, params, ...) \
    bool cmd_ ## cmd(__VA_ARGS__);

    #define P_declare(type, name) type name
    #define Pvoid_declare void
    DoCommandList(DeclareFunc, P_declare, Pvoid_declare)

    #define ShowHelp(cmd, help_text, params, ...) \
    printf(" " #cmd REMOVE_COMMAS(__VA_ARGS__) " - " help_text "\n"); #define P_help(type, name) " " #name "(" #type ")"
    #define Pvoid_help

    bool cmd_help(void) {
    printf("Help: \n");
    DoCommandList(ShowHelp, P_help, Pvoid_help)
    printf("\n\n");
    return true;
    }

    int get_int_param(void);
    double get_double_param(void);

    #define RunCommand(cmd, help_text, params, ...) \
    if (strcmp(command, #cmd) == 0) { \
    REMOVE_COMMAS(__VA_ARGS__) \
    return cmd_ ## cmd params; \
    }
    #define P_run(type, name) type name = get_ ## type ## _param();
    #define Pvoid_run

    bool dispatch_command(const char * command) {
    DoCommandList(RunCommand, P_run, Pvoid_run)

    printf("Unknown command - try \"help\"\n");
    return false;
    }


    Now, some of that is messy - no doubts there. Some things could have
    been a lot easier if C macros were more powerful, with features such as recursion or neater handling of variadic packs. Macro names scoped
    within functions would also make it better. So there's plenty of room
    for a new language to make things better than C.

    But what do we get out of this? We have all our commands defined in one
    list, with the textual name of the command, the parameters, and a help
    text. You can't get things out of sync - if you add a command, or
    change parameters, the help function, the declarations, and the
    dispatcher all adapt automatically.

    You might not think this is a good way to structure your source code.
    There are many possibilities, including more manual work, or more
    run-time work. You could use an external code generator. You could use
    a language with much better reflection capabilities (like Python). But
    this is something you can do, today, in plain C, and it shows that
    x-macros can do vastly more than declare an enumeration and a table of pointers.


    Now, if your language had powerful reflection and metaprogramming
    features, along with compile-time execution, so that this kind of thing
    could be done within the language without text-based macros, then I
    would happily agree that it has better features. Perhaps the way to do
    that would be to integrate your interpreted language in your compiler.



    (The author of this example goes on to say how it can also be used to
    define a set of function prototypes. I can't do that with my feature.
    But then I don't need function prototypes!)

    At best, if someone has already done the hard work of setting up a
    working set of macros, then you will be able to see how to modify,
    add or delete entries.

    They are not hard.

    Maybe not for you, but it would take me ages to come up with the right incantations to make it work.


    There is a marvellous tool that might help you here - it's called "the internet". You don't need to figure out the right incantations - all
    you need to do is search a little, and learn from other people, and you
    will have cases like the first simple usage sorted in minutes. It takes
    a bit of imagination, experience and trial and error to "invent"
    x-macros, and a lot more to "invent" creative uses of variadic macros.
    But no one is asking you to do that - all I am saying is that it is easy
    to look at existing code, read some blogs or explanations online, and understand what it is doing. Then you can adapt it to your own uses if
    you want.

    I simply don't believe you cannot understand how that first "jump_table" x-macro example works. It requires nothing more than beginners level C knowledge to comprehend it, and we both know you are a very intelligent
    and experienced programmer. Your attempts at claiming it's all too
    difficult for you are, quite frankly, pathetic.

    For the purposes of creating parallel sets of enums and associated
    data, I use special syntax which makes for a clearer and simpler
    feature.

    And that is, obviously, utterly useless - because it is not C or even
    a commonly supported extension.  Oh, and even if it /were/ part of C,
    it would not help because that's not what I was doing.

    I've given an example of a use-case for C macros which have a special
    purpose feature in other languages. So of cause it's not in C.


    And since this is comp.lang.c, I am showing C code. Code from other
    languages can sometimes be interesting for comparison, but that's all.


    Have you ever considered that if C didn't have macros, or they
    weren't powerful, then it could have evolved superior, built-in
    alternatives?


    The C preprocessor is part of C - it is already built in.

    You missed my point. Take a tiny feature like being able to easily get
    the size of a fixed-length array. You commonly see macro like this:

       #define LENGTH(a) (sizeof(a)/size(a[0]))

    What incentive is there to properly add a built-in way to do that, when
    it can be done, badly, and in 1000 different ways by each user, in a
    one-line macro?

    What need is there to add a feature to the language when you can already
    handle the situation?


    Another example is GETBIT(a, n).

    It wasn't a compiler limit - it was mine! The resulting expression
    was completely unreadable.

    I wonder if the code authors also found this expanded expression
    unreadable.

    Look at the example I posted in reply to Blue-Maned_Hawk.


    The code written by the authors was :

    op_arith(L, l_addi, luai_numadd);

    Now, I am not familiar with the Lua source code, but I presume that for
    people that are, these identifiers will all make perfect sense and it's
    all very simple and clear.

    You are looking at this all from completely the wrong viewpoint -
    intentionally trying to make it look difficult. It makes no more sense
    than taking the binary of a program and looking at all the ones and
    zeros and trying to understand how they fit together.

    In particular consider my last comment.

    Which one? "This looks fun to try to understand, debug or port" ? The
    Lua source code is extremely portable, and I've compiled it on 64-bit
    PC's and 16-bit microcontrollers. If anyone wants to understand, debug
    or port it, they will do so by looking at the high-level code, and
    working on things bit for bit as necessary. They won't expand all the
    macros and then go and cry.

    Or do you mean the question about why inlined functions were not used?
    You'd have to ask the Lua developers about that. It might be a matter
    of the code age, or the target C dialect, or concerns about efficiency
    on some compilers, or habit, or wanting to use many variables without
    passing them as parameters. There could be multiple reasons, and none
    of us here are really in the position to answer.



    I guess not - because they used macros so that they could write it in
    a clear and understandable way, and never have to look at the
    expansion!  You don't seem to comprehend the point of macros at all.

    In the end I resorted to looking at the AST as that was clearer than
    C source code.


    Why not just look at the code as written, if you want to understand it?

    In my case I was debugging my C compiler. Then you need to examine the
    actual expanded code in detail. Excessive use of macros makes that much harder.

    So the problem is your compiler, not other peoples' code? You think
    other people should write their code in a way that makes life as easy as possible for you, writing your compiler? That's a pretty ass-backwards attitude you have there. Don't you know the world does not revolve
    around you and your personal projects?


    That example came from Lua 5.4, an interpeter whose performance on a par
    with my own product running from HLL-only code. That doesn't doesn't use deeply-nested macro like this, and it doesn't appear to suffer,.


    Or would doing something that sensible put a damper on your rants?

    Look, I'm written a C preprocessor (I suspect you haven't). If I wanted,
    I could adapt it to work on my own languages.


    I expect you could, yes.

    But I /don't/ want to. Obviously I see something undesirable about them
    (I don't like metaprogramming in general; people get too carried away
    with it and want to show off their skills).

    Why can't you accept that?


    Oh, I fully appreciate that you don't like macros, or the C
    preprocessor, or C in general, or C compilers, or pretty much anything
    else that every other programmer works with daily. You are entitled to
    your own opinions and tastes.

    What I don't accept is your believe that you have a rational
    justification for your hatred and prejudice. And in my boundless and unjustified optimism, I keep thinking that if I can persuade you to
    learn a bit, and open your mind a little, then you will have a better understanding of how the rest of the programming world works.


    Half my use-cases involve inline assembly.

    Why can't that be in inlined functions?

    That doesn't work in inline assembly.


    So that's a limitation of your tools.

    Again, you are not showing that these limited macros have any benefits
    compared to what is found in C,

    That's true. I can't write something like:

         #define M a+b)*c

         (M;


    Nor can you write more useful macros.


    Macros are a C language artefact, and what they expand to is
    arbitrary C syntax that can be meaningless elsewhere.

    Yes - C code is C code.  It is not Pascal, or Fortran, or Ada.  Should
    this be a surprise?

    It's inconvenient. This is a macro from SDL2:

      #define SDL_CompilerBarrier()   \
      { SDL_SpinLock _tmp = 0; SDL_AtomicLock(&_tmp);
    SDL_AtomicUnlock(&_tmp); }

    Rather than declaring a function which resides in a language-neutral
    DLL, it declares it here, in the form of actual C code.


    That would be a macro that is relevant to C. Other languages would use different ways to implement compiler barriers, if they need such things.
    (It is, as Chris pointed out, a very weird way to make a compiler
    barrier. But that's another matter.)

    Even given a language with an FFI that is capable of calling external functions compiled as C, what are they supposed to do with this?


    As usual, you are blaming C for things relevant to other languages.

    That example was in a conditional block; I don't know the context. But
    here's a juicy one that is the result of an attempt to automatically translate the C SDL2 API to my syntax:

    global macro  SDL_FOURCC(A,B,C,D) = ((SDL_static_cast(Uint32,SDL_static_cast(Uint8,(A)))<<0)|(SDL_static_cast(Uint32,SDL_static_cast(Uint8,(B)))<<8)|(SDL_static_cast(Uint32,SDL_static_cast(Uint8,(C)))<<16)|(SDL_static_cast(Uint32,SDL_static_cast(Uint8,(D)))<<24))

    This also contains arbitrary C code; it needs a much more sophisticated
    tool than one that just does declaration. Basically a transpiler from
    C to the language one is writing bindinds for.


    You already have a habit of cherry-picking example code that is as
    "Bart-unfriendly" as you can find.

    Try testing your own C compiler on just about any codebase. You don't
    have to look far for extreme examples.

    I don't have your experience, but I find that /very/ hard to believe.
    I should imagine it's primarily a matter of working through the
    "translation phases" and "preprocessing directives" described in the C
    standards, step by step.

    The C standard has very little to say about. I once found a very, very
    long article which went into considerably more detail, now lost.


    It has plenty to say about the C preprocessor - it fully defines it.

    But even 1/3 through I decided that it was impossible.

    In 2017 when I created my CPP, many compilers would give different
    results with various edge cases of macros. Which were right and which
    were wrong? You'd think there'd be a definitive reference for it.

    Here's an example I've just found:

     #include <stdio.h>
     int main(void) {

      #define FOO
         #define BAR defined(FOO)
         #if BAR
             puts("BAR");
         #else
             puts("FOO");
         #endif
     }

    Most compilers will display BAR; MSVC I think is the only one showing
    FOO. (Some smaller compilers I tried on godbolt.org failed to compile it.)

    Mine showed BAR (obviously!)


    So MSVC has a bug. Report it if you like.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to Bart on Wed Jan 3 19:14:09 2024
    On Wed, 3 Jan 2024 13:03:18 +0000, Bart wrote:

    It's almost like they did it on purpose.

    It’s all a conspiracy, don’t you know. Microsoft is the poor, long- suffering Donald Trump of the computing world--everybody is against them.

    It does sound as though the GMP people crammed in as
    many Linux dependencies as possible just to spite MS.

    And Microsoft is totally powerless to prevent it. It’s like it has no development resources of its own to come up with anything better.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to David Brown on Wed Jan 3 19:57:32 2024
    On 2024-01-03, David Brown <david.brown@hesbynett.no> wrote:
    On 03/01/2024 18:14, Bart wrote:
    On 03/01/2024 15:32, Scott Lurndal wrote:
    Bart <bc@freeuk.cm> writes:
    On 02/01/2024 23:24, tTh wrote:
    On 1/2/24 21:24, Bart wrote:

    You missed my point. Take a tiny feature like being able to easily get >>>>>> the size of a fixed-length array. You commonly see macro like this: >>>>>>
         #define LENGTH(a) (sizeof(a)/size(a[0]))

         And why is that bad? That's a real question.

    Where do I start?

    * Why is it necessary for a million programmers to each come up with
       their own solutions for something so basic? Eg. they will all use >>>>    diferent names.

    Why is it necessary to exaggerate?

    How do you know whether I'm exaggerating or not? There have surely been
    many millions of people who've programmed in C over the years.


    And how many of them define or use such a macro? Some, certainly, but
    not all.

    I've seen it a lot. If it didn't have issues, it would be an excellent inclusion in <stddef.h>, along with offsetof(type, member) and such.

    Macros with issues should not be standardized though. For instance min
    and max macros appear regularly in C programs, but feature multiple
    argument evaluation.

    For these kinds of things, it's better to wait until the language
    develops a good solution. min and max want to be type-generic inline
    functions. I think that this is doable in C with _Generic. In the
    April 2023 draft, I don't see any min functions other than fminf,
    fmin and fminl, which are float, double and long double. No generic
    min and max are mentioned for <tgmath.h>

    There are probably too many combinations to handle; you need
    two levels of _Generic selection, each switching on a number of integer
    and floating-point types. (There being reams and reams of templates
    doesn't stop C++, though.)

    For counting the elements in an array, we really want a sizeof-like
    keyword, which takes a parenthesized type or expression. That expression
    is constrained to be of array type.

    (Might it be possible with _Generic to detect that we have an operand
    which is an "array of anything"? I'm guessing not.)

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Janis Papanagnou on Wed Jan 3 20:28:21 2024
    On 02/01/2024 20:54, Janis Papanagnou wrote:
    On 02.01.2024 20:35, Lawrence D'Oliveiro wrote:
    On Tue, 2 Jan 2024 20:23:24 +0100, Janis Papanagnou wrote:

    That said; during my early K&R C era we had 'register' declarations, but >>> I rarely saw them, they seem to have quickly vanished from usage. Now
    I've heard that 'inline' optimizations have been introduced in C. Isn't
    that considered a task for the compiler?

    Kids’ stuff. Want to see how a _real_ compiler does it?
    <https://gcc.gnu.org/onlinedocs/gcc-13.2.0/gcc/Attribute-Syntax.html>

    What shall I infer from that statement and link? - Mind to elaborate?

    My original question was related to compiler (as opposed to programmer)
    doing optimizations. I recall from decades ago that compilers will do optimizations e.g. on the attributed syntax tree level, while 'register'
    or 'inline' seem very primitive constructs (on a comparable low level).
    So I expressed my astonishment that 'inline' had been later introduced
    in C, and I wonder why. (Note that the other poster also mentioned it
    as a preferred way to replace [parameterized] macros, if I interpreted
    him correctly.)

    Janis


    Lua is designed to be highly portable, including to compilers that are
    not good at optimising, or are limited to old C dialects. (Many
    programmers think the world consists of Windows, Linux, and Macs, but C
    is used regularly on hundreds of different architectures, some of which
    are very limited and/or have very poor quality tools.) If you only ever
    have to deal with good quality tools on reasonable target processors,
    then you are correct that you should focus on writing good, clear, safe
    code, and let the compiler handle the low-level optimisations.

    "register" is pretty much useless, and ignored for most compilation
    (except for the side-effect that you cannot take the address of a
    register variable). In C++, it's function as a storage-class specifier
    has been removed so that it is available for completely different uses
    in the future.

    "inline" in C is really just a hint that the compiler should make the
    function as fast as possible, but on simpler compilers (or advanced
    compilers with only simpler optimisation enabled) it can be treated as a
    strong hint. It also allows you to have a local "inline" definition for
    a function and a separate external linkage version, but I advise against
    that as unhelpful and potentially confusing - IMHO, inline functions
    should always be static.

    (In C++, "inline" means there may be more than one definition of the
    function or object in the program, but you promise that all of them will
    be equivalent.)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Bart on Wed Jan 3 20:16:17 2024
    On 03/01/2024 18:14, Bart wrote:
    On 03/01/2024 15:32, Scott Lurndal wrote:
    Bart <bc@freeuk.cm> writes:
    On 02/01/2024 23:24, tTh wrote:
    On 1/2/24 21:24, Bart wrote:

    You missed my point. Take a tiny feature like being able to easily get >>>>> the size of a fixed-length array. You commonly see macro like this:

         #define LENGTH(a) (sizeof(a)/size(a[0]))

         And why is that bad? That's a real question.

    Where do I start?

    * Why is it necessary for a million programmers to each come up with
       their own solutions for something so basic? Eg. they will all use
       diferent names.

    Why is it necessary to exaggerate?

    How do you know whether I'm exaggerating or not? There have surely been
    many millions of people who've programmed in C over the years.


    And how many of them define or use such a macro? Some, certainly, but
    not all. I can't say I have ever had one in my code as far as I
    remember. Occasionally I find it convenient to calculate the size of an existing array, but I'll just write it manually as an expression (like
    Scott seems to do). Generally if I need the size of an array, I already
    know it - I can use the same "no_of_samples" (or whatever) constant I
    used when defining the array in the first place.

    And a large proportion may have needed the length of an array whose dimensions are not set by a constant, but by the number of data elements provided.

    I have no basis to guess whether this proportion is large or small. I
    don't imagine you do either.


    (Even in the former case, if you have int A[N] and B[N], I think it is
    better to have 'LENGTH(A)' within the subsequent code, rather than a
    bare 'N' which could mean anything: is it the length or A, B, or does it
    mean N by itself? What happens if you change it to A[M]?)

    The trick is not to use single letter identifiers when you want their
    meaning to be clear.


    I would not object to there being a standard C macro for finding the
    size of an array. But I think it would be out of character for the
    standard library. It would make more sense if the language had more
    support for arrays and allowing them as values, parameters, and in
    expressions - then a standard "size" feature would be expected.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to David Brown on Wed Jan 3 21:32:35 2024
    On 03/01/2024 16:41, David Brown wrote:
    On 02/01/2024 21:24, Bart wrote:

    I personally like to use a slightly different syntax, but that's just
    detail and style choice.


    #define DoStateList(DO) \
            DO(state0, func0) /* Comment */ \
            DO(state1, func1) \
            DO(state2, func2) \
            DO(state3, func3) \

    #define GetState(state, func) state,
    #define GetFunc(state, func) func,
    #define Counter(state, func) +1

    enum { num_of_states = DoStateList(Counter) };

    enum States {
        DoStateList(GetState)
    };

    p_func_t jump_table[] = {
        DoStateList(GetFunc)
    };



    I admit that your version is cleaner than other versions of X-macros
    I've come across. I can almost even understand it.

    But nearly every version I've seen /has/ been ugly, including one which
    may have been in a version of Lua. (One problem still is recognising
    uses of X-macros, as there is no prefix like 'xmacro' to look for. So
    the current version may well have them; I can't tell.)

    Note that the states can have comments.  You do need a backslash at the
    end of each line, and that means comments must be /* */ style, not //
    style.

    I tried various combinations but not /*...*/ before the \.

    And since we have the x-macro in place, we can use it for more things.
    Why manually declare "extern void func0(void);", etc., when you can do
    it in two lines for all the functions?

    #define DeclareFunc(state, func) extern void func(void); DoStateList(DeclareFunc)

    You can also ask why need the prototype. (My first big C app, I used a
    script to process my code, which also generated two sets of function prototypes: one for locals, one for exported. This applied to all define functions not just the ones relating to enums.)

    Maybe you want a switch rather than a jump table - that could give more inlining opportunities, and is a common choice for the structure of
    things like simple VM's and bytecode interpreters:

    #define SwitchEntry(state, func) case state : func(); break;
    void jump(enum States s) {
        switch (s) {
            DoStateList(SwitchEntry);
        }
    }


    That's an interesting use. But rather limited as it is, if it only
    contains a function call; a table of functions is better. Here you'd
    want to capture the generated output, and use that as a framework to
    populate with manual code later on.



    With my feature, it is merely this:

         global enumdata []ref void jumptable =
             (state1, func1),        # comment 1
             (state2, func2),
             (state3, func3),        # comment 3
             (state4, func4),
         end


    That is an very niche and very limited use-case.  Did you seriously add
    a special feature to your language based on one example in one stack
    overflow question?

    It's not limited at all. I use it very, very extensively. Virtually all
    of my enums are written in this form, as most will at least have
    associated names, if not other related data.

    I rarely use bare enums. By contrast, most C source code uses bare enum
    lists; there is very little use of X-macros.

    I wonder if that would be different if they were a built-in,
    easier-to-use feature?

    X macros are just an imaginative way to use a
    standard feature of C (and C++), giving vastly more flexibility than
    your single-use language feature.  I have difficulty seeing how it was
    worth the effort putting that feature in your tools.

    That 'feature' used to done with an external script, with input coming
    from text files. Putting it into the language added only one 130-line
    function, and was superior and much tidier.


    Let's try a more advanced example.


    #define REMOVE_COMMAS_(_1, _2, _3, _4, _5, _6, _7, _8, ...) \
        _1 _2 _3 _4 _5 _6 _7 _8
    #define REMOVE_COMMAS(...) REMOVE_COMMAS_(__VA_ARGS__, ,,,,,,,,)


    <snip>

    I'm sorry, but this where people get crazy with macros.

    It's not just x-macros anymore, but just macros.

    (Here, you will appreciate having one simple dedicated feature that you
    KNOW does one thing: declare parallel enums/arrays, or arrays/arrays, in
    table form.)

    If I have time, I will figure out what your code is meant to do later
    (it has some functions that need to be added before I can run it to see
    what it does).

    Then I will post a more readable version.

    ... OK, I had a look. I think it is good example of using macros
    ingeniously, but a poor example of code as it looks terrible. I think
    far from drawing things together, it looks all over the place.

    I tweaked your version into a runnable program, which took 66 lines of
    code. Then I took the expanded, non-macro version and tidied it up; it
    was 45 lines and far more readable.

    I doubt there would be that much savings in vertical space if scaled up
    to a lot more commands.

    It's hard to offer alternatives, since the task is unclear (using two
    levels of handler code).

    But, in my stuff this sort of thing typically makes use of function
    reflection. So given a command "show", I can scan function names for one
    called "cmd_show", but this tends to be done in a setup step that
    populates a table.

    The associated help text is harder; two features that might have helped (function metadata strings and docstrings) I used to have, but have
    since dropped.

    However there are lots of alternatives that still be clearer than your
    example (one more is given below).

    Now, some of that is messy - no doubts there.  Some things could have
    been a lot easier if C macros were more powerful,

    This is the danger - piling on even more features to a language already
    ten times harder to code in that C.

    If you're going to be adding features, how about fixing the main language?

    with features such as
    recursion or neater handling of variadic packs.  Macro names scoped
    within functions would also make it better.  So there's plenty of room
    for a new language to make things better than C.

    But what do we get out of this?  We have all our commands defined in one list, with the textual name of the command, the parameters, and a help text.  You can't get things out of sync - if you add a command, or
    change parameters, the help function, the declarations, and the
    dispatcher all adapt automatically.

    If you adopted enums to represent commands, things can stay in sync too
    (using my dynamic language so no types):

    enumdata cmdnames, cmdhelp, cmdhandlers =
    (showcmd, "show", "show help", cmd_show),
    ...

    Look up a command in 'cmdnames[]'; if found this can be used to index 'cmdhelp[]' and 'cmdhandlers[]'. Each handler function is conventional.


    You might not think this is a good way to structure your source code.
    There are many possibilities, including more manual work, or more
    run-time work.  You could use an external code generator.  You could use
    a language with much better reflection capabilities (like Python).  But
    this is something you can do, today, in plain C, and it shows that
    x-macros can do vastly more than declare an enumeration and a table of pointers.

    It simply highlights my point that /because/ C can offer such untidy,
    half-way solutions, it is less likely that anyone is going to touch the
    main language.



    Now, if your language had powerful reflection and metaprogramming
    features, along with compile-time execution, so that this kind of thing
    could be done within the language without text-based macros, then I
    would happily agree that it has better features.  Perhaps the way to do
    that would be to integrate your interpreted language in your compiler.

    I can tell you that my 'mcc' compiler had some trouble with those macros
    (but it seemed OK if I preprocessed it first then compiled that). I
    don't relish going to back to my CPP 7 years on. So any solution that
    doesn't stress it is preferable!


    There is a marvellous tool that might help you here - it's called "the internet".

    That's great; I can finally learn Chinese too!

    Meanwhile in these 49 numbers you will find next week's winning lottery numbers: 1 2 3 ... 49.

    So the problem is your compiler, not other peoples' code?  You think

    With such a project you are forced to delve more deeply into other
    people's source codes and headers than most. It can be revealing.


    That doesn't work in inline assembly.


    So that's a limitation of your tools.

    Invoking a function from assembly involves a sequence of instructions.

    In order to inline the call, means knowing how many of those
    instructions are involved so that they can be replaced. Maybe the ones
    directly to do with the call are intermingled with others.

    It is just not practical.


    It has plenty to say about the C preprocessor - it fully defines it.

    Many would disagree. Like those who have tried to implement them:

    'The C99 standard is about 500 pages, but only 19 of them are dedicated
    to describing how the C preprocessor should work. Most of the specs are
    high level qualitative descriptions that are intended to leave lots of
    freedom to the compiler implementor. It is likely that this vagueness in
    this specification was intentional so that it would not cause existing mainstream compilers (and code) to become non-conforming. Design freedom
    is good for allowing people to create novel optimizations too, but to
    much freedom can lead to competing interpretations.

    Bjarne Stroustrup even points out that the standard isn't clear about
    what should happen in function macro recursion[1]. With reference to a specific example he says "The question is whether the use of NIL in the
    last line of this sequence qualifies for non-replacement under the cited
    text. If it does, the result will be NIL(42). If it does not, the
    result will be simply 42.". In 2004, a decision was made to leave the
    standard in its ambiguous state: "The committee's decision was that no realistic programs "in the wild" would venture into this area, and
    trying to reduce the uncertainties is not worth the risk of changing conformance status of implementations or programs."'

    https://blog.robertelder.org/7-weird-old-things-about-the-c-preprocessor/

    So MSVC has a bug.  Report it if you like.

    Or maybe all the others do!

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to David Brown on Wed Jan 3 23:48:18 2024
    On 03/01/2024 19:16, David Brown wrote:
    On 03/01/2024 18:14, Bart wrote:

    How do you know whether I'm exaggerating or not? There have surely
    been many millions of people who've programmed in C over the years.


    And how many of them define or use such a macro?  Some, certainly, but
    not all.  I can't say I have ever had one in my code as far as I
    remember.  Occasionally I find it convenient to calculate the size of an existing array, but I'll just write it manually as an expression (like
    Scott seems to do).

    So writing the same long identifier twice? And hoping there's no typo in
    one? Because sizeof(A)/sizeof(B[0]) would be legal code when both A and
    B are arrays.

    (I already know your counter-argument: what stops someone writing
    LENGTH(B) instead of LENGTH(A) anyway. Well, writing it twice gives two opportunities to get it wrong!)

    Generally if I need the size of an array, I already
    know it - I can use the same "no_of_samples" (or whatever) constant I
    used when defining the array in the first place.

    This is my A/B/N argument below. Maybe 'no_of_samples' is used as the
    dimension for more than one array.

    And a large proportion may have needed the length of an array whose
    dimensions are not set by a constant, but by the number of data
    elements provided.

    I have no basis to guess whether this proportion is large or small.  I
    don't imagine you do either.

    Well, here is an extract from sqlite3.c:

    /*
    ** A convenience macro that returns the number of elements in
    ** an array.
    */
    #define ArraySize(X) ((int)(sizeof(X)/sizeof(X[0])))

    They considered it worth having. Here's another from SDL2:

    /**
    * The number of elements in an array.
    */
    #define SDL_arraysize(array) (sizeof(array)/sizeof(array[0]))

    I didn't see one in TCC sources; they have stuff like this instead:

    for(i = 0; i < sizeof(reg_saved)/sizeof(reg_saved[0]); i++) {

    A loop that I would write (in 1-based form), as:

    for i to reg_saved.len do

    I write 'i' once, not three times; and 'reg_saved' once, not twice.

    My view remains that C could do with a standardised macro, or a built-in feature like the lengthof() extension I demonstrated.

    (Even in the former case, if you have int A[N] and B[N], I think it is
    better to have 'LENGTH(A)' within the subsequent code, rather than a
    bare 'N' which could mean anything: is it the length or A, B, or does
    it mean N by itself? What happens if you change it to A[M]?)

    The trick is not to use single letter identifiers when you want their
    meaning to be clear.

    That doesn't affect my point. You still can't tell whether N or anything
    longer refers to the length of the first or second array. Including the
    name of the thing you're taking the length of is useful redundancy.

    I would not object to there being a standard C macro for finding the
    size of an array.  But I think it would be out of character for the
    standard library.

    It's in character for C to define a chunk of the language via the
    standard library. That doesn't appeal to me, it is very crude aspect of
    the language, and also makes basic language features optional: they
    don't exist until specifically enabled.



    It would make more sense if the language had more
    support for arrays and allowing them as values, parameters, and in expressions - then a standard "size" feature would be expected.


    I can't see how that effects anything. If you have:

    T A[] = {...};

    then it always makes sense to have a direct way of getting the number of elements in A without resorting to those measures demonstrated above.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to Bart on Thu Jan 4 01:57:38 2024
    Bart <bc@freeuk.cm> writes:
    On 03/01/2024 19:16, David Brown wrote:
    On 03/01/2024 18:14, Bart wrote:

    How do you know whether I'm exaggerating or not? There have surely
    been many millions of people who've programmed in C over the years.


    And how many of them define or use such a macro?  Some, certainly, but
    not all.  I can't say I have ever had one in my code as far as I
    remember.  Occasionally I find it convenient to calculate the size of an
    existing array, but I'll just write it manually as an expression (like
    Scott seems to do).

    So writing the same long identifier twice?

    Yes. Good editors mean you don't need to type it
    twice (ywllp).

    And hoping there's no typo in one?

    If there's a typo, the compiler will note it and I'll
    fix it. But, see above.


    Because sizeof(A)/sizeof(B[0]) would be legal code when both A and
    B are arrays.

    Good thing I don't use single letter identifiers.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to Scott Lurndal on Thu Jan 4 02:20:47 2024
    On 04/01/2024 01:57, Scott Lurndal wrote:
    Bart <bc@freeuk.cm> writes:
    On 03/01/2024 19:16, David Brown wrote:
    On 03/01/2024 18:14, Bart wrote:

    How do you know whether I'm exaggerating or not? There have surely
    been many millions of people who've programmed in C over the years.


    And how many of them define or use such a macro?  Some, certainly, but
    not all.  I can't say I have ever had one in my code as far as I
    remember.  Occasionally I find it convenient to calculate the size of an >>> existing array, but I'll just write it manually as an expression (like
    Scott seems to do).

    So writing the same long identifier twice?

    Yes. Good editors mean you don't need to type it
    twice (ywllp).

    And hoping there's no typo in one?

    If there's a typo, the compiler will note it and I'll
    fix it. But, see above.


    Because sizeof(A)/sizeof(B[0]) would be legal code when both A and
    B are arrays.

    Good thing I don't use single letter identifiers.


    What are you, 5 years old?

    A and B are placeholders for arbitrary identifiers of any length.

    How about if I change my remark to:

    "Because sizeof(pentium_msr_passthrough_with_knobs_on)/sizeof(k6_msr_passthrough_with_knobs_on)
    would be legal code when both pentium_msr_passthrough_with_knobs_on and k6_msr_passthrough_with_knobs_on are arrays."

    You get it now?

    But you're just winding me up aren't you?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Kaz Kylheku on Thu Jan 4 09:46:48 2024
    On 03/01/2024 20:57, Kaz Kylheku wrote:
    On 2024-01-03, David Brown <david.brown@hesbynett.no> wrote:
    On 03/01/2024 18:14, Bart wrote:
    On 03/01/2024 15:32, Scott Lurndal wrote:
    Bart <bc@freeuk.cm> writes:
    On 02/01/2024 23:24, tTh wrote:
    On 1/2/24 21:24, Bart wrote:

    You missed my point. Take a tiny feature like being able to easily get >>>>>>> the size of a fixed-length array. You commonly see macro like this: >>>>>>>
         #define LENGTH(a) (sizeof(a)/size(a[0]))

         And why is that bad? That's a real question.

    Where do I start?

    * Why is it necessary for a million programmers to each come up with >>>>>    their own solutions for something so basic? Eg. they will all use >>>>>    diferent names.

    Why is it necessary to exaggerate?

    How do you know whether I'm exaggerating or not? There have surely been
    many millions of people who've programmed in C over the years.


    And how many of them define or use such a macro? Some, certainly, but
    not all.

    I've seen it a lot. If it didn't have issues, it would be an excellent inclusion in <stddef.h>, along with offsetof(type, member) and such.

    Macros with issues should not be standardized though. For instance min
    and max macros appear regularly in C programs, but feature multiple
    argument evaluation.

    Arguably the "array size" macros /do/ have issues. Either they are too
    easy to use accidentally with pointers instead of arrays, or they cause compile-time errors for arrays of size one (due to protection against
    use with pointers), or they rely on compiler extensions to protect
    against use on pointers.

    In one way, that would make it a better choice to have "array_size" in
    the standard - the standard could simply require that applying
    "array_size" to a pointer is a constraint error, and it's up to the implementation to figure out what compiler magic is needed to make that
    work. (The "offsetof" macro is similar - it can't be implemented in
    completely portable, compliant and fully defined behaviour C code.)

    However, I think it is all too late for that. It certainly can't be
    added to <stddef.h> or <stdlib.h> - what would you call it without
    conflicting with existing names in existing code? You'd have to put it
    in its own header, and it would all be a huge amount of work and fuss
    for something that no one would ever use - everyone who wants such a
    macro can write one faster themselves than it takes to type "#include <stdarraysize.h>".


    For these kinds of things, it's better to wait until the language
    develops a good solution. min and max want to be type-generic inline functions. I think that this is doable in C with _Generic. In the
    April 2023 draft, I don't see any min functions other than fminf,
    fmin and fminl, which are float, double and long double. No generic
    min and max are mentioned for <tgmath.h>

    There are probably too many combinations to handle; you need
    two levels of _Generic selection, each switching on a number of integer
    and floating-point types. (There being reams and reams of templates
    doesn't stop C++, though.)



    C++ has three major differences for this kind of thing. One is that
    they have a good namespace for new standard library functions, hugely
    reducing the risk and cost of adding functions. They have arrays as
    "real" types - useable as values and with strong typing, eliminating the complication of decaying to pointers to the first element. (If you are programming in C++ and are working with fixed size arrays, std::array<>
    is often a better choice than C-style arrays. And if you are using
    dynamic arrays, std::vector<> is probably best.) And C++ already has an advanced template system that is far beyond what you can do with C11's _Generic.

    So I think you are partially right - I agree it is better not to try to
    have this kind of thing in the standard library without better language
    support for metaprogramming and for more solid arrays, but I don't think
    C will or should gain such features. I think C should be considered
    "mostly complete" - some improvements are helpful, but not major
    language changes. The whole point of C is its stability as a language,
    and if a programmer wants more, it's better to choose a different
    language such as C++ or one of the "better than C but less complicated
    than C++" options such as D or Rust.

    For counting the elements in an array, we really want a sizeof-like
    keyword, which takes a parenthesized type or expression. That expression
    is constrained to be of array type.

    (Might it be possible with _Generic to detect that we have an operand
    which is an "array of anything"? I'm guessing not.)


    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Bart on Thu Jan 4 09:55:20 2024
    On 04/01/2024 00:48, Bart wrote:
    On 03/01/2024 19:16, David Brown wrote:
    On 03/01/2024 18:14, Bart wrote:

    How do you know whether I'm exaggerating or not? There have surely
    been many millions of people who've programmed in C over the years.


    And how many of them define or use such a macro?  Some, certainly, but
    not all.  I can't say I have ever had one in my code as far as I
    remember.  Occasionally I find it convenient to calculate the size of
    an existing array, but I'll just write it manually as an expression
    (like Scott seems to do).

    So writing the same long identifier twice? And hoping there's no typo in
    one? Because sizeof(A)/sizeof(B[0]) would be legal code when both A and
    B are arrays.


    If you have difficulty typing out the same identifier twice, then you
    have picked a poor name for the identifier! The whole point of
    identifiers is to identify things - you give the thing a name, so that
    you can refer to it later by that same name and know you are referring
    to the same thing. If find this difficult when using the same array
    identifier twice in one expression, then you have vastly bigger problems
    with your coding than getting the size of an array.


    (I already know your counter-argument: what stops someone writing
    LENGTH(B) instead of LENGTH(A) anyway. Well, writing it twice gives two opportunities to get it wrong!)

    No, that was never in my thoughts as a counter-argument.


    Generally if I need the size of an array, I already know it - I can
    use the same "no_of_samples" (or whatever) constant I used when
    defining the array in the first place.

    This is my A/B/N argument below. Maybe 'no_of_samples' is used as the dimension for more than one array.

    Yes, maybe it is. But I know the size of the arrays I use. If I don't
    know, I look at the definition to see. I don't code blindly.


    And a large proportion may have needed the length of an array whose
    dimensions are not set by a constant, but by the number of data
    elements provided.

    I have no basis to guess whether this proportion is large or small.  I
    don't imagine you do either.

    Well, here is an extract from sqlite3.c:

    I didn't write sqlite3.c. I am not responsible for the choices of
    identifiers, or coding style, or macro usage, or anything else in it. I
    am telling you why /I/ don't need or use any kind of "array_size" macro.
    Other people may write their code differently.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to David Brown on Thu Jan 4 12:15:00 2024
    On 04/01/2024 08:55, David Brown wrote:
    On 04/01/2024 00:48, Bart wrote:
    On 03/01/2024 19:16, David Brown wrote:
    On 03/01/2024 18:14, Bart wrote:

    How do you know whether I'm exaggerating or not? There have surely
    been many millions of people who've programmed in C over the years.


    And how many of them define or use such a macro?  Some, certainly,
    but not all.  I can't say I have ever had one in my code as far as I
    remember.  Occasionally I find it convenient to calculate the size of
    an existing array, but I'll just write it manually as an expression
    (like Scott seems to do).

    So writing the same long identifier twice? And hoping there's no typo
    in one? Because sizeof(A)/sizeof(B[0]) would be legal code when both A
    and B are arrays.


    If you have difficulty typing out the same identifier twice, then you
    have picked a poor name for the identifier!  The whole point of
    identifiers is to identify things - you give the thing a name, so that
    you can refer to it later by that same name and know you are referring
    to the same thing.  If find this difficult when using the same array identifier twice in one expression, then you have vastly bigger problems
    with your coding than getting the size of an array.

    You can make typos. You can accidentally use the name of some other
    similarly named but unrelated variable.

    You really can't understand that the opportunities for such errors
    increases with the amount of code you have to write? As well as the
    amount of obscuring clutter?

    It's not just writing two long identifiers instead of one; here those additionally have to match.


    Yes, maybe it is.  But I know the size of the arrays I use.  If I don't know, I look at the definition to see.  I don't code blindly.

    You have this (and please don't complain about these terse names; a real program will be much larger, much busier, and have much longer names):

    enum {N = 10;}
    int A[N];
    int B[N];
    int C[N];

    for (i = 0; i<N; ++N)
    ... Do something with A ...

    for (i = 0; i<N; ++N)
    ... Do something with B ...

    (I will leave the typos I've just noticed. Bloody stupid C for loops...)

    Later I change A[N] to be A[M] as I said a few posts ago. Now you have
    to go through the program looking for things that reference the length of A.

    But you only have N; you can't tell from that itself whether it I
    specifically used as the length of A or not. The job is harder. Now
    consider this version:

    for (i = 0; i<LENGTH(A); ++i)
    ... Do something with A ...

    for (i = 0; i<LENGTH(B); ++i)
    ... Do something with B ...

    Which version will be easier to update? (Hint: this one doesn't need any changes.)



    Well, here is an extract from sqlite3.c:

    I didn't write sqlite3.c.  I am not responsible for the choices of identifiers, or coding style, or macro usage, or anything else in it.  I
    am telling you why /I/ don't need or use any kind of "array_size" macro.
     Other people may write their code differently.


    So in you code, if I see:

    sizeof(expr1)/sizeof(expr2)

    what I am supposed to deduce from that? That it /might/ be an idiom for
    getting the number of elements in array? Then it might involving looking
    in more detail to see that expr2 is really just expr1[0].

    On the other hand, if I see:

    ArraySize(expr)

    then you are expressing what your intentions are more clearly. Moreover,
    with that term now being more compact, I can concentrate more on what
    goes before and after it.

    BTW here is anoher macro, this time from a file called gcc.c (an old amalgamation):

    #define ARRAY_SIZE(a) (sizeof (a) / sizeof ((a)[0]))

    *Everybody* is at it, but that doesn't count because /you/ don't do it?
    It is used like this:

    for (i = 0; i < ARRAY_SIZE (conversion_tab); i++)
    if (sysctl (mib, ARRAY_SIZE (mib), &physmem, &len, NULL, 0) == 0
    b < (operator_array + ARRAY_SIZE (operator_array));
    size_t n = ARRAY_SIZE (builtin_array);

    which would otherwise look like so:

    for (i = 0; i < sizeof(conversion_tab)/sizeof(conversion_tab); i++)
    if (sysctl (mib, sizeof(mib)/sizeof(mib[0]), &physmem, &len, NULL,
    0) == 0
    b < (operator_array +
    sizeof(operator_array)/sizeof(operator_array)[0]);
    size_t n = sizeof(builtin_array)/sizeof(builtin_array[0]);

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Keith Thompson on Thu Jan 4 12:46:00 2024
    On 03/01/2024 22:42, Keith Thompson wrote:
    David Brown <david.brown@hesbynett.no> writes:
    On 02/01/2024 21:24, Bart wrote:
    [...]
    The X-macro solution was this, adapted from the first answer here
    (https://stackoverflow.com/questions/6635851/real-world-use-of-x-macros); assume those functions are in scope:
    -------
    #define STATE_TABLE \
            ENTRY(STATE0, func0) \
            ENTRY(STATE1, func1) \
            ENTRY(STATE2, func2) \
            ENTRY(STATE3, func3) \
    enum
    {
    #define ENTRY(a,b) a,
        STATE_TABLE
    #undef ENTRY
        NUM_STATES
    };
    void* jumptable[NUM_STATES] =
    {
    #define ENTRY(a,b) b,
        STATE_TABLE
    #undef ENTRY
    };
    -------

    (Why did you change the type from "p_func_t" to "void*" ? Was it just
    to annoy myself and other C programmers with a pointless and
    constraint-violating cast of a function pointer to "void*" ? Just add
    a suitable typedef - "typedef void(*p_func_t)(void);" )

    What constraint does it violate? And what cast are you referring to?

    I believe the initialisation follows the requirements for simple
    assignment, and function pointers are not compatible with void*. Bart
    (for reasons understood only by him) uses void* pointers when he wants
    generic function pointers. The stack overflow link uses "p_func_t",
    which is a function pointer typedef.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to David Brown on Thu Jan 4 12:37:40 2024
    On 04/01/2024 11:46, David Brown wrote:
    On 03/01/2024 22:42, Keith Thompson wrote:
    David Brown <david.brown@hesbynett.no> writes:
    On 02/01/2024 21:24, Bart wrote:
    [...]
    The X-macro solution was this, adapted from the first answer here
    (https://stackoverflow.com/questions/6635851/real-world-use-of-x-macros); assume those functions are in scope:
    -------
    #define STATE_TABLE \
              ENTRY(STATE0, func0) \
              ENTRY(STATE1, func1) \
              ENTRY(STATE2, func2) \
              ENTRY(STATE3, func3) \
    enum
    {
    #define ENTRY(a,b) a,
          STATE_TABLE
    #undef ENTRY
          NUM_STATES
    };
    void* jumptable[NUM_STATES] =
    {
    #define ENTRY(a,b) b,
          STATE_TABLE
    #undef ENTRY
    };
    -------

    (Why did you change the type from "p_func_t" to "void*" ?  Was it just
    to annoy myself and other C programmers with a pointless and
    constraint-violating cast of a function pointer to "void*" ?  Just add
    a suitable typedef - "typedef void(*p_func_t)(void);" )

    What constraint does it violate?  And what cast are you referring to?

    I believe the initialisation follows the requirements for simple
    assignment, and function pointers are not compatible with void*.  Bart
    (for reasons understood only by him) uses void* pointers when he wants generic function pointers.  The stack overflow link uses "p_func_t",
    which is a function pointer typedef.



    The SO link didn't define p_func_t that I could see. I changed it to
    void* to avoid the bother of doing so when testing, and to avoid having
    to include that in my example that would be a distraction.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Bart on Thu Jan 4 15:13:57 2024
    On 03/01/2024 22:32, Bart wrote:
    On 03/01/2024 16:41, David Brown wrote:
    On 02/01/2024 21:24, Bart wrote:

    I personally like to use a slightly different syntax, but that's just
    detail and style choice.


    #define DoStateList(DO) \
             DO(state0, func0) /* Comment */ \
             DO(state1, func1) \
             DO(state2, func2) \
             DO(state3, func3) \

    #define GetState(state, func) state,
    #define GetFunc(state, func) func,
    #define Counter(state, func) +1

    enum { num_of_states = DoStateList(Counter) };

    enum States {
         DoStateList(GetState)
    };

    p_func_t jump_table[] = {
         DoStateList(GetFunc)
    };



    I admit that your version is cleaner than other versions of X-macros
    I've come across. I can almost even understand it.

    I also think it is better this way. But I don't believe you can
    /almost/ understand it - I believe you can understand it perfectly well.
    (You might not like it, or see it as useful, but that's a matter of preference and opinion.)


    But nearly every version I've seen /has/ been ugly, including one which
    may have been in a version of Lua. (One problem still is recognising
    uses of X-macros, as there is no prefix like 'xmacro' to look for. So
    the current version may well have them; I can't tell.)

    "x-macro" is not a well-defined term. It is simply the general
    technique of making a list of some sort in a macro, and using it several
    times later.


    Note that the states can have comments.  You do need a backslash at
    the end of each line, and that means comments must be /* */ style, not
    // style.

    I tried various combinations but not /*...*/ before the \.


    You can use /* */ comments in all sorts of places - they get treated as
    white space.

    And since we have the x-macro in place, we can use it for more things.
    Why manually declare "extern void func0(void);", etc., when you can do
    it in two lines for all the functions?

    #define DeclareFunc(state, func) extern void func(void);
    DoStateList(DeclareFunc)

    You can also ask why need the prototype.

    You don't mean "prototype" here, you mean "declaration". A function
    prototype in C gives the number and types of the parameters, the name of
    the function, and the return type, and can be given as a stand-alone declaration or along with the function definition. You are asking why
    we need stand-alone declarations here.

    The easy answer is we need them because C requires declarations before
    you can call a function or take its address. The next question is why
    does C need that for functions that are defined later in the same source
    file, and the answer then is that it avoids needing an extra pass in the compiler. I would not expect newer languages to take such
    considerations and need declarations (other than for imported functions,
    and presumably a more modern language would use some kind of module
    mechanism there).

    (My first big C app, I used a
    script to process my code, which also generated two sets of function prototypes: one for locals, one for exported. This applied to all define functions not just the ones relating to enums.)

    Maybe you want a switch rather than a jump table - that could give
    more inlining opportunities, and is a common choice for the structure
    of things like simple VM's and bytecode interpreters:

    #define SwitchEntry(state, func) case state : func(); break;
    void jump(enum States s) {
         switch (s) {
             DoStateList(SwitchEntry);
         }
    }


    That's an interesting use. But rather limited as it is, if it only
    contains a function call; a table of functions is better. Here you'd
    want to capture the generated output, and use that as a framework to
    populate with manual code later on.


    No, the "manual code" goes in the handler functions.

    And no, a jump table is /not/ better - the switch is better in most ways:

    1. The compiler can always implement the switch with a jump table, if
    that is the most efficient method. It can choose many other ways to do
    it too.

    2. When the compiler can see the source of the functions (they are
    defined in the same file, or you are using LTO or other whole-program optimisation, as you do if you need top efficiency), the code can be
    generated inline in the switch. That can mean reducing function call overheads, sharing code, merging in constants, etc.

    3. If the "jump" function is called in a way that the compiler can see
    the parameter used, it can inline the called handler function in the
    code that calls "jump", again leading to many possible optimisations.

    4. Call tree analysis does not work (or works very poorly) over function pointers. If you can avoid function pointers, programmers, compilers, documentation tools, and static analysis tools have a vastly better
    picture of the possible code flow. This lets you get high-level
    overviews, trace code flow backwards, analyse stack usage, etc. It
    makes debugging and code analysis much easier.




    With my feature, it is merely this:

         global enumdata []ref void jumptable =
             (state1, func1),        # comment 1
             (state2, func2),
             (state3, func3),        # comment 3
             (state4, func4),
         end


    That is an very niche and very limited use-case.  Did you seriously
    add a special feature to your language based on one example in one
    stack overflow question?

    It's not limited at all. I use it very, very extensively. Virtually all
    of my enums are written in this form, as most will at least have
    associated names, if not other related data.

    I rarely use bare enums. By contrast, most C source code uses bare enum lists; there is very little use of X-macros.


    I use enumerated types a lot, but it's rare that it would make sense in
    my code to associate them with anything (names, data, actions) at that
    point in the code.

    I wonder if that would be different if they were a built-in,
    easier-to-use feature?

    It is possible - certainly familiarity will influence how much a
    technique is used. But lots of C programmers are unaware of a good deal
    of language features and standard library tools, so it is no guarantee.

    Remember, the choice of features for /your/ language and library are
    determined almost exclusively by what /you/ want, for the kinds of
    programs /you/ write. That is the advantage of personal languages, but
    it also means that comparisons of features popularity with mainstream
    languages are almost completely pointless. They are of even less
    validity than looking at the features a single C programmer (such as
    myself) uses, or what is used in a particular C program, when trying to
    judge the popularity of C features.


    X macros are just an imaginative way to use a standard feature of C
    (and C++), giving vastly more flexibility than your single-use
    language feature.  I have difficulty seeing how it was worth the
    effort putting that feature in your tools.

    That 'feature' used to done with an external script, with input coming
    from text files. Putting it into the language added only one 130-line function, and was superior and much tidier.


    It's neater if you don't need an external tool, yes.


    Let's try a more advanced example.


    #define REMOVE_COMMAS_(_1, _2, _3, _4, _5, _6, _7, _8, ...) \
         _1 _2 _3 _4 _5 _6 _7 _8
    #define REMOVE_COMMAS(...) REMOVE_COMMAS_(__VA_ARGS__, ,,,,,,,,)


    <snip>

    I'm sorry, but this where people get crazy with macros.

    It's not just x-macros anymore, but just macros.

    I fully appreciate that it requires more effort to understand such
    macros, and their use is not to everyone's taste. People who use these
    things generally have a collection of macros they like in a header, and
    use them as needed without worrying about the details. Mostly the
    macros will come from elsewhere. Actually, all that sounds just like programming as usual - it's what programmers do all the time, in any
    language. Very, very few programmers limit themselves to only ever
    using code they write themselves or understand themselves.


    (Here, you will appreciate having one simple dedicated feature that you
    KNOW does one thing: declare parallel enums/arrays, or arrays/arrays, in table form.)

    Sure - when that feature is sufficient. I was giving an example of
    something a lot more complicated than you can do with your features. It
    does not mean that's the only way to do it, or necessarily the best way
    to do it (for whatever value of "best" you pick) - it's just a possibility.


    If I have time, I will figure out what your code is meant to do later
    (it has some functions that need to be added before I can run it to see
    what it does).

    Then I will post a more readable version.

    OK.


    ... OK, I had a look. I think it is good example of using macros
    ingeniously, but a poor example of code as it looks terrible. I think
    far from drawing things together, it looks all over the place.


    It puts things in /different/ places than you might normally do. That
    splits some things up, and combines other things.


    I tweaked your version into a runnable program, which took 66 lines of
    code. Then I took the expanded, non-macro version and tidied it up; it
    was 45 lines and far more readable.

    Now imagine there were 24 commands, not 4. The implementation of the
    command handlers must, of course, be written regardless of the
    implementation - but apart from that the macro version only needs 20 new
    lines to declare the new commands. Everything else adapt automatically.
    But a "traditional" solution would need changes to the help function
    and the dispatch function as well.

    Now consider putting the "DoCommandList" in a header file "commands.h",
    along with the "DeclareFunc" bits. The file "commands.c" will include
    the "commands.h" file and hold the implementation of all the command
    handlers, except cmd_help and perhaps a few other generic commands. The
    file "cli_base.c" will hold all the other macros and calls, and can have
    things like the "get_int_param" parsers.

    So now you have a complete separation of tasks. Adding new commands
    doesn't involve changes to the base part at all, which can be common for different projects. The project-specific part - the list of commands
    and their handler implementation - is all neatly in one file, which is
    free from all the implementation details and complicated macros.

    You don't use a system like this for four things in a list - you use it
    when you have lots.


    I doubt there would be that much savings in vertical space if scaled up
    to a lot more commands.

    It's hard to offer alternatives, since the task is unclear (using two
    levels of handler code).

    But, in my stuff this sort of thing typically makes use of function reflection. So given a command "show", I can scan function names for one called "cmd_show", but this tends to be done in a setup step that
    populates a table.

    The associated help text is harder; two features that might have helped (function metadata strings and docstrings) I used to have, but have
    since dropped.

    However there are lots of alternatives that still be clearer than your example (one more is given below).

    Now, some of that is messy - no doubts there.  Some things could have
    been a lot easier if C macros were more powerful,

    This is the danger - piling on even more features to a language already
    ten times harder to code in that C.

    Macros are /not/ that hard to use - and the preprocessor is not a
    "language" but an inherent part of C.

    But greater power and more features of macros would make it /easier/ to
    use. Imagine, for example, how the "REMOVE_COMMAS" macros could work if
    the C preprocessor supported recursion and overloading on the number of parameters - relatively easy extensions. (I'll also use the gcc feature
    of giving a name to the "..." bit, instead of the ugly __VA_ARGS__.)
    Currently, we have :

    #define REMOVE_COMMAS_(_1, _2, _3, _4, _5, _6, _7, _8, ...) \
    _1 _2 _3 _4 _5 _6 _7 _8
    #define REMOVE_COMMAS(...) REMOVE_COMMAS_(__VA_ARGS__, ,,,,,,,,)

    If I want that to work with more than 8 parameters, I need to extend it
    with more placeholders and more commas (which would be even messier in a
    Usenet post). But with my suggested features, you'd have:

    #define REMOVE_COMMAS()
    #define REMOVE_COMMAS(x, args...) x REMOVE_COMMAS(args)

    /Much/ better.


    You can google for variadic "FOR_EACH" macro implementations, that let
    you write "FOR_EACH(DO, ...)" and apply the macro "DO" to each parameter
    in the following list. Such macros can be handy in some coding
    situations, but the implementations are very messy - you'd want to hide
    them away in an include file and never look at them again, and they also
    always have limits on the number of parameters. With my suggested added features, "FOR_EACH" would be defined as:

    #define FOR_EACH(DO)
    #define FOR_EACH(DO, x, args...) DO(x) FOR_EACH(DO, args)

    Again, simple, clean recursion. And REMOVE_COMMAS could be :

    #define ID(x) x
    #define REMOVE_COMMAS(args) FOR_EACH(ID, args)


    Of course more power gives more opportunities to make a mess and write
    bad code - but it also gives more opportunities to write /good/ code.


    If you're going to be adding features, how about fixing the main language?

    That's a topic for another thread :-)


    with features such as recursion or neater handling of variadic packs.
    Macro names scoped within functions would also make it better.  So
    there's plenty of room for a new language to make things better than C.

    But what do we get out of this?  We have all our commands defined in
    one list, with the textual name of the command, the parameters, and a
    help text.  You can't get things out of sync - if you add a command,
    or change parameters, the help function, the declarations, and the
    dispatcher all adapt automatically.

    If you adopted enums to represent commands, things can stay in sync too (using my dynamic language so no types):

        enumdata cmdnames, cmdhelp, cmdhandlers =
           (showcmd,   "show",  "show help", cmd_show),
           ...


    Different commands need different numbers and types of parameters.

    Look up a command in 'cmdnames[]'; if found this can be used to index 'cmdhelp[]' and 'cmdhandlers[]'. Each handler function is conventional.


    You might not think this is a good way to structure your source code.
    There are many possibilities, including more manual work, or more
    run-time work.  You could use an external code generator.  You could
    use a language with much better reflection capabilities (like
    Python).  But this is something you can do, today, in plain C, and it
    shows that x-macros can do vastly more than declare an enumeration and
    a table of pointers.

    It simply highlights my point that /because/ C can offer such untidy, half-way solutions, it is less likely that anyone is going to touch the
    main language.


    You have a different attitude to language design than mainstream
    language developers do, because you have /personal/ languages and tools.
    If you want to add something to your language, you just add a couple
    of hundred lines of code to your compiler - it's as easy to add it to
    the language and compiler as to add it to the application code written
    in the language. But for real languages, it's a /completely/ different
    matter. Adding an "enumdata" feature to C would be, at a wild estimate,
    many thousands of manhours work. It would need to be proposed, in a
    paper that considered use-cases, alternatives, names, details,
    specifications, changes to the wording of the C standards. It would
    need to be discussed and considered by many people. Prototype
    implementations would be needed, along with people testing it. People
    would have to consider conflicts with existing code, such as a a
    conflict between the new keyword and existing identifiers. Corner cases
    and interaction with other features would need to be considered and
    handled, such as interoperability with other enumeration types. It has
    to go into the standards drafts, and be voted upon. Compiler
    implementers need to write it, test it, document it, create test cases,
    analyse the efficiency of the generated code, suggest changes. People
    have to add it to their C books, tutorials, courses, blogs.

    Compare that to just writing a few lines of ugly macros and explaining
    them in a blog post. The hurdles for considering changing the core C
    language are significant.

    The more popular and established a language is, the harder it is to
    change it - so there is a huge contrast between the world's most used
    and best established language, and one of the world's least used and
    least established languages. Obviously the benefits from improving the language are also in contrast - improvements to your language help you
    and no one else, while improvements to C could help hundreds of
    thousands of developers, and countless millions of end users. But the
    path getting there is very different.




    Now, if your language had powerful reflection and metaprogramming
    features, along with compile-time execution, so that this kind of
    thing could be done within the language without text-based macros,
    then I would happily agree that it has better features.  Perhaps the
    way to do that would be to integrate your interpreted language in your
    compiler.

    I can tell you that my 'mcc' compiler had some trouble with those macros
    (but it seemed OK if I preprocessed it first then compiled that). I
    don't relish going to back to my CPP 7 years on. So any solution that
    doesn't stress it is preferable!


    There is a marvellous tool that might help you here - it's called "the
    internet".

    That's great; I can finally learn Chinese too!

    Meanwhile in these 49 numbers you will find next week's winning lottery numbers: 1 2 3 ... 49.

    So the problem is your compiler, not other peoples' code?  You think

    With such a project you are forced to delve more deeply into other
    people's source codes and headers than most. It can be revealing.


    It can also be irrelevant. You get hung up on details that don't matter.


    That doesn't work in inline assembly.


    So that's a limitation of your tools.

    Invoking a function from assembly involves a sequence of instructions.

    In order to inline the call, means knowing how many of those
    instructions are involved so that they can be replaced. Maybe the ones directly to do with the call are intermingled with others.

    It is just not practical.

    Some other compilers do it fine (though some other compilers share your limitations). I don't doubt that it would take effort, and I can quite
    believe that it is simply not worth the effort for you - your time and
    energy is not unlimited. But it is still a limitation of your compiler
    that is not inherent in the use of assembly mixed with a language like C.



    It has plenty to say about the C preprocessor - it fully defines it.

    Many would disagree. Like those who have tried to implement them:

    'The C99 standard is about 500 pages, but only 19 of them are dedicated
    to describing how the C preprocessor should work. Most of the specs are
    high level qualitative descriptions that are intended to leave lots of freedom to the compiler implementor. It is likely that this vagueness in
    this specification was intentional so that it would not cause existing mainstream compilers (and code) to become non-conforming. Design freedom
    is good for allowing people to create novel optimizations too, but to
    much freedom can lead to competing interpretations.

    Bjarne Stroustrup even points out that the standard isn't clear about
    what should happen in function macro recursion[1].  With reference to a specific example he says "The question is whether the use of NIL in the
    last line of this sequence qualifies for non-replacement under the cited text.  If it does, the result will be NIL(42). If it does not, the
    result will be simply 42.".  In 2004, a decision was made to leave the standard in its ambiguous state: "The committee's decision was that no realistic programs "in the wild" would venture into this area, and
    trying to reduce the uncertainties is not worth the risk of changing conformance status of implementations or programs."'

    https://blog.robertelder.org/7-weird-old-things-about-the-c-preprocessor/


    OK, so it is /almost/ fully defined. Few things in life are perfect,
    and that includes the C standards. It is, however, good enough to cover
    pretty much every preprocessor usage you'll find in real life, outside
    of the OCCC.

    So MSVC has a bug.  Report it if you like.

    Or maybe all the others do!


    Maybe :-)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Bart on Thu Jan 4 15:29:32 2024
    On 04/01/2024 13:15, Bart wrote:
    On 04/01/2024 08:55, David Brown wrote:
    On 04/01/2024 00:48, Bart wrote:
    On 03/01/2024 19:16, David Brown wrote:
    On 03/01/2024 18:14, Bart wrote:

    How do you know whether I'm exaggerating or not? There have surely
    been many millions of people who've programmed in C over the years.


    And how many of them define or use such a macro?  Some, certainly,
    but not all.  I can't say I have ever had one in my code as far as I
    remember.  Occasionally I find it convenient to calculate the size
    of an existing array, but I'll just write it manually as an
    expression (like Scott seems to do).

    So writing the same long identifier twice? And hoping there's no typo
    in one? Because sizeof(A)/sizeof(B[0]) would be legal code when both
    A and B are arrays.


    If you have difficulty typing out the same identifier twice, then you
    have picked a poor name for the identifier!  The whole point of
    identifiers is to identify things - you give the thing a name, so that
    you can refer to it later by that same name and know you are referring
    to the same thing.  If find this difficult when using the same array
    identifier twice in one expression, then you have vastly bigger
    problems with your coding than getting the size of an array.

    You can make typos. You can accidentally use the name of some other
    similarly named but unrelated variable.

    You really can't understand that the opportunities for such errors
    increases with the amount of code you have to write? As well as the
    amount of obscuring clutter?

    It's not just writing two long identifiers instead of one; here those additionally have to match.


    Yes, maybe it is.  But I know the size of the arrays I use.  If I
    don't know, I look at the definition to see.  I don't code blindly.

    You have this (and please don't complain about these terse names; a real program will be much larger, much busier, and have much longer names):

       enum {N = 10;}
       int A[N];
       int B[N];
       int C[N];

       for (i = 0; i<N; ++N)
         ... Do something with A ...

       for (i = 0; i<N; ++N)
         ... Do something with B ...

    (I will leave the typos I've just noticed. Bloody stupid C for loops...)

    Later I change A[N] to be A[M] as I said a few posts ago. Now you have
    to go through the program looking for things that reference the length
    of A.

    But you only have N; you can't tell from that itself whether it I specifically used as the length of A or not. The job is harder. Now
    consider this version:

       for (i = 0; i<LENGTH(A); ++i)
         ... Do something with A ...

       for (i = 0; i<LENGTH(B); ++i)
         ... Do something with B ...

    Which version will be easier to update? (Hint: this one doesn't need any changes.)


    That is not a problem I would come across in my code. If, for example,
    B and C hang together tightly while A might be different, then I would
    be defining them as :

    enum { N_A = 10, N_BC = 10 };

    int A[N_A];
    int B[N_BC];
    int C[N_BC];

    I wouldn't use the same size constant for inherently separate things
    simply because they happened to have the same number of items!

    And if I later decided that B and C did not belong tightly coupled after
    all, I'd change it to:

    enum { N_A = 10, N_B = 10, N_C = 10 };

    int A[N_A];
    int B[N_B];
    int C[N_C];

    I would expect to have to change the usage of the arrays in conjunction
    with the decoupling change, so changing the later for-loops is needed
    anyway. And if I forgot, the compiler would remind me when the use of
    N_BC fails to compile - forcing me to double-check the usage. I'd
    rather be forced to change the later loops, than have them compile
    silently after such changes, since it is likely that they will need
    other modification.



    Well, here is an extract from sqlite3.c:

    I didn't write sqlite3.c.  I am not responsible for the choices of
    identifiers, or coding style, or macro usage, or anything else in it.
    I am telling you why /I/ don't need or use any kind of "array_size"
    macro.   Other people may write their code differently.


    So in you code, if I see:

       sizeof(expr1)/sizeof(expr2)

    what I am supposed to deduce from that? That it /might/ be an idiom for getting the number of elements in array? Then it might involving looking
    in more detail to see that expr2 is really just expr1[0].


    You are supposed to deduce that I did not write it. I would never use
    such bad spacing conventions :-)

    But if it is not clear and obvious what is going on, then the code is bad.


    On the other hand, if I see:

       ArraySize(expr)

    then you are expressing what your intentions are more clearly.

    That is assuming you know whether ArraySize returns the number of
    elements in the array, or its size in bytes.

    But if you prefer a macro like this to alternatives, that's fine. I can
    only tell you what /I/ prefer to do.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to Bart on Thu Jan 4 16:08:17 2024
    Bart <bc@freeuk.cm> writes:
    On 04/01/2024 01:57, Scott Lurndal wrote:
    Bart <bc@freeuk.cm> writes:
    On 03/01/2024 19:16, David Brown wrote:
    On 03/01/2024 18:14, Bart wrote:

    How do you know whether I'm exaggerating or not? There have surely
    been many millions of people who've programmed in C over the years.


    And how many of them define or use such a macro?  Some, certainly, but >>>> not all.  I can't say I have ever had one in my code as far as I
    remember.  Occasionally I find it convenient to calculate the size of an >>>> existing array, but I'll just write it manually as an expression (like >>>> Scott seems to do).

    So writing the same long identifier twice?

    Yes. Good editors mean you don't need to type it
    twice (ywllp).

    And hoping there's no typo in one?

    If there's a typo, the compiler will note it and I'll
    fix it. But, see above.


    Because sizeof(A)/sizeof(B[0]) would be legal code when both A and
    B are arrays.

    Good thing I don't use single letter identifiers.


    What are you, 5 years old?

    A and B are placeholders for arbitrary identifiers of any length.

    How about if I change my remark to:

    "Because >sizeof(pentium_msr_passthrough_with_knobs_on)/sizeof(k6_msr_passthrough_with_knobs_on)
    would be legal code when both pentium_msr_passthrough_with_knobs_on and >k6_msr_passthrough_with_knobs_on are arrays."

    It's pretty obvious to even the meanest programmer that code is incorrect.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From James Kuyper@21:1/5 to David Brown on Thu Jan 4 12:33:22 2024
    On 1/4/24 06:46, David Brown wrote:
    On 03/01/2024 22:42, Keith Thompson wrote:
    David Brown <david.brown@hesbynett.no> writes:
    On 02/01/2024 21:24, Bart wrote:
    [...]
    The X-macro solution was this, adapted from the first answer here
    (https://stackoverflow.com/questions/6635851/real-world-use-of-x-macros); >>>> assume those functions are in scope:
    -------
    #define STATE_TABLE \
            ENTRY(STATE0, func0) \
            ENTRY(STATE1, func1) \
            ENTRY(STATE2, func2) \
            ENTRY(STATE3, func3) \
    enum
    {
    #define ENTRY(a,b) a,
        STATE_TABLE
    #undef ENTRY
        NUM_STATES
    };
    void* jumptable[NUM_STATES] =
    {
    #define ENTRY(a,b) b,
        STATE_TABLE
    #undef ENTRY
    };
    -------

    (Why did you change the type from "p_func_t" to "void*" ? Was it just
    to annoy myself and other C programmers with a pointless and
    constraint-violating cast of a function pointer to "void*" ? Just add
    a suitable typedef - "typedef void(*p_func_t)(void);" )

    What constraint does it violate? And what cast are you referring to?

    I believe the initialisation follows the requirements for simple
    assignment, and function pointers are not compatible with void*.

    You're right, for initialization "the same type constraints and
    conversions as for simple assignment apply,". This initialization
    violates those constraints. There's two options listed in 6.5.16.1p1
    that might apply: since the object is a pointer, the initializer could
    be a pointer to a compatible type. Since the object is, in particular, a
    void*, it could be a pointer to an object type - but this initializer
    doesn't qualify for either of those cases.
    However, it was the cast you described as "constraint-violating", not
    the initialization, and that's what's incorrect.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From James Kuyper@21:1/5 to Bart on Thu Jan 4 12:51:18 2024
    On 1/4/24 07:37, Bart wrote:
    On 04/01/2024 11:46, David Brown wrote:
    On 03/01/2024 22:42, Keith Thompson wrote:
    David Brown <david.brown@hesbynett.no> writes:
    On 02/01/2024 21:24, Bart wrote:
    [...]
    The X-macro solution was this, adapted from the first answer here
    (https://stackoverflow.com/questions/6635851/real-world-use-of-x-macros); assume those functions are in scope:
    -------
    #define STATE_TABLE \
              ENTRY(STATE0, func0) \
              ENTRY(STATE1, func1) \
              ENTRY(STATE2, func2) \
              ENTRY(STATE3, func3) \
    enum
    {
    #define ENTRY(a,b) a,
          STATE_TABLE
    #undef ENTRY
          NUM_STATES
    };
    void* jumptable[NUM_STATES] =
    {
    #define ENTRY(a,b) b,
          STATE_TABLE
    #undef ENTRY
    };
    -------

    (Why did you change the type from "p_func_t" to "void*" ?  Was it just >>>> to annoy myself and other C programmers with a pointless and
    constraint-violating cast of a function pointer to "void*" ?  Just add >>>> a suitable typedef - "typedef void(*p_func_t)(void);" )

    What constraint does it violate?  And what cast are you referring to?

    I believe the initialisation follows the requirements for simple
    assignment, and function pointers are not compatible with void*.  Bart
    (for reasons understood only by him) uses void* pointers when he wants
    generic function pointers.  The stack overflow link uses "p_func_t",
    which is a function pointer typedef.



    The SO link didn't define p_func_t that I could see. ...

    It also didn't provide declarations for func0, func1, func2, func3, or
    FuncX. From C's rules for initialization, you should have concluded that
    it was a pointer to a function type that is compatible with the types of
    those functions.

    ... I changed it to
    void* to avoid the bother of doing so when testing, and to avoid having
    to include that in my example that would be a distraction.

    You don't consider it distracting that void* makes it a constraint
    violation, rendering the behavior of any program that uses such code undeifined?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to Scott Lurndal on Thu Jan 4 18:35:06 2024
    On 04/01/2024 16:08, Scott Lurndal wrote:
    Bart <bc@freeuk.cm> writes:
    On 04/01/2024 01:57, Scott Lurndal wrote:

    Good thing I don't use single letter identifiers.


    What are you, 5 years old?

    A and B are placeholders for arbitrary identifiers of any length.

    How about if I change my remark to:

    "Because
    sizeof(pentium_msr_passthrough_with_knobs_on)/sizeof(k6_msr_passthrough_with_knobs_on)
    would be legal code when both pentium_msr_passthrough_with_knobs_on and
    k6_msr_passthrough_with_knobs_on are arrays."

    It's pretty obvious to even the meanest programmer that code is incorrect.

    Is it? How do you know? The code is valid:

    --------
    int pentium_msr_passthrough_with_knobs_on;
    int k6_msr_passthrough_with_knobs_on;

    int main(void) { sizeof(pentium_msr_passthrough_with_knobs_on)/sizeof(k6_msr_passthrough_with_knobs_on);
    }
    --------

    This compiles fine. It is valid C. But what does it mean? Given more
    suitable types for those expressions, is it an incorrect attempt to get
    an array length, or something else entirely?

    Using this definition of the first variable:

    int pentium_msr_passthrough_with_knobs_on[13];

    Then it does in fact evaluate its length, although partly by coincidence
    as both base-types are 'int'.

    (Now you're going to point out that it doesn't use that value; yes, we
    know.)

    Here's the same experiment with my language:

    int pentium_msr_passthrough_with_knobs_on
    int k6_msr_passthrough_with_knobs_on

    proc main=
    pentium_msr_passthrough_with_knobs_on.len
    end

    This is more transparent: it's definitely evaluating the length of that variable, but it fails because it is not an array. If I add that '[13]'
    (here it goes at the left), then it works.

    Now look at the basketcase that is C's attempt. C could EASILY have had
    an equivalent op that works like 'size'of'; it chose not to.

    Why is everyone trying to make out that C's approach is superior when it clearly isn't?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to James Kuyper on Thu Jan 4 18:21:38 2024
    On 04/01/2024 17:51, James Kuyper wrote:
    On 1/4/24 07:37, Bart wrote:

    The SO link didn't define p_func_t that I could see. ...

    It also didn't provide declarations for func0, func1, func2, func3, or
    FuncX. From C's rules for initialization, you should have concluded that
    it was a pointer to a function type that is compatible with the types of those functions.

    ... I changed it to
    void* to avoid the bother of doing so when testing, and to avoid having
    to include that in my example that would be a distraction.

    You don't consider it distracting that void* makes it a constraint
    violation, rendering the behavior of any program that uses such code undeifined?

    Only to people obsessed with Standards minutiae.

    I can't remember if I only did a preprocessing test, or added enough (declarations for functions etc) to compile, but here is a version
    within a working program that I've just done:

    -------------
    #include <stdio.h>

    void func0(void){}
    void func1(void){}
    void func2(void){}
    void func3(void){}

    #define STATE_TABLE \
    ENTRY(STATE0, func0) \
    ENTRY(STATE1, func1) \
    ENTRY(STATE2, func2) \
    ENTRY(STATE3, func3) \

    enum
    {
    #define ENTRY(a,b) a,
    STATE_TABLE
    #undef ENTRY
    NUM_STATES
    };

    void* jumptable[NUM_STATES] =
    {
    #define ENTRY(a,b) b,
    STATE_TABLE
    #undef ENTRY
    };

    int main(void) {
    printf("%d\n", NUM_STATES);
    printf("%d %p\n", STATE0, jumptable[STATE0]);
    printf("%d %p\n", STATE1, jumptable[STATE1]);
    printf("%d %p\n", STATE2, jumptable[STATE2]);
    printf("%d %p\n", STATE3, jumptable[STATE3]);
    }
    -------------

    I tested it on 5 compilers and it gave the expected results. It shows
    that the x-macro works, it's just unwieldy.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to David Brown on Thu Jan 4 18:57:26 2024
    On 2024-01-04, David Brown <david.brown@hesbynett.no> wrote:
    However, I think it is all too late for that. It certainly can't be
    added to <stddef.h> or <stdlib.h> - what would you call it without conflicting with existing names in existing code?

    I would call it elems or something, and hide the identifier unless
    a new dialect of C is being used.

    E.g. in the case of gcc, it would not be revealed in <stddef.h> if
    only -std=c23 is in effect; a newer dialect selection would be required.

    The standard headers have been getting new identifiers, right?
    Like many new functions in <math.h>.

    Those are functions which have linkage, which presents an implementation challenge on top of the declarations in the header being easy to hide.

    A size operator for arrays doesn't have linkage to worry about.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Janis Papanagnou@21:1/5 to Bart on Thu Jan 4 20:55:13 2024
    On 04.01.2024 19:35, Bart wrote:

    Now look at the basketcase that is C's attempt. C could EASILY have had
    an equivalent op that works like 'size'of'; it chose not to.

    You're asking for an, say, 'nof_elements(A)', where there's currently
    only a 'sizeof(A)/sizeof(A[0])' existing? - Is that the whole resume
    of this overly bulky subthread?

    Janis

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Keith Thompson on Thu Jan 4 21:59:16 2024
    On 04/01/2024 19:36, Keith Thompson wrote:
    David Brown <david.brown@hesbynett.no> writes:
    On 03/01/2024 22:42, Keith Thompson wrote:
    David Brown <david.brown@hesbynett.no> writes:
    On 02/01/2024 21:24, Bart wrote:
    [...]
    The X-macro solution was this, adapted from the first answer here
    (https://stackoverflow.com/questions/6635851/real-world-use-of-x-macros); assume those functions are in scope:
    -------
    #define STATE_TABLE \
            ENTRY(STATE0, func0) \
            ENTRY(STATE1, func1) \
            ENTRY(STATE2, func2) \
            ENTRY(STATE3, func3) \
    enum
    {
    #define ENTRY(a,b) a,
        STATE_TABLE
    #undef ENTRY
        NUM_STATES
    };
    void* jumptable[NUM_STATES] =
    {
    #define ENTRY(a,b) b,
        STATE_TABLE
    #undef ENTRY
    };
    -------

    (Why did you change the type from "p_func_t" to "void*" ? Was it just >>>> to annoy myself and other C programmers with a pointless and
    constraint-violating cast of a function pointer to "void*" ? Just add >>>> a suitable typedef - "typedef void(*p_func_t)(void);" )
    What constraint does it violate? And what cast are you referring
    to?

    I believe the initialisation follows the requirements for simple
    assignment, and function pointers are not compatible with void*. Bart
    (for reasons understood only by him) uses void* pointers when he wants
    generic function pointers. The stack overflow link uses "p_func_t",
    which is a function pointer typedef.

    Bart's code initializes array elements of type void* with values that
    are presumably of pointer-to-function type. That is indeed a constraint violation (one that, unfortunately, gcc doesn't even warn about with
    default options).

    David, your use of the word "cast" confused me. What's happening in the
    code is an implicit conversion, not a cast (or it would be if that
    particular implicit conversion were defined in the language, or if
    you're using a non-standard C dialect that defines it).


    You are correct there - there was no cast in the code, so my wording was
    wrong.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to Janis Papanagnou on Thu Jan 4 20:17:26 2024
    On 04/01/2024 19:55, Janis Papanagnou wrote:
    On 04.01.2024 19:35, Bart wrote:

    Now look at the basketcase that is C's attempt. C could EASILY have had
    an equivalent op that works like 'size'of'; it chose not to.

    You're asking for an, say, 'nof_elements(A)', where there's currently
    only a 'sizeof(A)/sizeof(A[0])' existing? - Is that the whole resume
    of this overly bulky subthread?

    Yes exactly.

    If you're arguing that it is such a trivial matter that it is hardly
    worth discussing, I can equally say that if it is that trivial, why
    isn't it just built-in? How hard can it be?

    The subthread was actually about the macro system, and this example was
    one I gave where macros make it possible for every man and his dog to
    create their own sloppy, incompatible workaround.

    Every other app seems to provide their own version; I gave three where
    so far we have ArraySize, SDL_arraysize, and ARRAY_SIZE.

    You yourself have suggested 'nof_elements'. You see the problem?

    I'm not actually too bothered about that (I know C is never going to
    have anything that is that sensible), I'm arguing against attitudes like
    yours that it doesn't matter.

    My original point may have been this: suppose C's macro system didn't
    exist. Would that have given enough impetus to whoever was or were
    responsible for the language, to actually do it properly?

    Since my conjecture was that C's macro scheme stopped it evolving normally.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to Bart on Thu Jan 4 21:14:35 2024
    Bart <bc@freeuk.cm> writes:
    On 04/01/2024 16:08, Scott Lurndal wrote:
    Bart <bc@freeuk.cm> writes:
    On 04/01/2024 01:57, Scott Lurndal wrote:

    Good thing I don't use single letter identifiers.


    What are you, 5 years old?

    A and B are placeholders for arbitrary identifiers of any length.

    How about if I change my remark to:

    "Because
    sizeof(pentium_msr_passthrough_with_knobs_on)/sizeof(k6_msr_passthrough_with_knobs_on)
    would be legal code when both pentium_msr_passthrough_with_knobs_on and
    k6_msr_passthrough_with_knobs_on are arrays."

    It's pretty obvious to even the meanest programmer that code is incorrect.

    Is it? How do you know? The code is valid:

    --------
    int pentium_msr_passthrough_with_knobs_on;
    int k6_msr_passthrough_with_knobs_on;

    int main(void) { >sizeof(pentium_msr_passthrough_with_knobs_on)/sizeof(k6_msr_passthrough_with_knobs_on);
    }
    --------

    This compiles fine.

    So what? Any programmer will see that they don't
    match immediately. It is obviously wrong.



    Give it up already. Propose a modification to the
    C language to the standards committee. Bitching about
    it is just noise.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Jim Jackson@21:1/5 to Scott Lurndal on Thu Jan 4 22:07:25 2024
    On 2024-01-04, Scott Lurndal <scott@slp53.sl.home> wrote:
    Bart <bc@freeuk.cm> writes:

    This compiles fine.

    So what? Any programmer will see that they don't
    match immediately. It is obviously wrong.

    Give it up already. Propose a modification to the
    C language to the standards committee. Bitching about
    it is just noise.

    Oh boy and isn't there a lot of it - yawn!!!!!

    I admire the preserverance

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From James Kuyper@21:1/5 to Bart on Thu Jan 4 17:39:11 2024
    On 1/4/24 13:21, Bart wrote:
    On 04/01/2024 17:51, James Kuyper wrote:
    ...
    You don't consider it distracting that void* makes it a constraint
    violation, rendering the behavior of any program that uses such code
    undeifined?

    Only to people obsessed with Standards minutiae.

    These "standards minutae" mean that a conforming implementation of C has
    no obligation to produce any particular kind of behavior for such code.
    If you don't care what kind of behavior your code has, why write a new
    program? Take any existing program - whatever behavior that existing
    program has, is one of the behaviors that an implementation is allowed
    to produce when translating your new program. Since you've declared, by deliberately using such code, that you don't care whether it produces
    such behavior, using the existing program should be fine.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to Jim Jackson on Thu Jan 4 22:48:17 2024
    On 04/01/2024 22:07, Jim Jackson wrote:
    On 2024-01-04, Scott Lurndal <scott@slp53.sl.home> wrote:
    Bart <bc@freeuk.cm> writes:

    This compiles fine.

    So what? Any programmer will see that they don't
    match immediately. It is obviously wrong.

    Give it up already. Propose a modification to the
    C language to the standards committee. Bitching about
    it is just noise.

    Oh boy and isn't there a lot of it - yawn!!!!!

    I admire the preserverance

    For 40 years 99% of my coding has been in a language where you just write:

    A.len

    instead of:

    sizeof(A)/size(A[0])

    So it's no skin off my nose if you prefer to do that or are happy
    reinventing various workarounds.

    I had wondered if things would have been any different without macros to
    create those workarounds.

    It looks like, probably not.

    So, /I/ admire your stoicism in putting up with a badly designed
    language of which this is one tiny aspect.

    (Tell me, if /you/ were to design a new language, would you still
    require that sizeof/sizeof business? No? Thought not!)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Bart on Thu Jan 4 23:14:59 2024
    On 2024-01-04, Bart <bc@freeuk.cm> wrote:
    On 04/01/2024 22:07, Jim Jackson wrote:
    On 2024-01-04, Scott Lurndal <scott@slp53.sl.home> wrote:
    Bart <bc@freeuk.cm> writes:

    This compiles fine.

    So what? Any programmer will see that they don't
    match immediately. It is obviously wrong.

    Give it up already. Propose a modification to the
    C language to the standards committee. Bitching about
    it is just noise.

    Oh boy and isn't there a lot of it - yawn!!!!!

    I admire the preserverance

    For 40 years 99% of my coding has been in a language where you just write:

    A.len

    instead of:

    sizeof(A)/size(A[0])

    But I suspect that language can pass the array to a function, and
    that function also do A.len, right?

    Even when we have a macro or language extension that calculates sizeof(A)/sizeof(*A), it is only usable for arrays that were not passed
    by pointer.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to Bart on Thu Jan 4 23:25:08 2024
    On Thu, 4 Jan 2024 22:48:17 +0000, Bart wrote:

    For 40 years 99% of my coding has been in a language where you just
    write:

    A.len

    Better still:

    len(A)

    which is just a generic wrapper around

    A.__len__()

    so it will work with your custom object types as well, all they have to do
    is define a method with that name.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to Kaz Kylheku on Thu Jan 4 23:48:02 2024
    On 04/01/2024 23:14, Kaz Kylheku wrote:
    On 2024-01-04, Bart <bc@freeuk.cm> wrote:
    On 04/01/2024 22:07, Jim Jackson wrote:

    I admire the preserverance

    For 40 years 99% of my coding has been in a language where you just write: >>
    A.len

    instead of:

    sizeof(A)/size(A[0])

    But I suspect that language can pass the array to a function, and
    that function also do A.len, right?

    That depends. If the length is part of the type like in these 3
    examples, then yes:

    proc F1([5]int A) = println A.len end # by 'value'

    proc F2(ref[5]int P) = println P.len end # by pointer**

    proc F3([5]int &A) = println A.len end # by reference

    (** It auto-derefs.) But it can only be called with an array of that
    exact size:

    [5]int A
    [7]int B

    F1(A)
    F2(&A)
    F3(A)

    I can use A, but not B.

    However most arrays passed to functions are unbounded (use [] not [N]),
    since it is most useful to allow arrays of arbitrary length (this was
    the problem with the first Pascals) then you will need a separate length parameter.

    Attempting A.len when A has type []int will give 0.

    Alternately, slices can be used which carry their length:

    proc F4(slice[]int S) = println S.len end

    Now I can call it with both A and B, and without needing a separate length:

    F4(A)
    F4(B)

    Or with a slice of an array:

    F4(B[3..5]) # displays 3, but the bounds are 1..3

    Even when we have a macro or language extension that calculates sizeof(A)/sizeof(*A), it is only usable for arrays that were not passed
    by pointer.

    The test lengthof(A) implementation which I did in 20 minutes the other
    day, doesn't report an error when A is not an array; it just ignores it
    and does sizeof(). So it will take bit longer to make it robust.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to Lawrence D'Oliveiro on Fri Jan 5 01:53:00 2024
    On 04/01/2024 23:25, Lawrence D'Oliveiro wrote:
    On Thu, 4 Jan 2024 22:48:17 +0000, Bart wrote:

    For 40 years 99% of my coding has been in a language where you just
    write:

    A.len

    Better still:

    len(A)

    which is just a generic wrapper around

    A.__len__()

    so it will work with your custom object types as well, all they have to do
    is define a method with that name.

    If a language has a built-in like A.len or len(A) or A'len and so on,
    then it can choose to allow overloading for that operator. The syntax
    chosen doesn't matter.

    It doesn't need to take a heavy-handed approach like C++ or Python.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to Bart on Fri Jan 5 04:53:46 2024
    On Fri, 5 Jan 2024 01:53:00 +0000, Bart wrote:

    If a language has a built-in like A.len or len(A) or A'len and so on,
    then it can choose to allow overloading for that operator. The syntax
    chosen doesn't matter.

    The operation still has to be defined for the relevant type, in order for
    the construct to work.

    It doesn't need to take a heavy-handed approach like C++ or Python.

    Given that the entire Python language definition is a small fraction of
    that of, say, Java, which does not allow operator overloading on user-
    defined types, I would hardly describe it as “heavy-handed”.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Janis Papanagnou@21:1/5 to Bart on Fri Jan 5 10:03:20 2024
    On 04.01.2024 21:17, Bart wrote:
    On 04/01/2024 19:55, Janis Papanagnou wrote:
    On 04.01.2024 19:35, Bart wrote:

    Now look at the basketcase that is C's attempt. C could EASILY have had
    an equivalent op that works like 'size'of'; it chose not to.

    You're asking for an, say, 'nof_elements(A)', where there's currently
    only a 'sizeof(A)/sizeof(A[0])' existing? - Is that the whole resume
    of this overly bulky subthread?

    Yes exactly.

    Fine.


    If you're arguing that it is such a trivial matter that it is hardly
    worth discussing, I can equally say that if it is that trivial, why
    isn't it just built-in? How hard can it be?

    Why so paranoid defensive? :-) I wasn't arguing (not yet), I was asking
    two questions; one was what appears to be the essence of thousands lines
    of posts with a lot repetitions. (And the other was on the posting meta
    level, with the implicit hope to come to an end since everything seems
    to have been said on that theme.) Also the two questions you have added
    here (seemingly as virtual counter-argument) had already been addressed.


    The subthread was actually about the macro system, and this example was
    one I gave where macros make it possible for every man and his dog to
    create their own sloppy, incompatible workaround.

    I'm sure I already read arguments on that many times in this thread. My
    view is that all the programming areas create (also sloppy) workarounds
    for features that are not part of the "language of choice" (here: "C").

    Specifically when discussing C there's IMO countless examples of what's missing, and what's wrongly designed, and whatnot. - Thus I am merely astonished that you fastened on that bit so vigorously, and also with
    so much text and repetitions.


    Every other app seems to provide their own version; I gave three where
    so far we have ArraySize, SDL_arraysize, and ARRAY_SIZE.

    You yourself have suggested 'nof_elements'. You see the problem?

    Not the least. The sizeof/sizeof I consider as an idiomatic approach
    to achieve for a special detail of the language a practical solution.

    And the naming of entities in programs through identifiers will always
    be individual, while in practice typically with semantically sensible
    naming. I really don't care if programmers call her entities X or Y.
    Only if a language designer (or committee) decides to add a feature as
    a standard part of the language it becomes important.

    There's (IMO) so much wrong with arrays in C that the discussed special
    case (which is also not generally applicable as pointed out repeatedly)
    is of little interest (for me; YMMV).


    I'm not actually too bothered about that (I know C is never going to
    have anything that is that sensible), I'm arguing against attitudes like yours that it doesn't matter.

    Yet - despite dozens of posts with thousands of lines - I haven't seen
    any convincing argument that it would matter.


    My original point may have been this: suppose C's macro system didn't
    exist. Would that have given enough impetus to whoever was or were responsible for the language, to actually do it properly?

    That's a noteworthy statement since formulated as thought experiment.
    But the hypothetical precondition shows that it's really not worth a
    discussion in the first place.

    Mind, I started with K&R C in the 1980's, at a time where much more sophisticated languages had already been present; the formally much
    more appealing Algol(68), the concepts-outstanding Simula(67), even
    Pascal (that you recently also mentioned) was very appealing due to
    its simplicity, efficiency, expressiveness and still clearness, just
    to name a few. What I was missing in C - one major issue was the OO capabilities that I had got used with Simula - I later found in C++,
    alongside with the efficiency of C inherited, but also with all the deficiencies of C; but for some features you've got alternatives
    (e.g. sophisticated STL containers replacing these (IMO annoying)
    low-level "C arrays" (with all its inhomogeneities), and providing
    yet much more that I appreciated).


    Since my conjecture was that C's macro scheme stopped it evolving normally.

    Above we identified a "resume" of the previous posts. Another one
    shall finalize this post; no one is forced to use C. - Myself I'm
    using it occasionally where appropriate. But if I think something
    is in my way - and may it be only as unimportant as 'A.length' -
    I'll just switch to another language (with richer feature set, or
    with a more coherent design). It certainly wouldn't occur to me to
    ask for features that I already find in many contemporary language
    I can choose from.

    I'm confident your mileage may vary, and we can agree to disagree.
    :-)

    Janis

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Bart on Fri Jan 5 15:05:59 2024
    On 05/01/2024 02:53, Bart wrote:
    On 04/01/2024 23:25, Lawrence D'Oliveiro wrote:
    On Thu, 4 Jan 2024 22:48:17 +0000, Bart wrote:

    For 40 years 99% of my coding has been in a language where you just
    write:

          A.len

    Better still:

         len(A)

    which is just a generic wrapper around

         A.__len__()

    so it will work with your custom object types as well, all they have
    to do
    is define a method with that name.

    If a language has a built-in like A.len or len(A) or A'len and so on,
    then it can choose to allow overloading for that operator. The syntax
    chosen doesn't matter.

    It doesn't need to take a heavy-handed approach like C++ or Python.


    One option for a useful array length operator/function/macro is a simple
    and limited feature that works for arrays of known size and gives a hard (compile-time) error when the size is not known. The one you have in
    your own language covers most of that, except for the insanity of
    evaluating to 0 when given a pointer/reference to an "unbounded" array.
    The "ARRAY_SIZE" macro in Linux is almost perfect - the only
    disadvantage is that it relies on compiler extensions:

    #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]) +
    __must_be_array(arr))

    #define __same_type(a, b) __builtin_types_compatible_p(typeof(a),
    typeof(b)))

    #define __must_be_array(a) BUILD_BUG_ON_ZERO(__same_type((a), &(a)[0]))

    #define BUILD_BUG_ON_ZERO(e) (sizeof(struct { int:-!!(e); }))


    The other option is a general mechanism for "length" as an operator/function/macro that can be applied to a wide range of types - including user-made types, assuming the language supports user-defined
    types. Whether that is done by function overloads, custom methods,
    etc., is a design choice.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to Bart on Fri Jan 5 15:54:58 2024
    Bart <bc@freeuk.cm> writes:
    On 04/01/2024 23:25, Lawrence D'Oliveiro wrote:
    On Thu, 4 Jan 2024 22:48:17 +0000, Bart wrote:

    For 40 years 99% of my coding has been in a language where you just
    write:

    A.len

    Better still:

    len(A)

    which is just a generic wrapper around

    A.__len__()

    so it will work with your custom object types as well, all they have to do >> is define a method with that name.

    If a language has a built-in

    The C language doesn't have such a built-in. Any discussion that
    assumes it does, or will, is pointless.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to David Brown on Fri Jan 5 16:29:24 2024
    On 05/01/2024 14:05, David Brown wrote:
    On 05/01/2024 02:53, Bart wrote:
    On 04/01/2024 23:25, Lawrence D'Oliveiro wrote:
    On Thu, 4 Jan 2024 22:48:17 +0000, Bart wrote:

    For 40 years 99% of my coding has been in a language where you just
    write:

          A.len

    Better still:

         len(A)

    which is just a generic wrapper around

         A.__len__()

    so it will work with your custom object types as well, all they have
    to do
    is define a method with that name.

    If a language has a built-in like A.len or len(A) or A'len and so on,
    then it can choose to allow overloading for that operator. The syntax
    chosen doesn't matter.

    It doesn't need to take a heavy-handed approach like C++ or Python.


    One option for a useful array length operator/function/macro is a simple
    and limited feature that works for arrays of known size and gives a hard (compile-time) error when the size is not known.  The one you have in
    your own language covers most of that, except for the insanity of
    evaluating to 0 when given a pointer/reference to an "unbounded" array.
    The "ARRAY_SIZE" macro in Linux is almost perfect - the only
    disadvantage is that it relies on compiler extensions:

    #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]) + __must_be_array(arr))

    #define __same_type(a, b) __builtin_types_compatible_p(typeof(a),
    typeof(b)))

    #define __must_be_array(a)    BUILD_BUG_ON_ZERO(__same_type((a), &(a)[0]))

    #define BUILD_BUG_ON_ZERO(e)   (sizeof(struct { int:-!!(e); }))

    At least Linux recognises some of the problems of the
    sizeof(A)/sizeof(A[10]) idiom.

    Which is more than can said for Scott, who can't see any possible issues
    at all. Apparently writing B for one of those As is impossible:

    "Any programmer will see that they don't match immediately. It is
    obviously wrong."

    No matter long or short or similar they are.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to Scott Lurndal on Fri Jan 5 16:23:30 2024
    On 05/01/2024 15:54, Scott Lurndal wrote:
    Bart <bc@freeuk.cm> writes:
    On 04/01/2024 23:25, Lawrence D'Oliveiro wrote:
    On Thu, 4 Jan 2024 22:48:17 +0000, Bart wrote:

    For 40 years 99% of my coding has been in a language where you just
    write:

    A.len

    Better still:

    len(A)

    which is just a generic wrapper around

    A.__len__()

    so it will work with your custom object types as well, all they have to do >>> is define a method with that name.

    If a language has a built-in

    The C language doesn't have such a built-in. Any discussion that
    assumes it does, or will, is pointless.

    It could be an extension.

    I assume none of your code uses extensions to standard C? Not even
    inline assembly?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Keith Thompson on Fri Jan 5 17:34:23 2024
    On 05/01/2024 16:58, Keith Thompson wrote:
    David Brown <david.brown@hesbynett.no> writes:
    [...]
    One option for a useful array length operator/function/macro is a
    simple and limited feature that works for arrays of known size and
    gives a hard (compile-time) error when the size is not known. The one
    you have in your own language covers most of that, except for the
    insanity of evaluating to 0 when given a pointer/reference to an
    "unbounded" array. The "ARRAY_SIZE" macro in Linux is almost perfect -
    the only disadvantage is that it relies on compiler extensions:

    #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]) +
    __must_be_array(arr))

    #define __same_type(a, b) __builtin_types_compatible_p(typeof(a),
    typeof(b)))

    #define __must_be_array(a) BUILD_BUG_ON_ZERO(__same_type((a), &(a)[0]))

    #define BUILD_BUG_ON_ZERO(e) (sizeof(struct { int:-!!(e); }))
    [...]

    When you wrote "in Linux", I wondered if you were being imprecise, but
    in fact that code is in the Linux kernel.


    I am perhaps being /too/ precise - "Linux" is technically the kernel,
    rather than the rest of the system (including user C code). But yes, it
    is in the headers for use when compiling the kernel, rather than for
    user code compiled for Linux systems.

    And yes, you can copy them (or variations of them) to your own code.

    That means the macros aren't directly available to normal C code, but
    you can always copy their definitions (*if* you're using a compiler
    that supports the __builtin_types_compatible_p extension).


    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Keith Thompson on Fri Jan 5 18:42:58 2024
    On 2024-01-05, Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:
    When you wrote "in Linux", I wondered if you were being imprecise, but
    in fact that code is in the Linux kernel.

    That means the macros aren't directly available to normal C code, but
    you can always copy their definitions (*if* you're using a compiler
    that supports the __builtin_types_compatible_p extension).

    You can always copy their definitions, if you're using a compiler
    that doesn't arbitrarily define __GNUC__ without providing the
    associated behaviors:

    #ifdef __GNU__

    // define array_size in the Linux kernel way

    #else

    #define arrray_size(x) (sizeof (x)/sizeof *(x))

    #endif

    If you regularly build the code with a compiler that provides GNU
    extensions (like as part of your CI), you're covered, even if you're
    going to production with something else.

    I use C++ this way in C projects; I have some macro features that
    provide extra checks under C++. I get the benefit even if I just
    compile the code as C++ only once before every release.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Janis Papanagnou on Fri Jan 5 18:37:34 2024
    On 2024-01-05, Janis Papanagnou <janis_papanagnou+ng@hotmail.com> wrote:
    Why so paranoid defensive? :-)

    For consistency with lengthy past history.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to Bart on Fri Jan 5 18:44:29 2024
    Bart <bc@freeuk.cm> writes:
    On 05/01/2024 14:05, David Brown wrote:
    On 05/01/2024 02:53, Bart wrote:
    On 04/01/2024 23:25, Lawrence D'Oliveiro wrote:
    On Thu, 4 Jan 2024 22:48:17 +0000, Bart wrote:

    For 40 years 99% of my coding has been in a language where you just
    write:

          A.len

    Better still:

         len(A)

    which is just a generic wrapper around

         A.__len__()

    so it will work with your custom object types as well, all they have
    to do
    is define a method with that name.

    If a language has a built-in like A.len or len(A) or A'len and so on,
    then it can choose to allow overloading for that operator. The syntax
    chosen doesn't matter.

    It doesn't need to take a heavy-handed approach like C++ or Python.


    One option for a useful array length operator/function/macro is a simple
    and limited feature that works for arrays of known size and gives a hard
    (compile-time) error when the size is not known.  The one you have in
    your own language covers most of that, except for the insanity of
    evaluating to 0 when given a pointer/reference to an "unbounded" array.
    The "ARRAY_SIZE" macro in Linux is almost perfect - the only
    disadvantage is that it relies on compiler extensions:

    #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]) +
    __must_be_array(arr))

    #define __same_type(a, b) __builtin_types_compatible_p(typeof(a),
    typeof(b)))

    #define __must_be_array(a)    BUILD_BUG_ON_ZERO(__same_type((a), &(a)[0]))

    #define BUILD_BUG_ON_ZERO(e)   (sizeof(struct { int:-!!(e); }))

    At least Linux recognises some of the problems of the
    sizeof(A)/sizeof(A[10]) idiom.

    Which is more than can said for Scott, who can't see any possible issues
    at all. Apparently writing B for one of those As is impossible:

    Even David has claimed to use the sizeof/sizeof[0] idiom.


    "Any programmer will see that they don't match immediately. It is
    obviously wrong."

    No matter long or short or similar they are.

    There are thousands of ways programmers can introduce
    bugs far more easily which are significantly harder
    to find and fix; and yes, I expect programmers to
    fully understand the code they're writing and test
    their code to find bugs which they then will subsequently
    fix.

    Do lookup the word 'strawman' as it relates to discourse.

    The use of that idiom is _not a problem_.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to Scott Lurndal on Fri Jan 5 19:33:48 2024
    On 05/01/2024 18:44, Scott Lurndal wrote:
    Bart <bc@freeuk.cm> writes:
    On 05/01/2024 14:05, David Brown wrote:

    One option for a useful array length operator/function/macro is a simple >>> and limited feature that works for arrays of known size and gives a hard >>> (compile-time) error when the size is not known.  The one you have in
    your own language covers most of that, except for the insanity of
    evaluating to 0 when given a pointer/reference to an "unbounded" array.
    The "ARRAY_SIZE" macro in Linux is almost perfect - the only
    disadvantage is that it relies on compiler extensions:

    #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]) +
    __must_be_array(arr))

    #define __same_type(a, b) __builtin_types_compatible_p(typeof(a),
    typeof(b)))

    #define __must_be_array(a)    BUILD_BUG_ON_ZERO(__same_type((a), &(a)[0]))

    #define BUILD_BUG_ON_ZERO(e)   (sizeof(struct { int:-!!(e); }))

    At least Linux recognises some of the problems of the
    sizeof(A)/sizeof(A[10]) idiom.

    Which is more than can said for Scott, who can't see any possible issues
    at all. Apparently writing B for one of those As is impossible:

    Even David has claimed to use the sizeof/sizeof[0] idiom.


    "Any programmer will see that they don't match immediately. It is
    obviously wrong."

    No matter long or short or similar they are.

    There are thousands of ways programmers can introduce
    bugs far more easily which are significantly harder
    to find and fix; and yes, I expect programmers to
    fully understand the code they're writing and test
    their code to find bugs which they then will subsequently
    fix.

    Do lookup the word 'strawman' as it relates to discourse.

    And yet, despite it having no problems according to you, people still
    feel the need to create macros like ARRAY_SIZE.

    So I'm puzzled. Why do they do that? What imaginary problem (according
    to you) do they solve?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to Bart on Fri Jan 5 20:06:26 2024
    Bart <bc@freeuk.cm> writes:
    On 05/01/2024 18:44, Scott Lurndal wrote:

    Do lookup the word 'strawman' as it relates to discourse.

    And yet, despite it having no problems according to you, people still
    feel the need to create macros like ARRAY_SIZE.

    So I'm puzzled. Why do they do that? What imaginary problem (according
    to you) do they solve?

    Why does anyone do anything?

    It's possible they believe it is fewer characters to
    to write. Some may find it more readable. Some may
    find it less readable.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to Janis Papanagnou on Fri Jan 5 19:25:02 2024
    On 05/01/2024 09:03, Janis Papanagnou wrote:
    On 04.01.2024 21:17, Bart wrote:

    Specifically when discussing C there's IMO countless examples of what's missing, and what's wrongly designed, and whatnot. - Thus I am merely astonished that you fastened on that bit so vigorously, and also with
    so much text and repetitions.

    Within one post or multiple? If I'm repeating myself it is either
    because others do, or I'm being attacked on multiple fronts.


    There's (IMO) so much wrong with arrays in C that the discussed special
    case (which is also not generally applicable as pointed out repeatedly)
    is of little interest (for me; YMMV).

    There is a huge amount wrong with C at this level of language, which is
    about par with my own.

    Many will just move on from C to modern alternatives, but they are
    generally bigger, more complex, and cumbersome.

    I like systems coding at this level. So as a long time user and
    developer of my alternate language, I can give talk about 100-200
    different aspects that I have made better. Most are microfeatures, but
    some are big ones.

    Mind, I started with K&R C in the 1980's, at a time where much more sophisticated languages had already been present; the formally much
    more appealing Algol(68), the concepts-outstanding Simula(67), even
    Pascal

    Mine has a syntax based largely on Algol and Pascal, notably Algol68.

    Algol68 has LWB a and UPB a, I turned those into a.lwb and a.upb, and
    added a.len. But my language was far simpler semantically.

    Above we identified a "resume" of the previous posts. Another one
    shall finalize this post; no one is forced to use C.

    Neither was I, until 1992, then I needed to start interacting with
    libraries defined via a C API. I was supposed to switch to using C (and
    get a proper job using that), but I couldn't do it.

    I realised my 'temporary' in-house language that I'd hacked together in
    my spare time was actually better.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Bart on Fri Jan 5 22:43:25 2024
    On 2024-01-05, Bart <bc@freeuk.cm> wrote:
    In my static language, '.len' works on two types (arrays and slices; for
    the latter it happens at runtime).

    Yeah, but some widely-used languages also solve this.

    Broadly speaking beyond the context of this array issue, those are your competition, not C.

    It's like you're dwelling in a room full of retards in order to feel
    smart.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Ben Bacarisse@21:1/5 to Bart on Fri Jan 5 23:02:55 2024
    Bart <bc@freeuk.cm> writes:

    In C, sizeof() is usually compile-time, except when used on VLAs, and here, as usual for VLAs, it is a lot more complicated than you'd think:

    --------
    #include <stdio.h>
    #include <stdlib.h>

    int main(void) {
    int n=rand()+1;
    typedef char (**T)[n];
    n=-777;

    T x;

    printf("%zu\n", sizeof(**x));
    }
    --------

    This actually displays 42 (really!). 'x' is a pointer (to a pointer to array). There is no actual array allocated.

    More to the point, x is uninitialised and thus evaluating sizeof **x is undefined behaviour. gcc has some flags that could have helped you to
    find that out.

    But it needs somewhere to store
    the size of the type which is the target of those pointers.

    I was hoping to do it without creating `x`, but I didn't know how.

    You don't need to create an array, nor a pointer to one, nor a pointer
    to a pointer to one, in order to show that the compiler must record the
    size of a variably modified array type at runtime:

    int n = rand() + 1;
    typedef char T[n];
    printf("%zu\n", sizeof (T));
    n = 42;
    printf("%zu\n", sizeof (T));

    n is something of a red herring here. If you write the expression in
    the typedef itself you might have been less surprised:

    typedef char T[rand() + 1]; // the size obviously needs to be stored

    --
    Ben.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to David Brown on Fri Jan 5 22:19:46 2024
    On 05/01/2024 14:05, David Brown wrote:
    On 05/01/2024 02:53, Bart wrote:

    It doesn't need to take a heavy-handed approach like C++ or Python.


    One option for a useful array length operator/function/macro is a simple
    and limited feature that works for arrays of known size and gives a hard (compile-time) error when the size is not known.  The one you have in
    your own language covers most of that, except for the insanity of
    evaluating to 0 when given a pointer/reference to an "unbounded" array.

    It gives zero because that is actually the compile-time type of the
    array. But it is low priority because it is never used that way.

    I mentioned elsewhere another mechanism (slices) for dealing better with
    arrays passed to functions.

    C as it is now has worse problems than this with array handling.


    The "ARRAY_SIZE" macro in Linux is almost perfect - the only
    disadvantage is that it relies on compiler extensions:

    #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]) + __must_be_array(arr))

    #define __same_type(a, b) __builtin_types_compatible_p(typeof(a),
    typeof(b)))

    #define __must_be_array(a)    BUILD_BUG_ON_ZERO(__same_type((a), &(a)[0]))

    #define BUILD_BUG_ON_ZERO(e)   (sizeof(struct { int:-!!(e); }))


    The other option is a general mechanism for "length" as an operator/function/macro that can be applied to a wide range of types - including user-made types, assuming the language supports user-defined types.  Whether that is done by function overloads, custom methods,
    etc., is a design choice.


    In my static language, '.len' works on two types (arrays and slices; for
    the latter it happens at runtime).

    In the dynamic one, '.len' works on 10 different types (list, array,
    string, bit-array, bit-set, decimal, record, struct, range). That's
    already enough to be dealing with. (Record types can have methods to
    return such info.)

    In C, sizeof() is usually compile-time, except when used on VLAs, and
    here, as usual for VLAs, it is a lot more complicated than you'd think:

    --------
    #include <stdio.h>
    #include <stdlib.h>

    int main(void) {
    int n=rand()+1;
    typedef char (**T)[n];
    n=-777;

    T x;

    printf("%zu\n", sizeof(**x));
    }
    --------

    This actually displays 42 (really!). 'x' is a pointer (to a pointer to
    array). There is no actual array allocated. But it needs somewhere to
    store the size of the type which is the target of those pointers.

    I was hoping to do it without creating `x`, but I didn't know how. (I
    couldn't do that in mine either; I'd need a way to get the target of a
    pointer type T.)

    Slices are child's play by comparison; they are basically:

    struct {T* ptr; u64 length;};

    -------
    c:\c>gcc c.c && a
    42

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to Keith Thompson on Sat Jan 6 01:09:05 2024
    On 05/01/2024 22:50, Keith Thompson wrote:
    Bart <bc@freeuk.cm> writes:
    On 05/01/2024 18:44, Scott Lurndal wrote:
    [...]
    There are thousands of ways programmers can introduce
    bugs far more easily which are significantly harder
    to find and fix; and yes, I expect programmers to
    fully understand the code they're writing and test
    their code to find bugs which they then will subsequently
    fix.
    Do lookup the word 'strawman' as it relates to discourse.

    And yet, despite it having no problems according to you, people still
    feel the need to create macros like ARRAY_SIZE.

    Yes. That's how they use the idiom.

    So I'm puzzled. Why do they do that? What imaginary problem (according
    to you) do they solve?

    I don't know what you're puzzled about.

    I'm puzzled because people like Scott are suggesting those macros are a
    waste of time, and yet you find them in big, important software.

    Actually, I'm not really puzzled, I know he's just being contrary to
    wind me up, and I was trying to catch him out.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to Ben Bacarisse on Sat Jan 6 01:45:31 2024
    On 05/01/2024 23:02, Ben Bacarisse wrote:
    Bart <bc@freeuk.cm> writes:

    In C, sizeof() is usually compile-time, except when used on VLAs,
    and here,
    as usual for VLAs, it is a lot more complicated than you'd think:

    --------
    #include <stdio.h>
    #include <stdlib.h>

    int main(void) {
    int n=rand()+1;
    typedef char (**T)[n];
    n=-777;

    T x;

    printf("%zu\n", sizeof(**x));
    }
    --------

    This actually displays 42 (really!). 'x' is a pointer (to a pointer to
    array). There is no actual array allocated.

    More to the point, x is uninitialised and thus evaluating sizeof **x is undefined behaviour. gcc has some flags that could have helped you to
    find that out.

    I actually don't know if it's evaluating it or not. Why on earth should
    it? If I change the program to print the original 'n', it is 42 too. So
    it doesn't need to evaluate **x to get that dimension.

    (When I implement sizeof(X), it works out the /type/ of X, at
    compile-time. That should be the case with VLA types too, except the
    type will involve that runtime element which is nothing to do with the
    value of X or **x.)

    But it needs somewhere to store
    the size of the type which is the target of those pointers.

    I was hoping to do it without creating `x`, but I didn't know how.

    You don't need to create an array, nor a pointer to one, nor a pointer
    to a pointer to one, in order to show that the compiler must record the
    size of a variably modified array type at runtime:

    int n = rand() + 1;
    typedef char T[n];
    printf("%zu\n", sizeof (T));
    n = 42;
    printf("%zu\n", sizeof (T));

    n is something of a red herring here. If you write the expression in
    the typedef itself you might have been less surprised:

    typedef char T[rand() + 1]; // the size obviously needs to be stored


    Yes, this why I set n to -777 to show that it has to capture the value
    of n inside the typedef.

    But in your example, the size of T /is/ the array dimension. In mine, T
    is a pointer; how to double check what the value of n was:

    int n=rand()+1;
    typedef char (**T)[n];
    n=-777;

    The original value of n is lost. So how to print the dimension of the
    array portion of that type, without creating an instance of the type?

    The point of the example was to so show that mixing sizeof and VLAs can
    get hairy.

    Both gcc and tcc (and clang) seemed to work with my example.

    lccwin32 crashed. When I hardcoded n to 4 or 5 (but still a VLA), then
    the array size was reported as either 4 or 5, except by lccwin32 which
    always said 8.

    Initialising x to an actual pointer to pointer to array made no difference.

    This compiler might be buggy, but it shows that VLAs can be difficult to
    get right. My compiler doesn't even try.

    I think my original point might have been that 'slices' are a modest
    advance over plain arrays, which have to carry a length parameter. very
    easy to understand and not hard to implement.

    But C decided to skip such a feaure, and leap straight to the much harder-to-implement VLAs.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to Kaz Kylheku on Sat Jan 6 02:04:11 2024
    On 05/01/2024 22:43, Kaz Kylheku wrote:
    On 2024-01-05, Bart <bc@freeuk.cm> wrote:
    In my static language, '.len' works on two types (arrays and slices; for
    the latter it happens at runtime).

    Yeah, but some widely-used languages also solve this.

    Broadly speaking beyond the context of this array issue, those are your competition, not C.

    What, languages like Rust, Zig, Java, C#?

    I don't think so. Even lesser known ones like C3 are pretty huge. The
    nearest in scale to what I'm doing might be Hare.

    C can be implemented [on and for x64] in under 200KB. Mine in something
    over 300KB (but it is multipass; tcc is single pass). Most of those
    others are 100MB and up.

    I deliberately keep my systems language simple and low level, yet there
    are lots of modest enhancements over C too.

    It's like you're dwelling in a room full of retards in order to feel
    smart.

    I would have let my language die years ago. And yet, C is still
    tremendously popular and widely used. It's something that people feel
    they can master and be in control of, rather than be intimidated by.

    But it's also full of terrible design choices.

    So if there's a place for such a systems language, then mine is worth maintaining too. The 'competitor' then is C.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From tTh@21:1/5 to Bart on Sat Jan 6 05:33:10 2024
    On 1/4/24 13:15, Bart wrote:

       for (i = 0; i<N; ++N)
         ... Do something with A ...


    And do nothing for the first element, big win.


    --
    +---------------------------------------------------------------------+
    | https://tube.interhacker.space/a/tth/video-channels | +---------------------------------------------------------------------+

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Kaz Kylheku on Sat Jan 6 08:39:37 2024
    On 05/01/2024 19:42, Kaz Kylheku wrote:
    On 2024-01-05, Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:
    When you wrote "in Linux", I wondered if you were being imprecise, but
    in fact that code is in the Linux kernel.

    That means the macros aren't directly available to normal C code, but
    you can always copy their definitions (*if* you're using a compiler
    that supports the __builtin_types_compatible_p extension).

    You can always copy their definitions, if you're using a compiler
    that doesn't arbitrarily define __GNUC__ without providing the
    associated behaviors:

    #ifdef __GNU__

    // define array_size in the Linux kernel way

    #else

    #define arrray_size(x) (sizeof (x)/sizeof *(x))

    #endif

    If you regularly build the code with a compiler that provides GNU
    extensions (like as part of your CI), you're covered, even if you're
    going to production with something else.

    I use C++ this way in C projects; I have some macro features that
    provide extra checks under C++. I get the benefit even if I just
    compile the code as C++ only once before every release.


    That is a good tactic if your code needs to be used with compilers that
    don't have such features for static checking (perhaps you are releasing
    your source code, and can't influence the tools or settings users use).
    In a way, you are using gcc (or C++) as a linter.

    I've done this myself when the actual target compiler had little in the
    way of static checking - I ran gcc in parallel, for a different target
    (the generated output was ignored), but with lots of warning flags that
    the real compiler did not support.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Bart on Sat Jan 6 10:02:28 2024
    On 05/01/2024 20:33, Bart wrote:


    And yet, despite it having no problems according to you, people still
    feel the need to create macros like ARRAY_SIZE.

    So I'm puzzled. Why do they do that? What imaginary problem (according
    to you) do they solve?


    People are different, and like to do things different ways. I would be
    a boring world if everyone solved the same problems or challenges
    identically! Some people put the milk in their cup before their tea,
    and some people put the tea in before the milk. Some drink their tea
    without milk, some drink just the milk, some vary their habits, some
    care a great deal and some don't care at all. Would you expect a tea-before-milk fanatic to justify how a milk-before-tea fanatic thinks?

    This is no different. Some people like to use a macro. Some people
    like to write it out explicitly in the code. Some people like to use
    other methods, such as referring to the constant used to define the
    array. Some people like to use a language that provides an operator for
    the task, or where they can define their own functions for the job.

    Has your use of your own personal languages and tools over the years
    made you so insular that you can't understand that different people have different preferences?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Bart on Sat Jan 6 10:09:55 2024
    On 05/01/2024 23:19, Bart wrote:
    On 05/01/2024 14:05, David Brown wrote:
    On 05/01/2024 02:53, Bart wrote:

    It doesn't need to take a heavy-handed approach like C++ or Python.


    One option for a useful array length operator/function/macro is a
    simple and limited feature that works for arrays of known size and
    gives a hard (compile-time) error when the size is not known.  The one
    you have in your own language covers most of that, except for the
    insanity of evaluating to 0 when given a pointer/reference to an
    "unbounded" array.

    It gives zero because that is actually the compile-time type of the
    array. But it is low priority because it is never used that way.


    That's the kind of potentially confusing short-cut you can make when
    only you ever use the language. It is also fine for when people make
    their own array_length macros for use in their own code. When you know
    all the details of the operator/macro/function, and you are the only one
    using it, it's okay if the specification is weird and unhelpful for some
    cases. It's a different matter entirely if you are making something
    that other people will use.


    In C, sizeof() is usually compile-time, except when used on VLAs, and
    here, as usual for VLAs, it is a lot more complicated than you'd think:


    No it is not. I don't think "garbage in, garbage out" is a complicated concept. You can write pathological shit in any language and get
    meaningless results out - it has no bearing on anything in real code.
    (It can be fun to play around with, but it does not mean there is a
    problem with the language feature.)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to David Brown on Sat Jan 6 10:27:32 2024
    On 06/01/2024 09:09, David Brown wrote:
    On 05/01/2024 23:19, Bart wrote:
    On 05/01/2024 14:05, David Brown wrote:
    On 05/01/2024 02:53, Bart wrote:

    It doesn't need to take a heavy-handed approach like C++ or Python.


    One option for a useful array length operator/function/macro is a
    simple and limited feature that works for arrays of known size and
    gives a hard (compile-time) error when the size is not known.  The
    one you have in your own language covers most of that, except for the
    insanity of evaluating to 0 when given a pointer/reference to an
    "unbounded" array.

    It gives zero because that is actually the compile-time type of the
    array. But it is low priority because it is never used that way.


    That's the kind of potentially confusing short-cut you can make when
    only you ever use the language.  It is also fine for when people make
    their own array_length macros for use in their own code.  When you know
    all the details of the operator/macro/function, and you are the only one using it, it's okay if the specification is weird and unhelpful for some cases.  It's a different matter entirely if you are making something
    that other people will use.


    In C, sizeof() is usually compile-time, except when used on VLAs, and
    here, as usual for VLAs, it is a lot more complicated than you'd think:


    No it is not.  I don't think "garbage in, garbage out" is a complicated concept.  You can write pathological shit in any language and get meaningless results out - it has no bearing on anything in real code.
    (It can be fun to play around with, but it does not mean there is a
    problem with the language feature.)


    And yet, in my example, lccwin32 gave the wrong result, and in one case crashed. (I no longer have other smaller C compilers to try out.)

    It suggests the problem is not trivial.

    The concept is not that easy to get your head around either, the idea
    that the variable aspects of a VLA are associated with its type, not its
    value or instance.

    In my example, there were no instances of any actual arrays, not even if
    I declared an instance of one of the pointers involved, yet space had to
    be allocated for its size. And I kept the type simple.

    I remember also trying out VLAs within a struct, and there I got a wider variance of results.

    You can keep saying that VLAs are trivial to understand and implement; I
    won't believe you.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to David Brown on Sat Jan 6 12:53:56 2024
    On 06/01/2024 09:09, David Brown wrote:
    On 05/01/2024 23:19, Bart wrote:
    On 05/01/2024 14:05, David Brown wrote:
    On 05/01/2024 02:53, Bart wrote:

    It doesn't need to take a heavy-handed approach like C++ or Python.


    One option for a useful array length operator/function/macro is a
    simple and limited feature that works for arrays of known size and
    gives a hard (compile-time) error when the size is not known.  The
    one you have in your own language covers most of that, except for the
    insanity of evaluating to 0 when given a pointer/reference to an
    "unbounded" array.

    It gives zero because that is actually the compile-time type of the
    array. But it is low priority because it is never used that way.


    That's the kind of potentially confusing short-cut you can make when
    only you ever use the language.

    I spent 10 mins trying to fix this today. So that '.len' on bounds
    specified as `[]` rather than `[0]`, and not later set by init data,
    would be an error.

    Then I got stuck on examples like '[]int a = ()' and realised it's
    tricky. And then I thought, why the hell am I doing this? A language
    I've already used for a million lines of code.

    Because somebody on the internet (who has never created and implemented
    a real language AFAIK) told me so?

    I don't claim it to be perfect, just that it does a better job than C.

    At least it has '.len' and quite a few extra bits to do with fixed-size
    arrays:

    'M' C

    Value arrays Y N
    Ptr to array params Y N (also Y but non-idiomatic)
    Pass-by-reference Y N

    Index a pointer N Y
    Deref an array N Y

    0-based arrays Y Y
    1-based Y N
    N-based Y N

    Number of bytes X.bytes sizeof(X)
    T.bytes sizeof(T)

    Array Length X.len sizeof(X)/sizeof(X[0])
    T.len ?

    Array Lower Bound X.lwb 0
    T.lwb 0

    Array Upper Bound X.upb sizeof(X)/sizeof(X[0])-1
    T.upb ?

    Table Indexing A[i,j] A[i][j] or j[A[i]] or j[i[A]]

    Loop over bounds For i in A.bounds do
    for (int i=0;
    i<sizeof(A)/sizeof(A[0]); ++i)

    Loop over values For x in A do
    For i,x in A do # (both)
    for (int i=0;
    i<sizeof(A)/sizeof(A[0]); ++i) {
    T x = A[i]; ...

    Zero array clear A memset(A, 0, sizeof(A)/sizeof(A[0]));

    Slices Y N

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to Bart on Sat Jan 6 14:11:52 2024
    On 06/01/2024 12:53, Bart wrote:
    On 06/01/2024 09:09, David Brown wrote:
    On 05/01/2024 23:19, Bart wrote:

    At least it has '.len' and quite a few extra bits to do with
    fixed-size arrays:

    'M' C

    Value arrays Y N
    Ptr to array params Y N (also Y but non-idiomatic)
    Pass-by-reference Y N

    Index a pointer N Y
    Deref an array N Y

    0-based arrays Y Y
    1-based Y N
    N-based Y N

    Number of bytes X.bytes sizeof(X)
    T.bytes sizeof(T)

    Array Length X.len sizeof(X)/sizeof(X[0])
    T.len ?

    Array Lower Bound X.lwb 0
    T.lwb 0

    Array Upper Bound X.upb sizeof(X)/sizeof(X[0])-1
    T.upb ?

    Table Indexing A[i,j] A[i][j] or j[A[i]] or j[i[A]]

    Loop over bounds For i in A.bounds do
    for (int i=0;
    i<sizeof(A)/sizeof(A[0]); ++i)

    Loop over values For x in A do
    For i,x in A do # (both)
    for (int i=0;
    i<sizeof(A)/sizeof(A[0]); ++i) {
    T x = A[i]; ...

    Zero array clear A memset(A, 0, sizeof(A)/sizeof(A[0]));

    The C is wrong; it should be just sizeof(A).

    You can see how sizeof or sizeof/sizeof features quite a bit in the
    right-hand column, but even the shorter .len is little seen on the left.

    It's easy to get it wrong or nor immediately notice (unless your
    initials are SL of course).

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Bart on Sat Jan 6 15:23:38 2024
    On 06/01/2024 11:27, Bart wrote:
    On 06/01/2024 09:09, David Brown wrote:
    On 05/01/2024 23:19, Bart wrote:
    On 05/01/2024 14:05, David Brown wrote:
    On 05/01/2024 02:53, Bart wrote:

    It doesn't need to take a heavy-handed approach like C++ or Python.


    One option for a useful array length operator/function/macro is a
    simple and limited feature that works for arrays of known size and
    gives a hard (compile-time) error when the size is not known.  The
    one you have in your own language covers most of that, except for
    the insanity of evaluating to 0 when given a pointer/reference to an
    "unbounded" array.

    It gives zero because that is actually the compile-time type of the
    array. But it is low priority because it is never used that way.


    That's the kind of potentially confusing short-cut you can make when
    only you ever use the language.  It is also fine for when people make
    their own array_length macros for use in their own code.  When you
    know all the details of the operator/macro/function, and you are the
    only one using it, it's okay if the specification is weird and
    unhelpful for some cases.  It's a different matter entirely if you are
    making something that other people will use.


    In C, sizeof() is usually compile-time, except when used on VLAs, and
    here, as usual for VLAs, it is a lot more complicated than you'd think:


    No it is not.  I don't think "garbage in, garbage out" is a
    complicated concept.  You can write pathological shit in any language
    and get meaningless results out - it has no bearing on anything in
    real code. (It can be fun to play around with, but it does not mean
    there is a problem with the language feature.)


    And yet, in my example, lccwin32 gave the wrong result, and in one case crashed. (I no longer have other smaller C compilers to try out.)

    It suggests the problem is not trivial.

    So what you are telling us is that a relatively simple and little-used
    compiler has bugs in dealing with pathological cases of code
    intentionally designed to be difficult and be far from any kind of
    realistic code? Are we supposed to be surprised? I'm sure the lccwin32
    author will be interested in the test case, because such extreme
    corner-case testing is useful for finding bugs in software - but it is
    only of interest to him and not to anyone actually writing C code. (And
    in this case, since your code has UB, there is no such thing as a
    correct result - and therefore no possibility of a wrong result.)


    The concept is not that easy to get your head around either, the idea
    that the variable aspects of a VLA are associated with its type, not its value or instance.

    If you find VLA's difficult, don't use them. Even if you were
    interested in making your tools compliant, they are optional from C11
    onwards. Since you are the only person who uses your tools, and you
    won't write C code with VLAs, you have nothing to lose by ignoring them entirely.


    In my example, there were no instances of any actual arrays, not even if
    I declared an instance of one of the pointers involved, yet space had to
    be allocated for its size. And I kept the type simple.

    I remember also trying out VLAs within a struct, and there I got a wider variance of results.

    You can keep saying that VLAs are trivial to understand and implement; I won't believe you.


    OK. Don't believe me.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Richard Damon@21:1/5 to David Brown on Sat Jan 6 09:56:05 2024
    On 1/6/24 9:28 AM, David Brown wrote:

    I have no idea why you are doing this.  I have no idea why you thought
    it was a good idea to make your own personal language and write a
    million lines that no one can ever use again, or work with, change,
    re-use, maintain or update.  I have no idea why you hate C, why you
    obsess about a language you hate and why you rave about your language
    that no one else uses and no one else cares about in a newsgroup for C.
    I have no idea why you continue to think that your language is God's
    gift to programming and that everyone else, the world over, is crazy to imagine there are any reasons to do something differently from the way
    you do things in your language.

    Yes, "why?" is a good question.


    I think the big reason for his "Why?" is that he KNOWS that his language
    isn't sufficient, but will need to interface to things that others have
    done, and those will invariably be written in C.

    His complaint is that while C has become the common-tounge for
    inter-language linkage, it really isn't designed for that.

    The "shims" to perform this tend to need some part of it to be written in C.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Bart on Sat Jan 6 15:28:52 2024
    On 06/01/2024 13:53, Bart wrote:
    On 06/01/2024 09:09, David Brown wrote:
    On 05/01/2024 23:19, Bart wrote:
    On 05/01/2024 14:05, David Brown wrote:
    On 05/01/2024 02:53, Bart wrote:

    It doesn't need to take a heavy-handed approach like C++ or Python.


    One option for a useful array length operator/function/macro is a
    simple and limited feature that works for arrays of known size and
    gives a hard (compile-time) error when the size is not known.  The
    one you have in your own language covers most of that, except for
    the insanity of evaluating to 0 when given a pointer/reference to an
    "unbounded" array.

    It gives zero because that is actually the compile-time type of the
    array. But it is low priority because it is never used that way.


    That's the kind of potentially confusing short-cut you can make when
    only you ever use the language.

    I spent 10 mins trying to fix this today. So that '.len' on bounds
    specified as `[]` rather than `[0]`, and not later set by init data,
    would be an error.

    Then I got stuck on examples like '[]int a = ()' and realised it's
    tricky. And then I thought, why the hell am I doing this? A language
    I've already used for a million lines of code.


    I have no idea why you are doing this. I have no idea why you thought
    it was a good idea to make your own personal language and write a
    million lines that no one can ever use again, or work with, change,
    re-use, maintain or update. I have no idea why you hate C, why you
    obsess about a language you hate and why you rave about your language
    that no one else uses and no one else cares about in a newsgroup for C.
    I have no idea why you continue to think that your language is God's
    gift to programming and that everyone else, the world over, is crazy to
    imagine there are any reasons to do something differently from the way
    you do things in your language.

    Yes, "why?" is a good question.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to Richard Damon on Sat Jan 6 15:57:00 2024
    On 06/01/2024 14:56, Richard Damon wrote:
    On 1/6/24 9:28 AM, David Brown wrote:

    I have no idea why you are doing this.  I have no idea why you thought
    it was a good idea to make your own personal language and write a
    million lines that no one can ever use again, or work with, change,
    re-use, maintain or update.  I have no idea why you hate C, why you
    obsess about a language you hate and why you rave about your language
    that no one else uses and no one else cares about in a newsgroup for
    C. I have no idea why you continue to think that your language is
    God's gift to programming and that everyone else, the world over, is
    crazy to imagine there are any reasons to do something differently
    from the way you do things in your language.

    Yes, "why?" is a good question.


    I think the big reason for his "Why?" is that he KNOWS that his language isn't sufficient, but will need to interface to things that others have
    done,

    I used it for at least 10 years without needing to interface to
    anything. After that I needed to use OS and other libraries.

    and those will invariably be written in C.

    His complaint is that while C has become the common-tounge for
    inter-language linkage, it really isn't designed for that.

    The inter-language ABI is based on sets of MACHINE types such: u8-u64,
    i8-i64, f32-f64, plus pointers and aggregate types. Those are common to
    many languages, not just C, which tend to have far more sensible and
    stable ways to denote such types.

    That is what my FFIs use too. At this point C doesn't enter into it.

    But it is true that many APIs (not ABIs) are defined in C terms or as C headers. And in the latter, yes C's types could be better. And often,
    some other aspects of C such as macros, which can include arbitrary C
    code, can be exposed.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to David Brown on Sat Jan 6 15:43:01 2024
    On 06/01/2024 14:28, David Brown wrote:
    On 06/01/2024 13:53, Bart wrote:
    On 06/01/2024 09:09, David Brown wrote:
    On 05/01/2024 23:19, Bart wrote:
    On 05/01/2024 14:05, David Brown wrote:
    On 05/01/2024 02:53, Bart wrote:

    It doesn't need to take a heavy-handed approach like C++ or Python. >>>>>>

    One option for a useful array length operator/function/macro is a
    simple and limited feature that works for arrays of known size and
    gives a hard (compile-time) error when the size is not known.  The
    one you have in your own language covers most of that, except for
    the insanity of evaluating to 0 when given a pointer/reference to
    an "unbounded" array.

    It gives zero because that is actually the compile-time type of the
    array. But it is low priority because it is never used that way.


    That's the kind of potentially confusing short-cut you can make when
    only you ever use the language.

    I spent 10 mins trying to fix this today. So that '.len' on bounds
    specified as `[]` rather than `[0]`, and not later set by init data,
    would be an error.

    Then I got stuck on examples like '[]int a = ()' and realised it's
    tricky. And then I thought, why the hell am I doing this? A language
    I've already used for a million lines of code.


    I have no idea why you are doing this.  I have no idea why you thought
    it was a good idea to make your own personal language and write a
    million lines that no one can ever use again, or work with, change,
    re-use, maintain or update.

    It started as an in-house language. It was used for real work, real
    programs. The products I made with it generated some $1m a year of
    business and created work for quite a few people. 1000s of customers
    benefited from those products for their own endeavours. And I managed to
    retire at age 42.

    Those are some reasons.


      I have no idea why you hate C, why you
    obsess about a language you hate and why you rave about your language
    that no one else uses and no one else cares about in a newsgroup for C.
    I have no idea why you continue to think that your language is God's
    gift to programming and that everyone else, the world over, is crazy to imagine there are any reasons to do something differently from the way
    you do things in your language.

    Yes, "why?" is a good question.

    Why? You pick on some relatively minor issue in my language, of which C
    has in spades.

    I list some aspects of working with arrays which I do better than C, to
    put into perspective why I considered that low priority.

    And you choose to go on the attack.

    That no one uses my language is irrelevant. The ideas would stand by themselves, even if never implemented. But being used in a real, proven
    product should lend more credence. Be funny if you took vapourware more seriously.

    Where are the systems languages from 70s/80s that did it properly? There
    aren't any; Unix put paid to that by inflicting its shitty language on everybody and everything.

    I picked on the sizeof thing for arrays as a simple example of C's
    macros used to paper over deficiences in the core language. You and
    others say it's no big deal.

    Well, dozens of other languages do do it properly, eg. using len(A), not
    just mine. Mine just demonstrates its use in a language of similar level
    and scale.

    I think you're just cross that my table wipes the floor with C and so
    want to piss all over my own efforts.

    --------
    BTW here is my original comment about sizeof/sizeof:

    BC to DB:

    You missed my point. Take a tiny feature like being able to easily get
    the size of a fixed-length array. You commonly see macro like this:

    #define LENGTH(a) (sizeof(a)/size(a[0]))

    What incentive is there to properly add a built-in way to do that, when
    it can be done, badly, and in 1000 different ways by each user, in a
    one-line macro?

    Another example is GETBIT(a, n).

    --------

    It's a good job I went with LENGTH and not GETBIT. I use as A.[n] for
    both GETBIT and SETBIT, and you would have gotten even angrier. How dare somebody not only come up with some decent ideas for a systems language,
    but who actually has the gall to go and implement them.

    You are welcome to 'A.[i]'; I think it would have been a far more
    interesting addition to new C that whatever it was that did go in. And
    easy to implement to boot; with I believe no clashes with current syntax.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to Richard Damon on Sat Jan 6 23:45:38 2024
    On Sat, 6 Jan 2024 09:56:05 -0500, Richard Damon wrote:

    His complaint is that while C has become the common-tounge for
    inter-language linkage, it really isn't designed for that.

    The "shims" to perform this tend to need some part of it to be written
    in C.

    There is a thing called libffi, which is commonly used by many high-level languages to interface to C code (or to code that assumes that it is being called from C code). This is the basis of the ctypes module in the
    standard Python library, for example.

    I have successfully used ctypes to produce “Pythonic” wrappers for several useful facilities (e.g. Cairo graphics, D-Bus, inotify). By “Pythonic” I mean that they try to look as though the underlying facility was designed
    to be used from Python.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to Bart on Sat Jan 6 23:58:02 2024
    On Sat, 6 Jan 2024 15:57:00 +0000, Bart wrote:

    But it is true that many APIs (not ABIs) are defined in C terms or as C headers.

    I wish they would be more consistent about that. It has been about a
    quarter century since C99 introduced stdbool, yet you still see rigmarole
    like (from /usr/include/SDL2/SDL_egl.h):

    typedef enum {
    KHRONOS_FALSE = 0,
    KHRONOS_TRUE = 1,
    KHRONOS_BOOLEAN_ENUM_FORCE_SIZE = KHRONOS_MAX_ENUM
    } khronos_boolean_enum_t;

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to Keith Thompson on Sun Jan 7 00:09:11 2024
    On 06/01/2024 21:40, Keith Thompson wrote:
    Bart <bc@freeuk.cm> writes:
    [...]
    The concept is not that easy to get your head around either, the idea
    that the variable aspects of a VLA are associated with its type, not
    its value or instance.

    Oh? I don't find it difficult at all. It's the kind of thing you learn once, and then you know it. Now you know it, but you're still
    complaining for some reason.

    Maybe you've never had to implement it. It is certainly not intuitive:

    int n=rand();

    typedef int T[n]; // here the size is stored with the type

    int A[n]; // here you'd expect it with each variable
    int B[n];
    T C[n]; // and here in both

    You might also expect the data of those variables to go on the stack.
    But here:

    T** P;

    it presumably goes on the heap, when you get around to allocating it.

    Despite all the complexity associated with VLAs (for example managing 17
    active VLA allocations on the stack, and assorted VLA typedefs that are
    now executable code) as you goto in and out of nested block scopes),
    when you pass a VLA 'A' for example to a function, you still have to
    supply the size or length separately.

    With the far simpler slice mechanism that I mentioned several posts
    back, that includes the length.

    The reason I brought up VLAs at all was because the language skipped a simple-to-implement and potentially more useful feature, for a much
    harder one.

    If I was to independently add a VLA-like feature to C, I would base it
    on slices. And I would allow them only at the top level of data
    structures, not nested, nor within conventional arrays, nor as a pointer
    target (if the memory management is to be automatic).

    They would also use heap storage not stack. And there would no dynamic
    elements within typedefs; the actual size is an attribute of the
    variable instance. Typedefs stay a purely compile-time artefact.

    That would cover 99% of the ways that VLAs are typically used, of which
    I believe a big chunk are inadvertent.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to Lawrence D'Oliveiro on Sun Jan 7 00:21:00 2024
    On 06/01/2024 23:45, Lawrence D'Oliveiro wrote:
    On Sat, 6 Jan 2024 09:56:05 -0500, Richard Damon wrote:

    His complaint is that while C has become the common-tounge for
    inter-language linkage, it really isn't designed for that.

    The "shims" to perform this tend to need some part of it to be written
    in C.

    There is a thing called libffi, which is commonly used by many high-level languages to interface to C code (or to code that assumes that it is being called from C code). This is the basis of the ctypes module in the
    standard Python library, for example.

    I have successfully used ctypes to produce “Pythonic” wrappers for several
    useful facilities (e.g. Cairo graphics, D-Bus, inotify). By “Pythonic” I mean that they try to look as though the underlying facility was designed
    to be used from Python.

    LIBFFI solves a different problem, which is that of synthesising
    function calls at runtime, given a function name as a string, and a
    sequence of N arguments as assorted types.

    Typically used in interpreted code.

    Native code calling native code across languages generally doesn't need
    that. It needs that information about the 1000s of different functions,
    types, and enums that are used in an API, in a set of bindings suitable
    for use in the calling language.

    LIBFFI doesn't help here; you still need that info whether making a
    statically compiled call or a dynamically synthesised one.

    (When you do need something like LIBFFI, then it is another of those hard-to-build C libraries.

    I generally don't bother with it. I use a 60-line solution of my own
    which cheats by using inline assembly. A lot less hassle, but it is
    specific to each target.)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to Bart on Sun Jan 7 00:55:28 2024
    On Sun, 7 Jan 2024 00:21:00 +0000, Bart wrote:

    (When you do need something like LIBFFI, then it is another of those hard-to-build C libraries.

    sudo apt-get install libffi-dev

    does it for me.

    If you want to learn how to build things from source, maybe look at how a source-based distro like Gentoo does it? They provide scripts to do automatically what you struggle to manage with your human brain. Maybe too
    much exposure to Microsoft Windows?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to Bart on Sun Jan 7 00:16:05 2024
    On Sun, 7 Jan 2024 00:09:11 +0000, Bart wrote:

    typedef int T[n]; // here the size is stored with the type

    int A[n]; // here you'd expect it with each variable int
    B[n];
    T C[n]; // and here in both

    The array size is in the wrong place. Java at least puts it in a more
    natural place:

    int[n] A;
    ... etc ...

    though unfortunately it forgets to include typedefs.

    Type specifications in C are all backwards, anyway. They should have
    adopted the Pascal syntax for that.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to Keith Thompson on Sun Jan 7 00:58:49 2024
    On Sat, 06 Jan 2024 16:40:03 -0800, Keith Thompson wrote:

    C uses a "declaration follows usage" rule (though not with 100%
    consistency).

    And putting the function result before the argument types turns out to
    cause trouble when carried over to C++, when you try to express
    dependencies between them. So they had to add a Pascal-style alternative syntax, with the function result declared after the arguments.

    Even pointer dereferencing should have been done with a postfix, not a
    prefix operator. Consider why you need “->”: it’s purely syntactic sugar to make things like

    (*a).b

    less awkward as

    a->b

    Whereas in Pascal, for example, there is no need for any alternative
    syntax to

    a^.b

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Janis Papanagnou@21:1/5 to Lawrence D'Oliveiro on Sun Jan 7 02:12:03 2024
    On 07.01.2024 01:16, Lawrence D'Oliveiro wrote:
    On Sun, 7 Jan 2024 00:09:11 +0000, Bart wrote:

    typedef int T[n]; // here the size is stored with the type

    int A[n]; // here you'd expect it with each variable int
    B[n];
    T C[n]; // and here in both

    The array size is in the wrong place.

    You mean that the poster has a misconception about the declaration
    mapping to the actual formal semantics? (That might at least explain
    why he's confused by the C way.)

    Java at least puts it in a more natural place:

    int[n] A;
    ... etc ...

    Or Algol(68) that I upthread mentioned for its formal sophistication

    [n] int A;


    though unfortunately it forgets to include typedefs.

    where Algol has 'mode' declarations for types

    mode intarr = [n] int A;


    Type specifications in C are all backwards, anyway. They should have
    adopted the Pascal syntax for that.

    Or any other [more] coherent language already existing at that time.

    Janis

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to Keith Thompson on Sun Jan 7 01:00:36 2024
    Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
    Bart <bc@freeuk.cm> writes:
    On 05/01/2024 22:50, Keith Thompson wrote:
    Bart <bc@freeuk.cm> writes:
    On 05/01/2024 18:44, Scott Lurndal wrote:
    [...]
    There are thousands of ways programmers can introduce
    bugs far more easily which are significantly harder
    to find and fix; and yes, I expect programmers to
    fully understand the code they're writing and test
    their code to find bugs which they then will subsequently
    fix.
    Do lookup the word 'strawman' as it relates to discourse.

    And yet, despite it having no problems according to you, people still
    feel the need to create macros like ARRAY_SIZE.
    Yes. That's how they use the idiom.

    So I'm puzzled. Why do they do that? What imaginary problem (according >>>> to you) do they solve?
    I don't know what you're puzzled about.

    I'm puzzled because people like Scott are suggesting those macros are
    a waste of time, and yet you find them in big, important software.

    Oh, so *that's* what you're complaining about.

    I just took a look at Scott's article to which you replied, and its
    parent, and so on all the way to the first article in this thread.
    Nowhere did Scott suggest that those macros are a waste of time.

    He did write some things that suggest that he considers the
    `sizeof arr / sizeof arr[0]` idiom clear enough to be written directly
    as part of an expression rather than as a macro (and I agree), but I
    didn't see him objecting to the idea of using a macro.

    Correct.


    I can't speak for him, but I think he was objecting to your insistence
    that using the idiom directly is unacceptable, and you interpreted that
    as him insisting that wrapping the idiom in a macro is useless. He
    didn't say that.

    Correct.


    I'm making an assumption here, that you find code that uses the idiom >directly more objectionable than code that wraps it in a macro.

    Personally, I'd be likely to define a macro if I'm going to be using it >multiple times. In an example I mentioned recently, where I modified
    some existing code to use `sizeof array / sizeof array[0]`, I was only
    doing it once, so just wrote the expression directly. Either method is
    fine with me, and I suspect Scott would agree.

    Indeed. I do believe that absent standardization, using a macro
    adds a level of indirection that may adversely affect the code
    readability (whether it is ARRAY_SIZE, ARRAY_LENGTH, LENGTH, NUM_ELEMENTS,
    or whatever, I would need to refer to the macro definition to
    determine the intent of the programmer).

    If syntax was added to the language standard that provided
    similar functionality, I'd certainly use it.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to Bart on Sun Jan 7 01:47:22 2024
    On 07/01/2024 01:45, Bart wrote:
    On 07/01/2024 01:12, Janis Papanagnou wrote:
    On 07.01.2024 01:16, Lawrence D'Oliveiro wrote:
    On Sun, 7 Jan 2024 00:09:11 +0000, Bart wrote:

          typedef int T[n];   // here the size is stored with the type >>>>
          int A[n];           // here you'd expect it with each variable
    int
          B[n];
          T C[n];             // and here in both

    The array size is in the wrong place.

    You mean that the poster has a misconception about the declaration
    mapping to the actual formal semantics? (That might at least explain
    why he's confused by the C way.)

    There was nothing wrong with the C code. (The quote has garbled the
    'int' belong to B.)

    LD'O is saying the C syntax puts it in the wrong place.

    Java at least puts it in a more natural place:

         int[n] A;
         ... etc ...

    Or Algol(68) that I upthread mentioned for its formal sophistication

         [n] int A;


    though unfortunately it forgets to include typedefs.

    where Algol has 'mode' declarations for types

         mode intarr = [n] int A;

    Interesting. My syntax uses:

       [n]int A
       type intarr = [n]int A

    I was blindly copying your example. There is no 'A' in the type
    definition, it is just:

    type intarr = [n]int

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to Lawrence D'Oliveiro on Sun Jan 7 01:26:29 2024
    On 07/01/2024 00:55, Lawrence D'Oliveiro wrote:
    On Sun, 7 Jan 2024 00:21:00 +0000, Bart wrote:

    (When you do need something like LIBFFI, then it is another of those
    hard-to-build C libraries.

    sudo apt-get install libffi-dev

    does it for me.

    And then what? LIBFFI is still hard to use.

    Bear in mind I would need to call LIBFFI itself across an FFI. Even if
    it worked beautifully, it's a horrible dependency with the usual ghastly
    build methods that require either Linux or MSVC, from what I can see.

    Why, for a specific platform, can I not just get one .c and/or
    one .s file to do the job?

    Unlike the algorithms behind GMP, this is a task I do understand!

    Below is the 60-line solution I mentioned. The function has already been located so that a reference to it is passed.

    (There is also a HLL-only version, including one in C, but it has many restrictions.)

    If you want to learn how to build things from source, maybe look at how a source-based distro like Gentoo does it?

    They provide scripts to do
    automatically what you struggle to manage with your human brain. Maybe too much exposure to Microsoft Windows?

    I can build ALL my projects using an invocation like:

    mm prog

    Most build in 1/10th of a second. I'm struggling to see how it can get
    any simpler or faster!

    This is what I do. I tried to do it with my C compiler, but the language
    puts some difficulties there, so that might be more like:

    mcc @prog

    Still only a compiler and the source files plus this auxiliary file.


    --------------------------------------
    --------------------------------------
    Core of 'libffi' solution for x64/Win64ABI
    Not in C code.
    --------------------------------------

    export func os_calldllfunction(
    ref proc fnaddr,
    int retcode, nargs,
    ref[]i64 args,
    ref[]byte argcodes)u64 =

    u64 a
    r64 x
    int nextra, pushedbytes

    nextra:=0

    !The stack is 16-byte aligned at this point

    if nargs<4 then
    nextra:=4-nargs !need at least 4 slots for shadow space
    elsif nargs.odd then !need one more for a 16-byte-aligned stack
    nextra:=1
    fi

    pushedbytes:=(nextra+nargs)*8

    to nextra do
    asm push 0
    od

    for i:=nargs downto 1 do
    a:=args[i] !get generic 64-bit value to push
    asm push u64 [a]
    od

    !blindly load first 4 args (incl unused) to both int/float regs
    !(both must be loaded when calling variadic functions)

    assem
    mov D10,[Dstack]
    movq XMM0,[Dstack]
    mov D11,[Dstack+8]
    movq XMM1,[Dstack+8]
    mov D12,[Dstack+16]
    movq XMM2,[Dstack+16]
    mov D13,[Dstack+24]
    movq XMM3,[Dstack+24]
    end

    if retcode='I' then
    a:=((ref func:i64(fnaddr))^())
    asm add Dstack, [pushedbytes]
    return a

    else
    x:=((ref func:r64(fnaddr))^())
    asm add Dstack, [pushedbytes]
    return u64@(x) ! (type-punning cast)

    fi
    end

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to Janis Papanagnou on Sun Jan 7 01:45:10 2024
    On 07/01/2024 01:12, Janis Papanagnou wrote:
    On 07.01.2024 01:16, Lawrence D'Oliveiro wrote:
    On Sun, 7 Jan 2024 00:09:11 +0000, Bart wrote:

    typedef int T[n]; // here the size is stored with the type

    int A[n]; // here you'd expect it with each variable int >>> B[n];
    T C[n]; // and here in both

    The array size is in the wrong place.

    You mean that the poster has a misconception about the declaration
    mapping to the actual formal semantics? (That might at least explain
    why he's confused by the C way.)

    There was nothing wrong with the C code. (The quote has garbled the
    'int' belong to B.)

    LD'O is saying the C syntax puts it in the wrong place.

    Java at least puts it in a more natural place:

    int[n] A;
    ... etc ...

    Or Algol(68) that I upthread mentioned for its formal sophistication

    [n] int A;


    though unfortunately it forgets to include typedefs.

    where Algol has 'mode' declarations for types

    mode intarr = [n] int A;

    Interesting. My syntax uses:

    [n]int A
    type intarr = [n]int A

    But then it came from Algol 68 in the first place. However, that
    language also got some things wrong. Eg. your example might be written
    as '[n]INT a;' due to 'stropping'.

    Notice my version uses a single case, and there are no semicolons. The
    Algol68 rules for semicolons, which separate statements not terminate,
    are a complete PITA.

    You spend half your time special-casing the last statement in a block,
    as that's the one without the semicolon - until you insert, delete,
    move, comment or uncomment lines, then you have to fix it.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to Bart on Sun Jan 7 02:14:41 2024
    On Sun, 7 Jan 2024 01:26:29 +0000, Bart wrote:

    And then what? LIBFFI is still hard to use.

    Using Python’s ctypes module, which is basically built on top of libffi, I have not found to be that hard at all.

    Looking at the sizes of those particular Python wrappers I mentioned:

    Cairo graphics <https://gitlab.com/ldo/qahirah> -- 8500 lines
    D-bus <https://gitlab.com/ldo/dbussy/> -- 11,000 lines
    inotify <https://gitlab.com/ldo/inotipy> -- under 600 lines

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to Janis Papanagnou on Sun Jan 7 02:16:19 2024
    On Sun, 7 Jan 2024 02:12:03 +0100, Janis Papanagnou wrote:

    Or Algol(68) that I upthread mentioned for its formal sophistication

    [n] int A;

    And for added fun, the distinction between

    [m, n] INT

    and

    [m][n] INT

    (using capitalization to indicate bold)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to Keith Thompson on Sun Jan 7 02:25:27 2024
    On 07/01/2024 01:15, Keith Thompson wrote:
    Bart <bc@freeuk.cm> writes:
    On 06/01/2024 21:40, Keith Thompson wrote:
    Bart <bc@freeuk.cm> writes:
    [...]
    The concept is not that easy to get your head around either, the idea
    that the variable aspects of a VLA are associated with its type, not
    its value or instance.
    Oh? I don't find it difficult at all. It's the kind of thing you
    learn
    once, and then you know it. Now you know it, but you're still
    complaining for some reason.

    Maybe you've never had to implement it. It is certainly not intuitive:

    No, I haven't. I worked on compilers in the distant past (for Ada,
    which doesn't distinguish on the language level between array with
    constant and non-constant bounds), but I haven't implemented VLAs for C.

    We were talking about the fact that, as you say, "variable aspects of a
    VLA are associated with its type, not its value or instance". Are you
    saying that makes implementing it unreasonably difficult?

    It makes it bizarre. It would make a typedef involving a VLA type
    something to be executed at runtime; it is executable code.


    int n=rand();

    typedef int T[n]; // here the size is stored with the type

    Yes, of course it is. And keep in mind that typedef doesn't create a
    new type. A compiler will create, at compile time, some internal node
    (or whatever data structure it uses) representing the anonymous type `int[n]`, and will associate an implicitly created automatic object with
    it to hold its length or size. It will then create a node representing
    the typedef T, referring to the anonymous array type.

    int A[n]; // here you'd expect it with each variable

    Why would you expect that?

    Why would expect it to be part of some anonymous, associated type?

    If you had a counted string type, would you expect its length to be part
    of the data belonging to the object, and or part of a separate type object?

    You might call it metadata, but being a type doesn't come to mind. What
    would be the point of that; what could you do with that type?

    Suppose you had a counted string variable S, currently set to "ABC" so
    that its length (which you say is naturally part its type) is 3.

    Assume you could somehow do this:

    typeof(S) T

    would you expect T to also have a length 3? What would the string contain?

    It doesn't make sense. But you say that's how VLAs work:

    int A[rand()];

    typeof(A) B;

    B has the same length as A, whatever that is.

    And if you have std::vector you don't need VLAs or slices. If you use a language other than C, you can avoid C's limitations.

    Can a std::vector also be a view? If not then you still need slices.

    However when I looked at std::vector, it was basically a pointer,
    length, and capacity. My slices were pointer and length. Capacity was
    something I'd thought about.

    Automatic memory management however would complicate matters greatly.
    This language (whether C or my own) is lower level and generally explicit.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Lawrence D'Oliveiro on Sun Jan 7 03:30:08 2024
    On 2024-01-07, Lawrence D'Oliveiro <ldo@nz.invalid> wrote:
    On Sat, 06 Jan 2024 16:40:03 -0800, Keith Thompson wrote:

    C uses a "declaration follows usage" rule (though not with 100%
    consistency).

    And putting the function result before the argument types turns out to
    cause trouble when carried over to C++, when you try to express
    dependencies between them. So they had to add a Pascal-style alternative syntax, with the function result declared after the arguments.

    In this millennium, you can have dependencies that flow opposite
    to the lexical order of tokens.

    C++ itself has no problem having inline function in a class declaration mutually call each other, without any forward declarations.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Bart on Sun Jan 7 03:32:33 2024
    On 2024-01-06, Bart <bc@freeuk.cm> wrote:
    Then I got stuck on examples like '[]int a = ()' and realised it's
    tricky. And then I thought, why the hell am I doing this? A language
    I've already used for a million lines of code.

    That exact reasoning can be used to reject new features from C,
    like an operator for the number of elements in an array.

    People have written billions of lines of C without it!

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to Kaz Kylheku on Sun Jan 7 11:37:11 2024
    On 07/01/2024 03:32, Kaz Kylheku wrote:
    On 2024-01-06, Bart <bc@freeuk.cm> wrote:
    Then I got stuck on examples like '[]int a = ()' and realised it's
    tricky. And then I thought, why the hell am I doing this? A language
    I've already used for a million lines of code.

    That exact reasoning can be used to reject new features from C,
    like an operator for the number of elements in an array.

    People have written billions of lines of C without it!


    That's a feature for which there is a need.

    The other is requiring a diagnostic, somewhat less useful if you just
    want to loop over an array without it looking like a dog's dinner.

    So, C has distinct concepts of 'zero-length' array and 'unbounded
    array', so that sizeof/sizeof on the latter generates a diagnostic.

    Great. But you don't get one here:

    int a;
    int* p=&a;

    printf("%zu\n", sizeof(p)/sizeof(p[0]));

    'p' is neither a zero-length nor unbounded array, but the array-length
    idiom still works. This program will likely print '2' on 64-bit machines.

    If I try the same thing:

    int a
    ref int p := &a

    println p.len

    it gives an error. But I can also get C's behaviour if I really wanted
    to do that:

    println p.bytes/p^.bytes

    This shows '1' (int is 64 bits).

    There /is/ a significant difference between sizeof and lengthof;
    emulating the latter with sizeof doesn't really cut it.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to Lawrence D'Oliveiro on Sun Jan 7 12:14:24 2024
    On 07/01/2024 02:14, Lawrence D'Oliveiro wrote:
    On Sun, 7 Jan 2024 01:26:29 +0000, Bart wrote:

    And then what? LIBFFI is still hard to use.

    Using Python’s ctypes module, which is basically built on top of libffi, I have not found to be that hard at all.

    Looking at the sizes of those particular Python wrappers I mentioned:

    Cairo graphics <https://gitlab.com/ldo/qahirah> -- 8500 lines
    D-bus <https://gitlab.com/ldo/dbussy/> -- 11,000 lines
    inotify <https://gitlab.com/ldo/inotipy> -- under 600 lines

    While I have my own C alternative language, I also have my own Python alternative language, although it is smaller, lower level and less
    dynamic. On this scale:

    C-1---2---------Python

    my languages might occupy positions 1 and 2.

    Using Python’s ctypes module, which is basically built on top of
    libffi, I
    have not found to be that hard at all.

    OK, so you're using:

    * A LIBFFI module that someone one else has written

    * A language (Python) that someone else has implemented (and
    originally, someone else has designed)

    * An extension (Ctypes) that is already done by somebody

    * A set of FFI bindings to some external library that someone has
    already taken the trouble to create

    * On top of that, you're probably using a C compiler that someone
    else has implemented (used to build LIBFFI etc and which has
    likely been used to build Python)

    * Plus a bunch of dependencies that others have taken care of (build
    systems etc)


    So it is not surprising that:

    I have not found to be that hard at all.

    Is there much left for you to do?

    I find it a little bit harder because I design and implement my own
    languages, I need to create my own bindings to libraries, and do all
    that without any dependencies other than those libraries I'm trying to use.

    And yet my dynamic language's FFI is much easier to use than most
    scripting languages. The FFI is built in as is support the low-level
    types needed. No extension module is needed.

    I can do this from interpreted, dynamic code (the printf is predefined
    but I'm showing here how it is predefined:

    importdll msvcrt =
    func printf(stringz, ...)int32
    end

    printf("Hello, World!\n")

    (When I used to run it on Linux, the 'msvcrt' library was mapped
    internally to 'libc.so.6').

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Lawrence D'Oliveiro on Sun Jan 7 15:48:50 2024
    On 07/01/2024 01:58, Lawrence D'Oliveiro wrote:
    On Sat, 06 Jan 2024 16:40:03 -0800, Keith Thompson wrote:

    C uses a "declaration follows usage" rule (though not with 100%
    consistency).

    And putting the function result before the argument types turns out to
    cause trouble when carried over to C++, when you try to express
    dependencies between them. So they had to add a Pascal-style alternative syntax, with the function result declared after the arguments.

    Even pointer dereferencing should have been done with a postfix, not a
    prefix operator. Consider why you need “->”: it’s purely syntactic sugar
    to make things like

    (*a).b

    less awkward as

    a->b

    Whereas in Pascal, for example, there is no need for any alternative
    syntax to

    a^.b

    There are two kinds of programming languages. There are ones that that
    exist long enough and are popular enough for people to see that the
    original design was not perfect and could have been done differently,
    and languages that die away to irrelevance before long. No one thinks
    the C way of doing things, or its syntax, is perfect - but a lot of
    people think it is good enough that they can live with it.

    A language has to either stick with the sub-optimal choices it made long
    ago, as C has done, or it can try to make changes and suffers from
    having to support new and old ideas, as C++ has done. Each technique
    has its advantages and disadvantages.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to David Brown on Sun Jan 7 15:34:58 2024
    On 07/01/2024 14:48, David Brown wrote:
    On 07/01/2024 01:58, Lawrence D'Oliveiro wrote:
    On Sat, 06 Jan 2024 16:40:03 -0800, Keith Thompson wrote:

    C uses a "declaration follows usage" rule (though not with 100%
    consistency).

    And putting the function result before the argument types turns out to
    cause trouble when carried over to C++, when you try to express
    dependencies between them. So they had to add a Pascal-style alternative
    syntax, with the function result declared after the arguments.

    Even pointer dereferencing should have been done with a postfix, not a
    prefix operator. Consider why you need “->”: it’s purely syntactic sugar
    to make things like

        (*a).b

    less awkward as

         a->b

    Whereas in Pascal, for example, there is no need for any alternative
    syntax to

         a^.b

    There are two kinds of programming languages.  There are ones that that exist long enough and are popular enough for people to see that the
    original design was not perfect and could have been done differently,
    and languages that die away to irrelevance before long.  No one thinks
    the C way of doing things, or its syntax, is perfect - but a lot of
    people think it is good enough that they can live with it.

    A language has to either stick with the sub-optimal choices it made long
    ago, as C has done, or it can try to make changes and suffers from
    having to support new and old ideas, as C++ has done.  Each technique
    has its advantages and disadvantages.

    I used to have that a^.b syntax (deref pointer then index).

    But for a few years I've relaxed that so that the deref is done
    automatically:

    a^.b becomes a.b
    a^[b] becomes a[b]
    a^(b) becomes a(b)

    AFAICS, C can could also relax the (*a).b or a->b synax so that you just
    do a.b. You could do that today, and nothing changes. (Of course it
    would need a compiler update).

    The others don't affect C so much: pointers to arrays, that would
    require (*a)[i], are rarely used. Everybody uses a[i] anyway with 'a'
    being a pointer to the first element.

    And it already allows, via some mysterious rules, for (*a)(b) to be
    written as a(b).

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to Keith Thompson on Sun Jan 7 15:26:23 2024
    On 07/01/2024 03:28, Keith Thompson wrote:
    Bart <bc@freeuk.cm> writes:


    You might call it metadata, but being a type doesn't come to
    mind. What would be the point of that; what could you do with that
    type?

    You could define objects of the type,

    Only if accessible. You say typeof isn't available.

    you could apply sizeof to it,

    Same here, but sizeof can also apply to the expression.

    you
    could define a type that points to it, or that's an array of it. You
    know, all the stuff you can normally do with a type

    My example stored 'with the variable' has an anonymous type. If might be something that only exists within the compiler, a generic 'VLA' type
    since the size is not known until runtime.

    It would be extraordinary for C to actually have tangible type objects
    at runtime since for me, types in C are a compile-time concept.

    But we're talking about a number.

    You claim that conceptually, that number is considered part of the type.



    Suppose you had a counted string variable S, currently set to "ABC" so
    that its length (which you say is naturally part its type) is 3.

    What exactly do you mean by a "counted string variable"? If you mean
    you have a variable whose current value is "ABC", and you can update it
    so its value becomes "ABCDEF", > then of course the count has to be associated with the object.

    It could be fixed or variable. But it's interesting that you consider
    the two to be different.

    Why can't you then consider a VLA that is capable of changing its size,
    but happens to keep the same one over its lifetime. Why should that then
    change that size from variable-associated to type-associated?

    typedef int rvla[rand() % 10 + 1];
    rvla A;
    rvla B;

    A and B are of the same type and therefore have the same length.

    I'm sorry, it's still bizarre to me. I consider a length to be part of a
    type when it's compile-time fixed value, /and is also defined with the
    type/, for example:

    type float vector[4];

    But never when the size is variable, whether that means determined at
    runtime and never changes, or determined at runtime but can grow. What
    might be recorded is the fact that the size is a variable quantity that
    needs further input.



    The
    obvious way to implement that would be to store the length in an
    anonymous object associated with the type. You're saying you'd expect
    the size be associated with A and with B, not with the type.

    Given:

    int row_count = 1000;
    int col_count = 2000;
    int vla_2d[row_count][col_count];

    you have 1000 rows of 2000 elements each. Would you expect to create
    1000 implicit objects, one for each row, each holding the value 2000?

    I don't know. I haven't implemented VLAs, and I don't have an equivalent feature at this level of language.

    At the next level up, if row_count and col_count are really not known
    until runtime (your example would be better off as enums), then yes,
    there would be 1000 rows, and each row is a 2000-element array
    containing its length.

    (Since in that language, each row can be a different length, or even a different type entirely.)

    What I would look at would be a special multi-dimensional type (say like
    a 2D slice), which stores 2 or more dimensions, and which would allow
    all the data to be allocated in a contiguous block.

    I don't support that right now. But don't feel so smug, because when I
    tried your example, it crashed.

    Since that data structure needs 8MB of stack. Here you could anticipate
    that; but often you can't. That's one big problem with VLAs.

    After scaling down the task to 100x200, then gcc and tcc gave me a size
    of 80,000. lccwin gave me 320,000.

    Meanwhile my dynamic language can effortlessly create that 1000 x 2000 x
    int32 data structure, even though it uses 8048048 bytes in all instead
    of C's 8000000 plus whatever overheads the VLA needs, assuming a big
    enough stack, or a smart enough implementation that will switch to heap
    as needed.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to Bart on Sun Jan 7 19:29:10 2024
    On Sun, 7 Jan 2024 12:14:24 +0000, Bart wrote:

    Is there much left for you to do?

    Yes. Make it work as though it were written for Python programmers.

    For example, the Cairo graphics API requires you to pass X- and Y-
    coordinates as separate arguments to every function. I wrap them up into a single “Vector” type, with its own arithmetic operators. For example, compare what you would have to do in C:

    x0 = 0;
    y0 = scope_radius;
    x1 = x0 * cos(- trace_width_angle) - y0 * sin(- trace_width_angle);
    y1 = x0 * sin(- trace_width_angle) + y0 * cos(- trace_width_angle);
    cairo_line_to(ctx, x1, y1);

    with what my Python wrapper allows:

    ctx.line_to(Vector(0, - scope_radius).rotate(- trace_width_angle))

    I had to write the code to do that.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to Lawrence D'Oliveiro on Sun Jan 7 22:41:11 2024
    On 07/01/2024 19:29, Lawrence D'Oliveiro wrote:
    On Sun, 7 Jan 2024 12:14:24 +0000, Bart wrote:

    Is there much left for you to do?

    Yes. Make it work as though it were written for Python programmers.

    For example, the Cairo graphics API requires you to pass X- and Y- coordinates as separate arguments to every function. I wrap them up into a single “Vector” type, with its own arithmetic operators. For example, compare what you would have to do in C:

    x0 = 0;
    y0 = scope_radius;
    x1 = x0 * cos(- trace_width_angle) - y0 * sin(- trace_width_angle);
    y1 = x0 * sin(- trace_width_angle) + y0 * cos(- trace_width_angle);
    cairo_line_to(ctx, x1, y1);

    with what my Python wrapper allows:

    ctx.line_to(Vector(0, - scope_radius).rotate(- trace_width_angle))

    I had to write the code to do that.

    I was interested in the FFI used by CPython as I'd never seen it in
    action. The setup, as used in your link, is something like this:

    cairo = ct.cdll.LoadLibrary(LIBNAME["cairo"]) # LIBNAME is a dict
    ...
    cairo.cairo_line_to.argtypes = (ct.c_void_p, ct.c_double, ct.c_double)
    cairo.cairo_line_to.restype = None
    ...

    c_double etc are from 'ctypes', and 'ct' is an alias for that module.

    So it's all done with libraries, classes, tuples and attributes.

    But so far, no need for LIBFFI; that's only needed when you call that
    function.

    In my dynamic language, the equivalent set up code is:

    type cairo_t = ref void # I guess cairo_t is opaque

    importdll cairo = # don't know the exact dll name
    ...
    proc cairo_line_to(cairo_t ctx, r64 x, y)
    ...
    end

    This is called as 'cairo_line_to(...)'. Actually the syntax used in this example is exactly the same as my static language.

    I'm using parameter names so that, with more elaborate functions, I can
    use keyword arguments. I can also define default values so that not all arguments need be supplied.

    In the dynamic version, both DLL and individual functions are loaded
    on-demand; that is, the first time a library or function is needed. The
    code runs even without the DLL if I don't call any functions from it.

    ---

    So, this is what /I/ do, provide simple, easy-to-use features that other languages make a bit of a meal over.

    From your library:

    def line_to(self, p) :
    ...
    #end line_to

    I see you have the same opinion of Python block syntax as I do. Nim does
    the same thing; I had to add exactly those #end comments as I kept
    getting things lined up wrong.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to Keith Thompson on Sun Jan 7 22:54:55 2024
    On 07/01/2024 22:41, Keith Thompson wrote:
    Bart <bc@freeuk.cm> writes:

    So, C has distinct concepts of 'zero-length' array and 'unbounded
    array', so that sizeof/sizeof on the latter generates a diagnostic.

    No, it doesn't. C doesn't have zero-length arrays. A fixed-length
    array with a length of 0:
    int arr[0];
    is a constraint violation.

    So you can't have an array with an empty initialiser list either?

    I must say you have to try quite hard to get a diagnostic for those!

    So what happens if you have a generated data block for example,
    containing N items; it only works when N is at least 1? What are you
    supposed to do when N is zero?

    It sounds quite a limitation.

    I don't know what you mean by "unbounded array".

    One without a bound specified? C might call it 'incomplete', even though
    the syntax is common; this is one example of many:

    void F(int A[]) {}

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to Bart on Sun Jan 7 23:27:19 2024
    On Sun, 7 Jan 2024 22:41:11 +0000, Bart wrote:

    I'm using parameter names so that, with more elaborate functions, I can
    use keyword arguments. I can also define default values so that not all arguments need be supplied.

    Python has all that as standard.

    But you can’t define custom overloads for operators as in Python, can you? That’s important to the definitions of my “Vector” and “Matrix” types,
    just for example.

    importdll cairo = # don't know the exact dll name

    On Linux, that‘s “libcairo.so.2”. Note the “.2” on the end. That gets incremented if there are any incompatible ABI changes made. Windows seems
    to have no mechanism for this.

    From your library:

    def line_to(self, p) :
    ...
    #end line_to

    I see you have the same opinion of Python block syntax as I do.

    I have custom Emacs commands to jump between lines with the same
    indentation level. That lets me move quickly between the beginning and
    ending of a nested block. I also have commands to jump to the beginning/
    end of the next outer indentation level.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to Keith Thompson on Mon Jan 8 01:32:20 2024
    On 07/01/2024 23:51, Keith Thompson wrote:
    Bart <bc@freeuk.cm> writes:

    Here's a small non-executable example:

    #include <stddef.h>

    size_t foo_size;
    size_t bar_size;

    void func(void) {
    int n = 42;
    typedef int vla[n];
    vla foo;
    vla bar;

    This isn't how VLAs are typically used. I think there are three kinds of
    people when it comes to VLAs:

    (1) Those who don't know about them and only inadvertently create
    them because the dimension is a variable not a compiler-time
    expression

    (2) Those who know about VLAs, but who will supply the dimensions to
    the variable declaration

    (3) A minority who know that VLA dimensions can also be used in
    typedefs.

    In the case of (2), what happens is that some storage is reserved for a
    size. A clever compiler can use one lot of storage if it realises
    multiple variables use that same size.

    But whatever it is, you saying that bit of storage is to do with the
    type; I'm saying it's to do with the instance of that type. In the end
    it probably doesn't matter.

    It does however still bother me that a mere typedef, not actually used
    for anything, could take up runtime resources.


    At the next level up, if row_count and col_count are really not known
    until runtime (your example would be better off as enums), then yes,
    there would be 1000 rows, and each row is a 2000-element array
    containing its length.

    If row_count and col_count were enums, they would be constants, and
    vla_2d would not be a VLA. The whole point is that we're talking about
    VLAs.

    This is actually an important point. Many examples of VLAs I've seen are exactly like your example, due to point (1) above.


    Yes, VLAs can result in stack overflows. So can ordinary fixed-length arrays.

    VLAs can do so much more easily:

    void F(int n) { int A[n];}

    What are the likely values of n? Without VLAs you have to knowingly use
    large fixed values of n, and/or rely on deep recursion, to get overflow.

    If you define a VLA that you know can't be longer than N
    elements, then that's no more dangerous that defining an ordinary array
    with a length of exactly N.

    The compiler likely doesn't know. Some compilers (like gcc on Windows)
    use a call like __checkstk() to allocate local storage which it either
    knows will exceed 4KB, or it might do (like a VLA, even if n turns out
    to be only 3).


    This is clearly a bug in lccwin. Feel free to report it to jacob navia and/or post to comp.compilers.lcc. I don't think he'd be interested in hearing about it from me.

    This is important too. If someone that experienced with implementing C
    has some trouble, then I've got no chance.

    (BTW why did VLAs become optional from C11?)

    Meanwhile my dynamic language [...]

    is irrelevant. If you want to discuss it, I suggest starting a thread
    in comp.lang.misc. I might even participate. (Your point in bringing
    up your own languages seems to be that other languages do some things
    better than C does.

    Actually I was pointing out a deficiency in both my languages in lack of support for multi-dimensional arrays of runtime dimensions, so that the
    data cannot be allocated in a single block.

    C apparently allows this in VLAs, and in passing such arrays to
    functions where the dimensions are provided via parameters.

    I consider this complex and out of place at this level of language.
    (Especially given there more basic features that are missing.)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Ike Naar@21:1/5 to Keith Thompson on Mon Jan 8 08:53:13 2024
    On 2024-01-07, Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:
    C *does not have* VLAs whose type is "determined at runtime but can
    grow". Logically associating the length of a VLA type with the type
    *works*.

    Here's a small non-executable example:

    #include <stddef.h>

    size_t foo_size;
    size_t bar_size;

    void func(void) {
    int n = 42;
    typedef int vla[n];
    vla foo;
    vla bar;
    foo_size = sizeof foo;
    bar_size = sizeof bar;
    }

    When I compile it with "gcc -S", I get assembly code that appears to
    store the value 42 just once:
    movl $42, -52(%rbp)
    and retrieves that value from the same place to copy it to foo_size and bar_size. (I'm not an expert in x86_64 assembly language, but I'm
    fairly sure that's what's going on.) Please take a look at the
    generated assembly code yourself, using any C compilers you like. Do
    any of them store the sizes of foo and bar separately? Why do you think
    it would be better to do so?

    The movl instruction most likely stores 42 in n.

    If sizeof (int) > 1, it's unlikely that 42 is copied to foo_size or bar_size, because (sizeof foo) and (sizeof bar) are (42 * sizeof (int)).

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Bart on Mon Jan 8 13:50:08 2024
    On 07/01/2024 16:34, Bart wrote:
    On 07/01/2024 14:48, David Brown wrote:
    On 07/01/2024 01:58, Lawrence D'Oliveiro wrote:
    On Sat, 06 Jan 2024 16:40:03 -0800, Keith Thompson wrote:

    C uses a "declaration follows usage" rule (though not with 100%
    consistency).

    And putting the function result before the argument types turns out to
    cause trouble when carried over to C++, when you try to express
    dependencies between them. So they had to add a Pascal-style alternative >>> syntax, with the function result declared after the arguments.

    Even pointer dereferencing should have been done with a postfix, not a
    prefix operator. Consider why you need “->”: it’s purely syntactic sugar
    to make things like

        (*a).b

    less awkward as

         a->b

    Whereas in Pascal, for example, there is no need for any alternative
    syntax to

         a^.b

    There are two kinds of programming languages.  There are ones that
    that exist long enough and are popular enough for people to see that
    the original design was not perfect and could have been done
    differently, and languages that die away to irrelevance before long.
    No one thinks the C way of doing things, or its syntax, is perfect -
    but a lot of people think it is good enough that they can live with it.

    A language has to either stick with the sub-optimal choices it made
    long ago, as C has done, or it can try to make changes and suffers
    from having to support new and old ideas, as C++ has done.  Each
    technique has its advantages and disadvantages.

    I used to have that a^.b syntax (deref pointer then index).

    But for a few years I've relaxed that so that the deref is done automatically:

        a^.b     becomes    a.b
        a^[b]    becomes    a[b]
        a^(b)    becomes    a(b)

    AFAICS, C can could also relax the (*a).b or a->b synax so that you just
    do a.b. You could do that today, and nothing changes. (Of course it
    would need a compiler update).

    You could, in the sense that (AFAICS) there would be no situation where
    in code today "a.b" and "a->b" were both syntactically and semantically
    correct but meant different things. Then you could have a compiler
    treat the syntax or constraint error "a.b" as intending to mean "a->b".

    I don't think it would be a good idea - I think it just adds confusion
    because you easily lose track of what are structs and what are pointers
    to structs. I'd rather it be an error when you get these wrong in the
    code. I remember from programming in Delphi (and Borland Object Pascal)
    it could often be hard to figure out what was a pointer to an object,
    and what was a "real" object, since dereferencing was automatic in some circumstances. My personal preference is either to say that everything
    is always a reference (like Python), or everything is always a value
    (like C) and do the dereferencing explicitly. Other people make think
    such automatic dereferencing is a good idea, but I personally don't.


    The others don't affect C so much: pointers to arrays, that would
    require (*a)[i], are rarely used. Everybody uses a[i] anyway with 'a'
    being a pointer to the first element.

    And it already allows, via some mysterious rules, for (*a)(b) to be
    written as a(b).


    Think of it rather as C allows you to write function calls like
    "foo(x)", and that considering function names as being function pointers
    is a natural view that is easy to implement in compilers and keeps the C
    to assembly conversion as lean as possible - it means "foo" is the
    address of the function, rather than being the function itself. Being
    able to write "foo(x)" as "(*foo)(x)" is just a byproduct of this - it
    would need extra rules added to C to disallow it.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to Keith Thompson on Mon Jan 8 13:28:30 2024
    On 08/01/2024 04:35, Keith Thompson wrote:
    Bart <bc@freeuk.cm> writes:

    It does however still bother me that a mere typedef, not actually used
    for anything, could take up runtime resources.

    It's not the typedef that takes up runtime resources. It's the array
    type to which the typedef refers.

    Consider this:
    printf("%zu\n", sizeof (int[rand() % 10 } 1]);

    (Tested as:) printf("%zu\n", sizeof (int[rand() % 10 + 1]));

    That's a perfectly valid usage.

    I don't consider it a reasonable one.

    At least I've learnt some things from this thread:

    * Within an elaborate type-chain of pointers and arrays, VLA
    dimensions need to be managed by the impementation at each point
    along the chain.

    * But actual VLA data allocations are only managed at the top level.
    That's if an actual object is created at all. VLA data further
    along (eg. as a pointer target) is still managed by the programmer.

    So, to switch to LTR declarations for a second as C's syntax is still
    beyond me, here:

    [x][y]**[z]int A # array x of array x of ptr to ptr to array z ..

    If x,y,z were all variable dimensions, then the [x][y] is allocated
    for you, but not the [z]. However all x,y,z sizes are stored.

    * I'm never going to implement VLAs as defined by C, as I don't
    agree with how they work, even if I fully understood them. Your
    example demonstrates that well.

    Actually I can't even emulate your example with my higher level
    scripting language. It's just not a good fit for such a lower level one.


    Earlier in this thread, you seemed to have the misconception that the
    length of a VLA object could change during the object's lifetime.

    No; you made a remark that if it could change, then you'd agree the
    dimension could switch from being stored from the type, to the instance.

    I suggested, why not pretend the dimension could change (even it you had
    no intention of that), then storing dimensions with the variable could
    work just as well.

    So don't do that.

    Was it you who keeps saying that or is someone else?

    It is akin to saying: 'So don't have any bugs'. VLAs /do/ make it more
    likely to have stack overflows.


    An int parameter can have a value up to INT_MAX. If you don't want a
    stack overflow, then *write your code* so that n can't be too big.

    malloc() can take any value up to SIZE_MAX.

    * Typical heap space might be 1000 times bigger than stack space

    * Asking for too much heap doesn't cause a crash; it returns a failure
    code

    * You can check whether the allocation was successful then decide what
    to do next, including making a graceful exit


    Don't write code that
    can actually attempt to allocate that that much memory.

    This is interesting:

    unsigned long long int n=SIZE_MAX;
    int A[n];

    printf("%zu\n",sizeof(A));
    printf("%zu\n",SIZE_MAX);

    This outputs (with gcc):

    18446744073709551612
    18446744073709551615

    It goes wrong if you try to write to A beyond whatever the stack size is.

    No chance of what? Were you planning to implement C VLAs? You don't
    seem to understand them well enough to use them, let alone to implement
    them.

    EXACTLY. And I'm a fairly experienced compiler writer of this level of language. How many C users do you think understand all the ins and outs?


    I seriously urge you to contact jacob navia, the author and maintainer
    of lcc-win, and let him know about this bug.

    I doubt it is still under active development. I'm also sure he would
    have heard about any problems by now.

    It's not even clear if it is actually wrong in your example other than
    using more memory than expected. Look at the output of the gcc example
    above: what's with the missing 3 bytes?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Bart on Mon Jan 8 16:00:58 2024
    On 08/01/2024 02:32, Bart wrote:
    On 07/01/2024 23:51, Keith Thompson wrote:
    Bart <bc@freeuk.cm> writes:

    Here's a small non-executable example:

    #include <stddef.h>

    size_t foo_size;
    size_t bar_size;

    void func(void) {
         int n = 42;
         typedef int vla[n];
         vla foo;
         vla bar;

    This isn't how VLAs are typically used. I think there are three kinds of people when it comes to VLAs:

    (1) Those who don't know about them and only inadvertently create
        them because the dimension is a variable not a compiler-time
        expression

    (2) Those who know about VLAs, but who will supply the dimensions to
        the variable declaration

    (3) A minority who know that VLA dimensions can also be used in
        typedefs.


    I would have thought that a majority of C programmers know that an array
    size can be used in a typedef. If they also know that array sizes can
    be non-constant (not every C programmer knows VLAs exist), it seems a
    small step to know that you can use typedef with VLAs.

    I think quite a lot of VLAs are of sizes that are known at compile time,
    but are not "constants" according to C's strict definitions. For example :

    void foo(void) {
    const int rows = 10;
    const int cols = 20;
    const int entries = rows * cols;
    int data[entries];
    }

    "data" is a VLA in C - yet its dimension can be fully established at compile-time. (It would be legal in C++, where there are no C-style
    VLAs but where a wider range of things can be view as "constant enough"
    to be the size of an array.)

    If I want to make a VLA of size based on a variable, I'll define it
    directly. If I want to make several VLA's of the same size, I might use
    a typedef - or I might define them all directly, whatever is (IMHO)
    clearest in the code. I don't expect the compiler to treat them
    differently. Far and away the most common situation, I think, is that
    you have just one VLA.

    In the case of (2), what happens is that some storage is reserved for a
    size. A clever compiler can use one lot of storage if it realises
    multiple variables use that same size.


    An even cleverer compiler usually doesn't need to store the size at all, anywhere. A typical real-world implementation of a VLA like "T xs[n]"
    will mean preserving the current stack pointer, then subtracting
    sizeof(T) * n. The new stack pointer is the address of xs, and if you
    need its size, it is available as "n". It is only if the variable "n"
    is later changed - which would likely be a silly way to write your code,
    but people do silly things - that the compiler has to preserve the
    original "n" in some way. But it will do that as though you had written
    "const sizeof_t original_n = n;" at the VLA definition. Storing on the
    stack, or in a register, or combining with other things, is just part of
    the daily grind for a compiler, optimised as well or as badly as it does
    for anything else.

    You seem to imagine this is all special, and complicated, and difficult,
    full of corner cases. It is not - it needs nothing more than a local
    constant variable that is hidden from the user. Everything else is
    normal compiler work. (Optimising register allocation, stack slot
    usage, variable lifetimes, etc., - /that/ is hard work. Adding an
    another constant variable to the function is not.)

    But whatever it is, you saying that bit of storage is to do with the
    type; I'm saying it's to do with the instance of that type. In the end
    it probably doesn't matter.

    It doesn't matter to the VLA user, certainly. And since each variable
    has a type, the compiler could attach information about the type to each instance of that type - but it would probably be an unhelpful way to
    think about things.

    After all, having types that are anonymous and only ever used once is
    not new in C :

    struct { int a; int b; } x;
    struct { int a; int b; } y;

    x and y are different types, used only once, and with no way to refer to
    the type again (until C23 "typeof").


    It does however still bother me that a mere typedef, not actually used
    for anything, could take up runtime resources.

    That's a matter of implementation quality. Lots of things that are not actually used can take up runtime resources, from debug information
    taking up run-time memory to unoptimised code that does calculations or
    has variables or stage usage that never actually affects the outcome of
    the program.



    At the next level up, if row_count and col_count are really not known
    until runtime (your example would be better off as enums), then yes,
    there would be 1000 rows, and each row is a 2000-element array
    containing its length.

    If row_count and col_count were enums, they would be constants, and
    vla_2d would not be a VLA. The whole point is that we're talking about
    VLAs.

    This is actually an important point. Many examples of VLAs I've seen are exactly like your example, due to point (1) above.


    I agree that many uses of VLA's are actually with sizes that are known
    at compile time. A good compiler will not need to store the size
    anywhere, a less optimising compiler might store the size somewhere.

    (Since you are not trying to make a conforming compiler, you could quite reasonably allow such VLA's, treating them identically to normal arrays,
    while disallowing VLA's whose size is not known until compile time.)



    Yes, VLAs can result in stack overflows.  So can ordinary fixed-length
    arrays.

    VLAs can do so much more easily:

        void F(int n) { int A[n];}

    What are the likely values of n? Without VLAs you have to knowingly use
    large fixed values of n, and/or rely on deep recursion, to get overflow.


    This is a myth that is regularly trotted out by people who, for unknown reasons, don't like VLAs. They pretend that somehow heap allocation is
    "safer" because malloc will return 0 if an allocation fails, while VLA's
    have no such mechanism. In reality, if you were to write "malloc(n)"
    with no idea what "n" might be, your program is as hopelessly unsound as
    one that writes "int A[n];" with no idea what "n" might be. If you are
    writing a good, safe program, you know the range for "n", and know
    whether it makes sense for a VLA. Sanitizing your inputs is one of the
    first lessons in programming. So please, let this be the last time this
    silly complaint about VLAs turns up.

    If you define a VLA that you know can't be longer than N
    elements, then that's no more dangerous that defining an ordinary array
    with a length of exactly N.

    The compiler likely doesn't know. Some compilers (like gcc on Windows)
    use a call like __checkstk() to allocate local storage which it either
    knows will exceed 4KB, or it might do (like a VLA, even if n turns out
    to be only 3).


    Keith wrote "that /you/ know can't be longer than N". The programmer is responsible for writing correct code, not the compiler.


    This is clearly a bug in lccwin.  Feel free to report it to jacob navia
    and/or post to comp.compilers.lcc.  I don't think he'd be interested in
    hearing about it from me.

    This is important too. If someone that experienced with implementing C
    has some trouble, then I've got no chance.

    /You/ are experienced in implementing C. But both you and Jacob suffer
    from the same problem here - you are trying to do everything yourself.
    Most people understand that they can make mistakes or misunderstanding,
    and for something as advanced and complicated as a compiler, it is
    usually better to be more than one person. Jacob is a smart guy, but he
    is only one person, and only human - mistakes in his tools are not a
    surprise.


    (BTW why did VLAs become optional from C11?)

    I don't know for sure. But I do know that not all C implementations use
    a stack, and for some targets a "true VLA" (as distinct from a VLA where
    the size is known at compile time) would be extremely inefficient to
    implement. It is possible that this has something to do with it.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to David Brown on Mon Jan 8 15:53:09 2024
    On 08/01/2024 12:50, David Brown wrote:
    On 07/01/2024 16:34, Bart wrote:

    I used to have that a^.b syntax (deref pointer then index).

    But for a few years I've relaxed that so that the deref is done
    automatically:

         a^.b     becomes    a.b
         a^[b]    becomes    a[b]
         a^(b)    becomes    a(b)

    AFAICS, C can could also relax the (*a).b or a->b synax so that you
    just do a.b. You could do that today, and nothing changes. (Of course
    it would need a compiler update).

    You could, in the sense that (AFAICS) there would be no situation where
    in code today "a.b" and "a->b" were both syntactically and semantically correct but meant different things.  Then you could have a compiler
    treat the syntax or constraint error "a.b" as intending to mean "a->b".

    I don't think it would be a good idea - I think it just adds confusion because you easily lose track of what are structs and what are pointers
    to structs.

    Yet this is exactly what happens with those other examples: you don't
    know if the X in X[i] has type T[] or T*. (The use of (*X)[i] when X is
    of type T(*)[] is rare.)

    And you don't know if the F in F(x) is an actual function, or a pointer
    to a function.

    The "->" alternate is anyway a little strange:

    (*P).m can be written as P->m
    (**Q).m can only be reduced to (*Q)->m

    So it only works on the last lot of indirection. There is also no
    euivalent of just (*P), "->" needs to specify a member name as it
    combines two operations.

    I'd rather it be an error when you get these wrong in the
    code.

    I had the same misgivings: there is a loss of transparency, but after I
    started using the auto-deref, the benefits outweighed that:

    Code was remarkably free of clutter. (And in my case, I had sections of
    code that could often be ported as-is to/from my other language that
    didn't need those derefs.)

      My personal preference is either to say that everything
    is always a reference (like Python), or everything is always a value
    (like C) and do the dereferencing explicitly.  Other people make think
    such automatic dereferencing is a good idea, but I personally don't.

    This can occur with reference parameters too: I believe you get the same
    thing in C++.



    The others don't affect C so much: pointers to arrays, that would
    require (*a)[i], are rarely used. Everybody uses a[i] anyway with 'a'
    being a pointer to the first element.

    And it already allows, via some mysterious rules, for (*a)(b) to be
    written as a(b).


    Think of it rather as C allows you to write function calls like
    "foo(x)", and that considering function names as being function pointers
    is a natural view that is easy to implement in compilers and keeps the C
    to assembly conversion as lean as possible - it means "foo" is the
    address of the function, rather than being the function itself.  Being
    able to write "foo(x)" as "(*foo)(x)" is just a byproduct of this - it
    would need extra rules added to C to disallow it.

    It's worse than that. Given an actual function:

    void F(void){}

    all of these calls are valid:

    (&F)();
    F();
    (*F)();
    (**F)();
    (***F)();
    (****F)(); // etc

    across the half-dozen compilers I tried. Except for my 'mcc' which only
    allows up to (*F), and not (**F)() or beyond. I just thought it was silly.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to David Brown on Mon Jan 8 18:02:21 2024
    On 08/01/2024 15:00, David Brown wrote:
    On 08/01/2024 02:32, Bart wrote:
    On 07/01/2024 23:51, Keith Thompson wrote:
    Bart <bc@freeuk.cm> writes:

    Here's a small non-executable example:

    #include <stddef.h>

    size_t foo_size;
    size_t bar_size;

    void func(void) {
         int n = 42;
         typedef int vla[n];
         vla foo;
         vla bar;

    This isn't how VLAs are typically used. I think there are three kinds
    of people when it comes to VLAs:

    (1) Those who don't know about them and only inadvertently create
         them because the dimension is a variable not a compiler-time
         expression

    (2) Those who know about VLAs, but who will supply the dimensions to
         the variable declaration

    (3) A minority who know that VLA dimensions can also be used in
         typedefs.


    I would have thought that a majority of C programmers know that an array
    size can be used in a typedef.


    Even that, having a fixed size array as a type, would be unusual, and
    typically used for small arrays with special uses, for example:

    typedef float vector[4];
    typedef float matrix[4][4];
    typedef quad byte[4]; // an actual one of mine

    I can't even think of a use for such an array to have a size known at
    runtime, unless somebody does this:

    const int size = 4;
    typedef float vector[size];
    typedef float matrix[size][size];

    (How many even know that you can typedef an actual function, not just a function pointer:

    #include <stdio.h>

    typedef int Op(int a, int b);

    Op add {return a+b;}
    Op sub {return a-b;}
    Op mul {return a*b;}
    Op div {return a/b;}

    int main(void) {
    printf("%d\n", add(2, mul(3,4)));
    }

    Here, most compilers accept that typedef. However only two allow you to
    use that typedef in the way shown, to provide a common template for a
    function signature: Tiny C, and mine. Those produce a program that
    displays 14.)
    An even cleverer compiler usually doesn't need to store the size at all, anywhere.  A typical real-world implementation of a VLA like "T xs[n]"
    will mean preserving the current stack pointer, then subtracting
    sizeof(T) * n.

    That seems simple enough with one VLA in a function, and well structured
    flow.

    In practice there could be a dozen active VLAs, there could be loops so
    that some VLAs are destroyed and recreated as a different size; there
    could be gotos in and out of blocks [jump into a VLA scope is an error;
    jumping out isn't, but a compiler needs to detect all this], early
    returns and so on.

    (Optimising register allocation, stack slot
    usage, variable lifetimes, etc., - /that/ is hard work.  Adding an
    another constant variable to the function is not.)

    That's also optional; you can make that as complex or simple as you
    like. With a VLA the options are fewer.

    (Since you are not trying to make a conforming compiler, you could quite reasonably allow such VLA's, treating them identically to normal arrays, while disallowing VLA's whose size is not known until compile time.)

    Any VLAs I might implement would use heap allocation (but that
    introduces other matters of implicit calls to support functions that I
    would prefer to keep out of a C implementation).

    What are the likely values of n? Without VLAs you have to knowingly
    use large fixed values of n, and/or rely on deep recursion, to get
    overflow.


    This is a myth that is regularly trotted out by people who, for unknown reasons, don't like VLAs.  They pretend that somehow heap allocation is "safer" because malloc will return 0 if an allocation fails, while VLA's
    have no such mechanism.  In reality, if you were to write "malloc(n)"
    with no idea what "n" might be, your program is as hopelessly unsound as
    one that writes "int A[n];" with no idea what "n" might be.

    Except that the memory that malloc can draw on can be 1000 times larger
    than stack memory.

    In an earlier example, creating a VLA with int A[SIZE_MAX] didn't fail
    (it would only do so if trying to write beyond the stack size), but malloc(SIZE_MAX) did return NULL.

    Keith wrote "that /you/ know can't be longer than N".  The programmer is responsible for writing correct code, not the compiler.

    Say you write a library function that looks like this:

    int F(int n) {
    int A[n];
    ....

    or like this:

    bool G(char* s) {
    int A[strlen(s)];
    ....


    You don't know who or what will call your function; what checks would
    you insert?

    What should your docs say about the range of n? You'd want the range to
    be as high as possible, or to work with as long strings as possible.

    At the same time, you want them to be efficient when n or strlen(s) is
    small, which may be most of the time; you don't want the extra overheads
    of VLAs, and on gcc-Windows, there can be extra overheads.

    (My solution to those examples is extra code to either use a small, fixed-length array, or use the heap, depending on N.)


    /You/ are experienced in implementing C.  But both you and Jacob suffer
    from the same problem here - you are trying to do everything yourself.

    Above you say that VLAs are simple to implement. Here you suggest it
    might be too much for an individual.

    Although two persons who don't know how it works won't help! You
    probably mean looking for existing solutions and implementations from
    somebody who eventually figured it out.

    I prefer language features that don't present difficulties. The only
    open-ended aspect of compilation I will acknowledge, is back-end
    optimisation. And I said that there, you can go as far as you like.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to Keith Thompson on Mon Jan 8 18:55:59 2024
    On 08/01/2024 18:25, Keith Thompson wrote:
    Bart <bc@freeuk.cm> writes:

    This is interesting:

    unsigned long long int n=SIZE_MAX;
    int A[n];

    printf("%zu\n",sizeof(A));
    printf("%zu\n",SIZE_MAX);

    This outputs (with gcc):

    18446744073709551612
    18446744073709551615

    It goes wrong if you try to write to A beyond whatever the stack size is.

    But if you don't want to contact him, I'll do it myself. I'll post to comp.compilers.lcc, which he followed as of a few years ago. I can
    credit you for finding the bug or leave you out of it, whichever you
    prefer.

    Best not to mention me; I'm already well-known to him as a bug-finder.


    It's not even clear if it is actually wrong in your example other than
    using more memory than expected. Look at the output of the gcc example
    above: what's with the missing 3 bytes?

    What missing 3 bytes? The gcc output looked correct to me.

    Look carefully at the last digit. It should be '5' for SIZE_MAX, but
    sizeof() reports a value with '2' at the end.

    This is a complete program:


    #include <stdio.h>
    #include <stdint.h>

    int main(void) {
    unsigned long long int n=SIZE_MAX;
    int A[n];

    printf("%zu\n",sizeof(A));
    printf("%zu\n",SIZE_MAX);
    }

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to Bart on Mon Jan 8 19:01:21 2024
    On 08/01/2024 18:55, Bart wrote:
    On 08/01/2024 18:25, Keith Thompson wrote:
    Bart <bc@freeuk.cm> writes:

    This is interesting:

         unsigned long long int n=SIZE_MAX;
         int A[n];

         printf("%zu\n",sizeof(A));
         printf("%zu\n",SIZE_MAX);

    This outputs (with gcc):

         18446744073709551612
         18446744073709551615

    It goes wrong if you try to write to A beyond whatever the stack size
    is.

    But if you don't want to contact him, I'll do it myself.  I'll post to
    comp.compilers.lcc, which he followed as of a few years ago.  I can
    credit you for finding the bug or leave you out of it, whichever you
    prefer.

    Best not to mention me; I'm already well-known to him as a bug-finder.


    It's not even clear if it is actually wrong in your example other than
    using more memory than expected. Look at the output of the gcc example
    above: what's with the missing 3 bytes?

    What missing 3 bytes?  The gcc output looked correct to me.

    Look carefully at the last digit. It should be '5' for SIZE_MAX, but
    sizeof() reports a value with '2' at the end.

    Never mind. A is an int array not a char array. So it's size would be 4*SIZE_MAX; that apparently gives the given value when limited to 64 bits.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Bart on Mon Jan 8 20:50:37 2024
    On 08/01/2024 16:53, Bart wrote:
    On 08/01/2024 12:50, David Brown wrote:
    On 07/01/2024 16:34, Bart wrote:

    I used to have that a^.b syntax (deref pointer then index).

    But for a few years I've relaxed that so that the deref is done
    automatically:

         a^.b     becomes    a.b
         a^[b]    becomes    a[b]
         a^(b)    becomes    a(b)

    AFAICS, C can could also relax the (*a).b or a->b synax so that you
    just do a.b. You could do that today, and nothing changes. (Of course
    it would need a compiler update).

    You could, in the sense that (AFAICS) there would be no situation
    where in code today "a.b" and "a->b" were both syntactically and
    semantically correct but meant different things.  Then you could have
    a compiler treat the syntax or constraint error "a.b" as intending to
    mean "a->b".

    I don't think it would be a good idea - I think it just adds confusion
    because you easily lose track of what are structs and what are
    pointers to structs.

    Yet this is exactly what happens with those other examples: you don't
    know if the X in X[i] has type T[] or T*. (The use of (*X)[i] when X is
    of type T(*)[] is rare.)

    And you don't know if the F in F(x) is an actual function, or a pointer
    to a function.

    The "->" alternate is anyway a little strange:

       (*P).m   can be written as  P->m
       (**Q).m  can only be reduced to (*Q)->m

    So it only works on the last lot of indirection. There is also no
    euivalent of just (*P), "->" needs to specify a member name as it
    combines two operations.

    Yes, the short-cut only works for the (by far) most common case.


    I'd rather it be an error when you get these wrong in the code.

    I had the same misgivings: there is a loss of transparency, but after I started using the auto-deref, the benefits outweighed that:

    Code was remarkably free of clutter. (And in my case, I had sections of
    code that could often be ported as-is to/from my other language that
    didn't need those derefs.)


    I didn't like automatic dereferencing (but I could live with it -
    overall I found Delphi a very productive tool). But that's a
    preference, and it is no surprise that other people have different
    preferences.

      My personal preference is either to say that everything is always a
    reference (like Python), or everything is always a value (like C) and
    do the dereferencing explicitly.  Other people make think such
    automatic dereferencing is a good idea, but I personally don't.

    This can occur with reference parameters too: I believe you get the same thing in C++.

    Not quite - that's an easy and common misunderstanding. (It is even
    more understandable for you, since your language uses "ref" to mean what
    is called a "pointer" in C and C++.) References in C++ are not "auto-dereferenced pointers" - they are alternative names for objects.
    It is better to think of references as being ways to identify objects,
    and pointers as being indirect references. C++ references are /not/
    pointers.




    The others don't affect C so much: pointers to arrays, that would
    require (*a)[i], are rarely used. Everybody uses a[i] anyway with 'a'
    being a pointer to the first element.

    And it already allows, via some mysterious rules, for (*a)(b) to be
    written as a(b).


    Think of it rather as C allows you to write function calls like
    "foo(x)", and that considering function names as being function
    pointers is a natural view that is easy to implement in compilers and
    keeps the C to assembly conversion as lean as possible - it means
    "foo" is the address of the function, rather than being the function
    itself.  Being able to write "foo(x)" as "(*foo)(x)" is just a
    byproduct of this - it would need extra rules added to C to disallow it.

    It's worse than that.

    No, it's not worse - it's just a side-effect of the convenience of
    notation. Disallowing some of the forms you show below would require
    extra rules, adding complication to the standards, while allowing them
    is harmless (since people would not use them in code).

    Given an actual function:

        void F(void){}

    all of these calls are valid:

        (&F)();
        F();
        (*F)();
        (**F)();
        (***F)();
        (****F)();  // etc

    across the half-dozen compilers I tried. Except for my 'mcc' which only allows up to (*F), and not (**F)() or beyond. I just thought it was silly.


    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Keith Thompson on Mon Jan 8 21:41:11 2024
    On 08/01/2024 19:32, Keith Thompson wrote:
    David Brown <david.brown@hesbynett.no> writes:
    On 08/01/2024 02:32, Bart wrote:
    [...]
    (BTW why did VLAs become optional from C11?)

    I don't know for sure. But I do know that not all C implementations
    use a stack, and for some targets a "true VLA" (as distinct from a VLA
    where the size is known at compile time) would be extremely
    inefficient to implement. It is possible that this has something to
    do with it.

    I'm skeptical that that's the reason. Almost all C implementations do
    use a "stack", in the sense of a contiguously allocated region of memory
    in which automatic objects are allocated, with addresses uniformly
    increasing or decreasing for new allocations. (All C implementations
    use a "stack" in the sense of a last-in/first-out data structure.) I'm
    not aware that any implementers of non-stack implementations objected to VLAs. For that matter, I don't know why VLAs would be extremely
    inefficient in such an implementation. They need to have the ability to allocate new stack frames anyway, and determining the allocation size at
    run time shouldn't be a huge burden.

    I've seen suggestions that the intent was to make it possible to create conforming C implementations for small embedded processors. That might
    make sense, but it could have been addressed by making VLAs optional
    only for freestanding implementations.

    That would have been a little odd, as the differences between
    freestanding and hosted requirements are almost all a matter of library support. But it is certainly possible.


    I think Microsoft has chosen not to implement VLAs in their C compiler.


    This is also entirely possible. The C (and C++) standards have a some
    absolute piddle as a result of trying to please Microsoft (such as the
    madness of so many different character types, or Microsoft's attempts at
    "safe" library functions in C).

    But as I say, I don't know for sure - your guess is at least as good as
    mine!

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Bart on Mon Jan 8 21:36:31 2024
    On 08/01/2024 19:02, Bart wrote:
    On 08/01/2024 15:00, David Brown wrote:
    On 08/01/2024 02:32, Bart wrote:
    On 07/01/2024 23:51, Keith Thompson wrote:
    Bart <bc@freeuk.cm> writes:

    Here's a small non-executable example:

    #include <stddef.h>

    size_t foo_size;
    size_t bar_size;

    void func(void) {
         int n = 42;
         typedef int vla[n];
         vla foo;
         vla bar;

    This isn't how VLAs are typically used. I think there are three kinds
    of people when it comes to VLAs:

    (1) Those who don't know about them and only inadvertently create
         them because the dimension is a variable not a compiler-time
         expression

    (2) Those who know about VLAs, but who will supply the dimensions to
         the variable declaration

    (3) A minority who know that VLA dimensions can also be used in
         typedefs.


    I would have thought that a majority of C programmers know that an
    array size can be used in a typedef.


    Even that, having a fixed size array as a type, would be unusual, and typically used for small arrays with special uses, for example:

        typedef float vector[4];
        typedef float matrix[4][4];
        typedef quad byte[4];                   // an actual one of mine

    I can't even think of a use for such an array to have a size known at runtime, unless somebody does this:

        const int size = 4;
        typedef float vector[size];
        typedef float matrix[size][size];


    That all seems fine to me - now you have typedefs of VLAs.

    (How many even know that you can typedef an actual function, not just a function pointer:

        #include <stdio.h>

        typedef int Op(int a, int b);

        Op add {return a+b;}
        Op sub {return a-b;}
        Op mul {return a*b;}
        Op div {return a/b;}

        int main(void) {
            printf("%d\n", add(2, mul(3,4)));
        }

    Here, most compilers accept that typedef.

    There's nothing wrong with the typedef, and I'd be surprised to see a
    compiler that did not accept it even if the compiler did not claim to be
    a full C compiler. Some people (such as myself) prefer to typedef
    function pointer types, others prefer to typedef function types and
    declare pointers to those typedefs. Each is valid, and it is a
    stylistic choice.

    However only two allow you to
    use that typedef in the way shown, to provide a common template for a function signature: Tiny C, and mine. Those produce a program that
    displays 14.)

    Such compilers would be non-compliant - there is nothing in the syntax
    and constraints of C that would allow such a syntax. In fact, under
    6.9.1 "Function definitions", there is a footnote saying explicitly that
    this is wrong :

    typedef int F(void);

    F f { /* */ } // WRONG: syntax/constraint error

    (Again, the typedef is fine - the use of it in a function definition is
    not.)

    "Op add, sub, mult, div;" as a declaration of the functions is fine.

    So whoever wrote those two compilers either failed to do even a basic
    reading of fundamental parts of the C standards, or decided
    intentionally that this would be a useful extension to add to their
    compiler.


    An even cleverer compiler usually doesn't need to store the size at
    all, anywhere.  A typical real-world implementation of a VLA like "T
    xs[n]" will mean preserving the current stack pointer, then
    subtracting sizeof(T) * n.

    That seems simple enough with one VLA in a function, and well structured flow.


    The most common case.

    In practice there could be a dozen active VLAs, there could be loops so
    that some VLAs are destroyed and recreated as a different size; there
    could be gotos in and out of blocks [jump into a VLA scope is an error; jumping out isn't, but a compiler needs to detect all this], early
    returns and so on.

    It all fits in the same way. Complicated structure would hinder
    compiler optimisations such as combining stack manipulation. But
    there's nothing to stop the compiler treating:

    {
    ...
    T xs[n];
    ...
    }

    as roughly :

    {
    ...

    const sizeof_t n_for_xs = n;
    const void * old_sp = SP;
    SP -= (n_for_xs * sizeof(T));
    T * xs = SP;

    ...
    SP = old_sp;
    }

    If there are a dozen VLA's (/highly/ unlikely), do this a dozen times. Everything else, including these new secret variables, works as normal.

    I agree that gotos could potentially cause trouble here. Fortunately,
    the C standards committee thought so too, and "A goto statement shall
    not jump from outside the scope of an identifier having a variably
    modified type to inside the scope of that identifier." (6.8.6.1p1).



    (Optimising register allocation, stack slot usage, variable lifetimes,
    etc., - /that/ is hard work.  Adding an another constant variable to
    the function is not.)

    That's also optional; you can make that as complex or simple as you
    like. With a VLA the options are fewer.

    (Since you are not trying to make a conforming compiler, you could
    quite reasonably allow such VLA's, treating them identically to normal
    arrays, while disallowing VLA's whose size is not known until compile
    time.)

    Any VLAs I might implement would use heap allocation (but that
    introduces other matters of implicit calls to support functions that I
    would prefer to keep out of a C implementation).


    Putting VLA's on the heap would be a strange idea that would run
    contrary to people's expectations (though I think the standards would
    allow it - after all, the C standards don't require a stack at all).
    Still, as you are the only user, that's up to you and your own preferences.

    What are the likely values of n? Without VLAs you have to knowingly
    use large fixed values of n, and/or rely on deep recursion, to get
    overflow.


    This is a myth that is regularly trotted out by people who, for
    unknown reasons, don't like VLAs.  They pretend that somehow heap
    allocation is "safer" because malloc will return 0 if an allocation
    fails, while VLA's have no such mechanism.  In reality, if you were to
    write "malloc(n)" with no idea what "n" might be, your program is as
    hopelessly unsound as one that writes "int A[n];" with no idea what
    "n" might be.

    Except that the memory that malloc can draw on can be 1000 times larger
    than stack memory.

    That means nothing at all. If you have got your "n" from some random
    unchecked and unsanitized input, it's madness to use it - a factor of
    1000 won't save you.


    In an earlier example, creating a VLA with int A[SIZE_MAX] didn't fail
    (it would only do so if trying to write beyond the stack size), but malloc(SIZE_MAX) did return NULL.

    Keith wrote "that /you/ know can't be longer than N".  The programmer
    is responsible for writing correct code, not the compiler.

    Say you write a library function that looks like this:

        int F(int n) {
            int A[n];
            ....


    Let's say that I would not do so, unless it were appropriate for me to
    specify that the function only has defined behaviour for certain ranges
    of "n". Then the caller has full responsibility for making sure "n" is appropriate, and if there is a crash from a bad "n", it is their fault
    alone.

    or like this:

        bool G(char* s) {
            int A[strlen(s)];
            ....


    The same applies here. (There's a reason why people often prefer
    memchr() to strlen() when the input is not known to be safe.)


    You don't know who or what will call your function; what checks would
    you insert?

    I specify my functions. It is up to users to use them correctly,
    according to the specifications - or the program will have undefined
    behaviour. If I suspect that the users might be newbies, or incompetent programmers, and the functions don't have to be optimally efficient,
    then I might put in extra checks to ensure that the parameters are safe
    and crash with debug information if not (i.e., I'd use something like an assert()), if such checks are feasible.


    What should your docs say about the range of n? You'd want the range to
    be as high as possible, or to work with as long strings as possible.

    Why do you think that? It depends entirely on what the function does.
    There is no range of "n" that would be suitable for all situations for a stack-allocated VLA. Equally, there is no range of "n" that would be
    suitable for all situations with heap-allocated data.


    At the same time, you want them to be efficient when n or strlen(s) is
    small, which may be most of the time; you don't want the extra overheads
    of VLAs, and on gcc-Windows, there can be extra overheads.

    (My solution to those examples is extra code to either use a small, fixed-length array, or use the heap, depending on N.)


    /You/ are experienced in implementing C.  But both you and Jacob
    suffer from the same problem here - you are trying to do everything
    yourself.

    Above you say that VLAs are simple to implement. Here you suggest it
    might be too much for an individual.

    No, I think they should - at least most of the time - be simple to
    implement. But there might be odd cases that are challenging, and if particular individuals find them difficult to understand, then they
    should try to discuss ideas with others in order to learn the details.
    I also believe quite strongly that people make mistakes sometimes, even
    for things that they can, or should, understand.


    Although two persons who don't know how it works won't help! You
    probably mean looking for existing solutions and implementations from somebody who eventually figured it out.

    I prefer language features that don't present difficulties. The only open-ended aspect of compilation I will acknowledge, is back-end optimisation. And I said that there, you can go as far as you like.



    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to David Brown on Tue Jan 9 01:05:21 2024
    On 08/01/2024 19:50, David Brown wrote:
    On 08/01/2024 16:53, Bart wrote:

    This can occur with reference parameters too: I believe you get the
    same thing in C++.

    Not quite - that's an easy and common misunderstanding.  (It is even
    more understandable for you, since your language uses "ref" to mean what
    is called a "pointer" in C and C++.)  References in C++ are not "auto-dereferenced pointers" - they are alternative names for objects.
    It is better to think of references as being ways to identify objects,
    and pointers as being indirect references.  C++ references are /not/ pointers.

    They look like auto-dereferenced pointers to me:

    ---------------------------
    #include <stdio.h>

    void F1(int a) {a = a + 1;}
    void F2(int* a) {*a = *a + 1;}
    void F3(int &a) {a = a + 1;}

    int main(void) {

    int a=100, b=200, c=300;

    F1(a);
    F2(&b);
    F3(c);

    printf("a = %d, b = %d, c = %d\n", a, b, c);
    }
    ---------------------------

    This is C code except for F3 and the call to it. It was compiled as C++.

    The output expected is '100 201 301'. F1 can't modify its caller's data.
    F2 can do so via explicit pointers. F3 does it via references.

    But notice the body of F3 is identical to F1's; and so is the passed
    argument (doesn't need &).

    You get the same behaviour as using pointers, but without explicit
    address-of ops on arguments, and deref ops on parameters.

    But also, there is that same hidden deref so that you can't tell exactly
    what's going on unles you see that & in the argument list.

    My languages use the same & in the argument list to indicate by-reference.


    It's worse than that.

    No, it's not worse - it's just a side-effect of the convenience of notation.  Disallowing some of the forms you show below would require
    extra rules, adding complication to the standards,

    My MCC compiler seems to manage it (I'm not sure how), and my main
    compiler does even better: you can't even do the equivalent of (*F)(),
    because F is not a pointer to anything and can't be dereferenced.

    (Yes, F by itself is still equal to &F, but not F().)

    Whatever extra rules or logic are involved, they are insignificant
    compared to those for VLAs, or for mixed sign arithmetic, for the
    minimum groupings of {} around init data, or the algorithm for searching
    for includes files, ...

    while allowing them
    is harmless (since people would not use them in code).

    I suggested using .len on my unbounded arrays were harmless because they wouldn't be used; you disagreed...

    It just looks sloppy to me that you can effectively dereference
    something an unlimited number of times; where is the type system in this?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to David Brown on Tue Jan 9 01:50:19 2024
    On 2024-01-08, David Brown <david.brown@hesbynett.no> wrote:
    On 08/01/2024 16:53, Bart wrote:
    On 08/01/2024 12:50, David Brown wrote:
    On 07/01/2024 16:34, Bart wrote:

    I used to have that a^.b syntax (deref pointer then index).

    But for a few years I've relaxed that so that the deref is done
    automatically:

         a^.b     becomes    a.b
         a^[b]    becomes    a[b]
         a^(b)    becomes    a(b)

    AFAICS, C can could also relax the (*a).b or a->b synax so that you
    just do a.b. You could do that today, and nothing changes. (Of course
    it would need a compiler update).

    You could, in the sense that (AFAICS) there would be no situation
    where in code today "a.b" and "a->b" were both syntactically and
    semantically correct but meant different things.  Then you could have
    a compiler treat the syntax or constraint error "a.b" as intending to
    mean "a->b".

    I don't think it would be a good idea - I think it just adds confusion
    because you easily lose track of what are structs and what are
    pointers to structs.

    Yet this is exactly what happens with those other examples: you don't
    know if the X in X[i] has type T[] or T*. (The use of (*X)[i] when X is
    of type T(*)[] is rare.)

    And you don't know if the F in F(x) is an actual function, or a pointer
    to a function.

    The "->" alternate is anyway a little strange:

       (*P).m   can be written as  P->m
       (**Q).m  can only be reduced to (*Q)->m

    So it only works on the last lot of indirection. There is also no
    euivalent of just (*P), "->" needs to specify a member name as it
    combines two operations.

    Yes, the short-cut only works for the (by far) most common case.


    I'd rather it be an error when you get these wrong in the code.

    I had the same misgivings: there is a loss of transparency, but after I
    started using the auto-deref, the benefits outweighed that:

    Code was remarkably free of clutter. (And in my case, I had sections of
    code that could often be ported as-is to/from my other language that
    didn't need those derefs.)


    I didn't like automatic dereferencing (but I could live with it -
    overall I found Delphi a very productive tool). But that's a
    preference, and it is no surprise that other people have different preferences.

      My personal preference is either to say that everything is always a
    reference (like Python), or everything is always a value (like C) and
    do the dereferencing explicitly.  Other people make think such
    automatic dereferencing is a good idea, but I personally don't.

    This can occur with reference parameters too: I believe you get the same
    thing in C++.

    Not quite - that's an easy and common misunderstanding. (It is even
    more understandable for you, since your language uses "ref" to mean what
    is called a "pointer" in C and C++.) References in C++ are not "auto-dereferenced pointers" - they are alternative names for objects.

    There is no way to distinguish a C++ reference from an
    "auto-dereferenced pointer", though.

    Importantly, an "auto-dereferenced *constant* pointer".

    References behave like aliases only in a lexical scope. In a lexical
    scope, pointer variables can also disappear, particularly ones that
    are initialized and never modified.

    int a = 0, *const pa = &a;

    #define b (*pa)

    b now behaves like another name for a. The compiler can replace every
    *pa with b.

    References in other situations are necessarily real objects that
    hold a pointer-like address.


    int &ref = *new int;

    // ...

    delete &ref;

    The ref entity is definitely not just an alias name. The name "ref"
    doesn't even exist at run time. "ref" is the compile-time name of a
    reference, and that reference is an entity that is somehow bound to the
    object from operator new. That binding of ref to the allocated object
    requires a pointer-like entity. What comes out of new is a pointer, and
    what must go into delete is a pointer. What carries it in between those
    is carrying a pointer, and we can get one out of it using the address-of operator.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Tim Rentsch@21:1/5 to Kaz Kylheku on Mon Jan 8 22:28:22 2024
    Kaz Kylheku <433-929-6894@kylheku.com> writes:

    There is no way to distinguish a C++ reference from an
    "auto-dereferenced pointer", though. [...]

    Right. The C++ standard would have us believe a fiction
    that a reference is not just an auto-dereferenced pointer.
    It's an excellent example of the fantasy world in which the
    C++ standard operates.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Tim Rentsch@21:1/5 to All on Mon Jan 8 22:56:00 2024
    scott@slp53.sl.home (Scott Lurndal) writes:

    [..concerning using a macro to determine the number of elements
    in an array, eg

    #define ARRAY_SIZE(a) (sizeof (a) / sizeof (a)[0])
    ]

    Indeed. I do believe that absent standardization, using a macro
    adds a level of indirection that may adversely affect the code
    readability (whether it is ARRAY_SIZE, ARRAY_LENGTH, LENGTH, NUM_ELEMENTS,
    or whatever, I would need to refer to the macro definition to
    determine the intent of the programmer).

    This reaction strikes me as being a bit nutty. I don't insist
    that a macro be used in such cases, but I wouldn't object to it
    either. It's much like the question of whether to wrap up
    several statements in a separate function definition, or simply
    write them inline. It's almost always true that either choice
    is acceptable, depending on what the author wants to emphasize.
    There are pluses and minuses on both sides.

    There are situations for which using a macro rather than writing
    out the code directly nearly always has a predominately negative
    effect, but this case isn't one of them.

    (To be clear, all of the above simply statements of my own
    opinions.)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Tim Rentsch@21:1/5 to Janis Papanagnou on Mon Jan 8 22:20:30 2024
    Janis Papanagnou <janis_papanagnou+ng@hotmail.com> writes:

    On 01.01.2024 01:07, Tim Rentsch wrote:

    [...]

    Thanks for your post and suggestions.

    Some have already been addressed (and some also answered) in
    this thread, [...]

    I knew there was some overlap between my comments and
    previous comments. I posted what I did both to add some
    new information and to give some corrections in those
    cases where there was overlap, phrased in a positive
    way.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to David Brown on Tue Jan 9 07:38:52 2024
    On Mon, 8 Jan 2024 13:50:08 +0100, David Brown wrote:

    I don't think it would be a good idea - I think it just adds confusion because you easily lose track of what are structs and what are pointers
    to structs.

    Ada tries to pretend there is no difference. There is mostly no ambiguity, except potentially in something like

    a := b;

    where “a” and “b” are both pointers to some common type. In this case, the
    meaning is not to dereference the pointers; to force a dereference, you
    write

    a.all := b.all;

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Bart on Tue Jan 9 08:30:07 2024
    On 09/01/2024 02:05, Bart wrote:
    On 08/01/2024 19:50, David Brown wrote:
    On 08/01/2024 16:53, Bart wrote:

    This can occur with reference parameters too: I believe you get the
    same thing in C++.

    Not quite - that's an easy and common misunderstanding.  (It is even
    more understandable for you, since your language uses "ref" to mean
    what is called a "pointer" in C and C++.)  References in C++ are not
    "auto-dereferenced pointers" - they are alternative names for objects.
    It is better to think of references as being ways to identify objects,
    and pointers as being indirect references.  C++ references are /not/
    pointers.

    They look like auto-dereferenced pointers to me:


    I know they look like that at first glance, but they are not
    auto-dereferenced pointers. It can sometimes be useful to understand
    that when you move references around (such as for function parameters),
    they are implemented as though they were a special kind of pointer -
    that tells you how efficient they are. But conceptually, for their use
    and understanding, I don't think it is helpful.


    It's worse than that.

    No, it's not worse - it's just a side-effect of the convenience of
    notation.  Disallowing some of the forms you show below would require
    extra rules, adding complication to the standards,

    My MCC compiler seems to manage it (I'm not sure how), and my main
    compiler does even better: you can't even do the equivalent of (*F)(), because F is not a pointer to anything and can't be dereferenced.

    How is that in any way "better"? You have gone out of your way to make
    your compiler non-conforming for the sake of stopping something no one
    would ever write? It all sounds a bit pointless (but harmless) to me.


    (Yes, F by itself is still equal to &F, but not F().)

    Whatever extra rules or logic are involved, they are insignificant
    compared to those for VLAs, or for mixed sign arithmetic, for the
    minimum groupings of {} around init data, or the algorithm for searching
    for includes files, ...

    Whataboutism is a strong sign that the person arguing has lost track of
    their point.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to David Brown on Tue Jan 9 11:11:28 2024
    On 09/01/2024 07:30, David Brown wrote:
    On 09/01/2024 02:05, Bart wrote:
    On 08/01/2024 19:50, David Brown wrote:
    On 08/01/2024 16:53, Bart wrote:

    This can occur with reference parameters too: I believe you get the
    same thing in C++.

    Not quite - that's an easy and common misunderstanding.  (It is even
    more understandable for you, since your language uses "ref" to mean
    what is called a "pointer" in C and C++.)  References in C++ are not
    "auto-dereferenced pointers" - they are alternative names for
    objects. It is better to think of references as being ways to
    identify objects, and pointers as being indirect references.  C++
    references are /not/ pointers.

    They look like auto-dereferenced pointers to me:


    I know they look like that at first glance, but they are not auto-dereferenced pointers.  It can sometimes be useful to understand
    that when you move references around (such as for function parameters),
    they are implemented as though they were a special kind of pointer -
    that tells you how efficient they are.  But conceptually, for their use
    and understanding, I don't think it is helpful.


    It's worse than that.

    No, it's not worse - it's just a side-effect of the convenience of
    notation.  Disallowing some of the forms you show below would require
    extra rules, adding complication to the standards,

    My MCC compiler seems to manage it (I'm not sure how), and my main
    compiler does even better: you can't even do the equivalent of (*F)(),
    because F is not a pointer to anything and can't be dereferenced.

    How is that in any way "better"?  You have gone out of your way to make
    your compiler non-conforming for the sake of stopping something no one
    would ever write?  It all sounds a bit pointless (but harmless) to me.


    (Yes, F by itself is still equal to &F, but not F().)

    Whatever extra rules or logic are involved, they are insignificant
    compared to those for VLAs, or for mixed sign arithmetic, for the
    minimum groupings of {} around init data, or the algorithm for
    searching for includes files, ...

    Whataboutism is a strong sign that the person arguing has lost track of
    their point.

    Not at all. You are defending some crazy anomaly, that you find nowhere
    else, for some insubstantial reason.

    It works like that because the language was poorly designed in the first
    place and nobody thought it worthwhile fixing it.

    So it's just another quirk.

    I quite like the idea that if you want to do *X, then it better have a
    type like T*. Then the result of *X, ie. dereferencing X, will be T.

    Now C does something odd: if T happens to be a function type, it
    immediately turns that result back into *T.

    I used to have some rules (not in C but expressed as C) where, if F is
    an actual function and has function type:

    Type Action

    &F func* Evaluate function reference
    F func Same as F()
    F(...) func Call the function (yielding func ret type)
    *F --- Error, not a pointer

    I changed that to be more C-like: I always need () to call the function
    even if there are no arguments, while F and &F are equivalent.

    This required an extra bit of logic. As far as explaining it goes:

    &F func* Evaluate function reference
    F func* Evaluate function reference
    F(...) func Call the function
    *F --- Error, not a pointer

    It's not that hard, it just introduced one small anomaly where F and F&
    are the same.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Bart on Tue Jan 9 15:56:28 2024
    On 09/01/2024 12:11, Bart wrote:
    On 09/01/2024 07:30, David Brown wrote:
    On 09/01/2024 02:05, Bart wrote:
    On 08/01/2024 19:50, David Brown wrote:
    On 08/01/2024 16:53, Bart wrote:

    This can occur with reference parameters too: I believe you get the
    same thing in C++.

    Not quite - that's an easy and common misunderstanding.  (It is even
    more understandable for you, since your language uses "ref" to mean
    what is called a "pointer" in C and C++.)  References in C++ are not
    "auto-dereferenced pointers" - they are alternative names for
    objects. It is better to think of references as being ways to
    identify objects, and pointers as being indirect references.  C++
    references are /not/ pointers.

    They look like auto-dereferenced pointers to me:


    I know they look like that at first glance, but they are not
    auto-dereferenced pointers.  It can sometimes be useful to understand
    that when you move references around (such as for function
    parameters), they are implemented as though they were a special kind
    of pointer - that tells you how efficient they are.  But conceptually,
    for their use and understanding, I don't think it is helpful.


    It's worse than that.

    No, it's not worse - it's just a side-effect of the convenience of
    notation.  Disallowing some of the forms you show below would
    require extra rules, adding complication to the standards,

    My MCC compiler seems to manage it (I'm not sure how), and my main
    compiler does even better: you can't even do the equivalent of
    (*F)(), because F is not a pointer to anything and can't be
    dereferenced.

    How is that in any way "better"?  You have gone out of your way to
    make your compiler non-conforming for the sake of stopping something
    no one would ever write?  It all sounds a bit pointless (but harmless)
    to me.


    (Yes, F by itself is still equal to &F, but not F().)

    Whatever extra rules or logic are involved, they are insignificant
    compared to those for VLAs, or for mixed sign arithmetic, for the
    minimum groupings of {} around init data, or the algorithm for
    searching for includes files, ...

    Whataboutism is a strong sign that the person arguing has lost track
    of their point.

    Not at all. You are defending some crazy anomaly, that you find nowhere
    else, for some insubstantial reason.

    I'm saying it doesn't matter. I have never heard it mentioned,
    anywhere, except by you. It is completely irrelevant.


    It works like that because the language was poorly designed in the first place and nobody thought it worthwhile fixing it.

    It works like that because the language was /well/ designed, for its
    purpose at the time, with the requirements and limitations of the time. Remember, as you always seem to forget, C was not designed with the sole purpose of making /your/ life as easy as possible, or suiting /your/
    particular preferences. The "(***foo)" anomaly is a side-effect of the
    way functions work in C. Leaving it in costs nothing, removing it would
    be an effort and an inconvenience.

    Do you /really/ think that, if this were important, no one would have
    suggested disallowing it? Do you /really/ think that, if this were
    leading to poor code, misunderstandings or mistakes, that compiler
    writers would not be able to add warnings about it? Do you /really/
    think you are so vastly superior to everyone else in the C world that
    you alone see the problem?

    Sometimes you are like that old joke of the guy driving the wrong way
    down the motorway, complaining that everyone else is wrong.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Bart on Tue Jan 9 18:12:23 2024
    On 2024-01-09, Bart <bc@freeuk.cm> wrote:
    Now C does something odd: if T happens to be a function type, it
    immediately turns that result back into *T.

    That's one way to have distinct concepts of function type and pointer to
    that type, while preserving most the assembly language semantics of
    working with functions as addresses.

    In declarations, other than of a function parameter, a function type
    is distinct from a pointer to that function type.

    But in all the places where the rhetorical rubber meets the proverbial pavement, functions are manipulated as pointers, like in machine
    languages.

    Thus we have decay rules similar to the array ones: parameter of
    function type adjusted to pointer; expressions of function type
    converted to pointer.

    It poses fewer difficulties for functions, in comparison to arrays;
    it is hardly noticeable.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to David Brown on Tue Jan 9 17:46:48 2024
    On 09/01/2024 14:56, David Brown wrote:
    On 09/01/2024 12:11, Bart wrote:

    Not at all. You are defending some crazy anomaly, that you find
    nowhere else, for some insubstantial reason.

    I'm saying it doesn't matter.  I have never heard it mentioned,
    anywhere, except by you.  It is completely irrelevant.

    So you see this in code:

    (*F)(x);

    and don't assume that F must be a pointer to function; why not? I
    thought you wanted that transparency?


    It works like that because the language was poorly designed in the
    first place and nobody thought it worthwhile fixing it.

    It works like that because the language was /well/ designed, for its
    purpose at the time, with the requirements and limitations of the time.

    Poppycock.

    What limitations were there? These were people who were well funded to
    do exactly this work, and had decent equipment like DEC minicomputers to
    work with.

    My language a decade later was done in my spare time on an 8-bit
    machine. There was no funding for that, and I wasn't an academic.

    But it didn't the same plethora of quirks as C. Most languages don't.

    Remember, as you always seem to forget, C was not designed with the sole purpose of making /your/ life as easy as possible, or suiting /your/ particular preferences.  The "(***foo)" anomaly is a side-effect of the
    way functions work in C.  Leaving it in costs nothing, removing it would
    be an effort and an inconvenience.

    Really? I didn't have much trouble.

    Do you /really/ think that, if this were important, no one would have suggested disallowing it?  Do you /really/ think that, if this were
    leading to poor code, misunderstandings or mistakes, that compiler
    writers would not be able to add warnings about it?  Do you /really/
    think you are so vastly superior to everyone else in the C world that
    you alone see the problem?

    Sometimes you are like that old joke of the guy driving the wrong way
    down the motorway, complaining that everyone else is wrong.

    Would you design a language like that today?

    I could list dozens of howlers within the language.

    I've long thought that C was an elaborate joke that escaped from the
    lab. Possibly it is, and no one has yet owned up to it.

    But people actually take it seriously and even defend all this stuff.

    YOU would be the guy driving along the motorway in a Model T Ford,
    backed up by a 40-foot truck containing all the advanced equipment
    needed to work around all its shortcomings.

    Behind them would be a further fleet of trucks containing the
    environment that you insist on being available.

    Oh, sorry, I forgot that in 2023 it acquired new door handles. Yeah,
    that makes a difference.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Bart on Tue Jan 9 19:56:54 2024
    On 09/01/2024 18:46, Bart wrote:
    On 09/01/2024 14:56, David Brown wrote:
    On 09/01/2024 12:11, Bart wrote:

    Not at all. You are defending some crazy anomaly, that you find
    nowhere else, for some insubstantial reason.

    I'm saying it doesn't matter.  I have never heard it mentioned,
    anywhere, except by you.  It is completely irrelevant.

    So you see this in code:

       (*F)(x);

    and don't assume that F must be a pointer to function; why not? I
    thought you wanted that transparency?

    Yes, I assume F is a pointer to a function - because I assume, unless
    proven otherwise, that the author of the code is not a complete moron or
    an evil maniac doing his or her best to confuse people.


    (I haven't bothered responding to the rest of your post, because I can't
    see a realistic and accurate response without sounding nasty, and I
    don't want to do that. Suffice to say that C is inordinately successful
    as a language, and your language is not - perhaps there are good reasons
    for that.)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to Kaz Kylheku on Tue Jan 9 20:01:55 2024
    On 09/01/2024 19:20, Kaz Kylheku wrote:
    On 2024-01-09, Bart <bc@freeuk.cm> wrote:
    On 09/01/2024 14:56, David Brown wrote:
    On 09/01/2024 12:11, Bart wrote:

    Not at all. You are defending some crazy anomaly, that you find
    nowhere else, for some insubstantial reason.

    I'm saying it doesn't matter.  I have never heard it mentioned,
    anywhere, except by you.  It is completely irrelevant.

    So you see this in code:

    (*F)(x);

    and don't assume that F must be a pointer to function; why not? I
    thought you wanted that transparency?

    The use of (*pf)(args, ...) is unnecessary and in my experience, rare.

    It's a style used to emphasize that this is an indirect call.

    I probably first saw that ages ago in some XWindow sources or examples.

    I don't think I've ever seen that used on a function, as in

    (*strcmp)(str, "foo")

    which would be a kind of misuse, like a deliberately misleading comment.

    (Was that explicit dereference ever required in some versions of C or C compilers?)

    (*pf)(args, ...) style for indirections does have something
    to recommend it. If it is consistently and appropriately used, it
    clearly indicates the function indirections.


    This is the funny thing about C (well one of many funny things):

    * It has types which are pointers to Arrays, Structs and Functions

    * It has formal syntax to dereference all of those

    * But, that syntax is never used!

    This is what I mean:

    Type Formal Syntax Idiomatic Use

    A Pointer to Array (*A)[i] A[i]

    P Pointer to Struct (*P).m P->m

    F Pointer to Function (*F)(x) F(x)


    You only ever see syntax in the last column, hardly ever that formal style.

    Which is probably just as well, as it's pretty ugly, and harder to type.

    With arrays, people don't even bother with actual array pointers.
    Structs have that funny -> operator, and Functions have those special rules.

    At least, with P->m, you know that P must be a pointer to a struct, but
    you can't tell with those others.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Bart on Tue Jan 9 19:20:06 2024
    On 2024-01-09, Bart <bc@freeuk.cm> wrote:
    On 09/01/2024 14:56, David Brown wrote:
    On 09/01/2024 12:11, Bart wrote:

    Not at all. You are defending some crazy anomaly, that you find
    nowhere else, for some insubstantial reason.

    I'm saying it doesn't matter.  I have never heard it mentioned,
    anywhere, except by you.  It is completely irrelevant.

    So you see this in code:

    (*F)(x);

    and don't assume that F must be a pointer to function; why not? I
    thought you wanted that transparency?

    The use of (*pf)(args, ...) is unnecessary and in my experience, rare.

    It's a style used to emphasize that this is an indirect call.

    I probably first saw that ages ago in some XWindow sources or examples.

    I don't think I've ever seen that used on a function, as in

    (*strcmp)(str, "foo")

    which would be a kind of misuse, like a deliberately misleading comment.

    (Was that explicit dereference ever required in some versions of C or C compilers?)

    (*pf)(args, ...) style for indirections does have something
    to recommend it. If it is consistently and appropriately used, it
    clearly indicates the function indirections.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to David Brown on Tue Jan 9 20:52:06 2024
    On 09/01/2024 18:56, David Brown wrote:
    On 09/01/2024 18:46, Bart wrote:
    On 09/01/2024 14:56, David Brown wrote:
    On 09/01/2024 12:11, Bart wrote:

    Not at all. You are defending some crazy anomaly, that you find
    nowhere else, for some insubstantial reason.

    I'm saying it doesn't matter.  I have never heard it mentioned,
    anywhere, except by you.  It is completely irrelevant.

    So you see this in code:

        (*F)(x);

    and don't assume that F must be a pointer to function; why not? I
    thought you wanted that transparency?

    Yes, I assume F is a pointer to a function - because I assume, unless
    proven otherwise, that the author of the code is not a complete moron or
    an evil maniac doing his or her best to confuse people.

    Maybe F used to be a pointer, but is now a normal function, and the code
    was not updated. Or maybe a normal function named F is now shadowing the
    more global function pointer F.


    (I haven't bothered responding to the rest of your post, because I can't
    see a realistic and accurate response without sounding nasty, and I
    don't want to do that.  Suffice to say that C is inordinately successful
    as a language, and your language is not - perhaps there are good reasons
    for that.)

    There's being successful, and there's being right.

    /I/ can write:

    println 0123

    and get the expected output. C would give you 83.

    Don't tell me: gcc has some option to warn of using octal literals. So
    your 'successful' language relies on extensive extra tools (all the
    stuff in that support truck) to keep it useable.

    BTW here's an exchange from a few days ago:

    DB:
    .. the insanity of evaluating to 0 when given a pointer/reference
    to an "unbounded" array.

    BC:
    ..it is low priority because it is never used that way.

    DB:
    That's the kind of potentially confusing short-cut you can make when
    only you ever use the language.

    I made the argument you gave - no one would do that - but when I do it,
    it makes things confusing.

    I did then try to fix that, but stopped because of interactions with
    actual zero-length arrays (I would need to be fully immersed to make the changes).

    Then the next day I found that C doesn't even /have/ zero-length arrays:
    you can't have an empty array; how about that? I thought C was zero-based!

    As for things that are insane, how about:

    int spam() {
    spam: spam(spam, spam(), spam(spam(spam)), spam);
    }

    I can't write that in my language, as it won't accept complete
    gobbledygook. Of course, it's not as 'successful' as C, so what do I know.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to Keith Thompson on Tue Jan 9 21:33:37 2024
    On 09/01/2024 21:15, Keith Thompson wrote:
    Bart <bc@freeuk.cm> writes:
    [...]
    I did then try to fix that, but stopped because of interactions with
    actual zero-length arrays (I would need to be fully immersed to make
    the changes).

    Then the next day I found that C doesn't even /have/ zero-length
    arrays: you can't have an empty array; how about that? I thought C
    was zero-based!
    [...]

    Have you considered learning the language before trying to implement it?

    Which version of C do you suggest; the one where:

    #include <stdio.h>

    int main(void) {
    int A[0];
    int B[]={};

    printf("%zu\n", sizeof(A));
    printf("%zu\n", sizeof(B));
    }

    compiles fine with tcc, gcc and clang, and displays 0 for the sizes?

    Or the one where those produce warnings? Or the one where it actually fails?

    So, if C doesn't have zero-length arrays, why don't I get a fatal error?

    Just like if I get if I try:

    int A[-1];

    Here they are much more unequivocal; all will give a hard error without
    needing their arm twisting.

    Perhaps the language should take this stuff more seriously, then maybe I
    would.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From James Kuyper@21:1/5 to Kaz Kylheku on Tue Jan 9 16:42:10 2024
    On 1/9/24 14:20, Kaz Kylheku wrote:
    ...
    The use of (*pf)(args, ...) is unnecessary and in my experience, rare.

    It's a style used to emphasize that this is an indirect call.

    I probably first saw that ages ago in some XWindow sources or examples.

    I don't think I've ever seen that used on a function, as in

    (*strcmp)(str, "foo")

    which would be a kind of misuse, like a deliberately misleading comment.

    (Was that explicit dereference ever required in some versions of C or C compilers?)

    Yes. In K&R C, a function call started with a primary expression, and
    "The primary expression must be of type "function returning ...". (7.1,
    P186), so function pointers that had not been explicitly dereferenced
    didn't qualify. There was no implicit conversion from expressions of
    function type into a pointer to a function.
    Therefore, you could use func() or (*funcptr)(), but not funcptr().

    In C89 there were two connected changes:
    "Except when it is the operand of the sizeof operator /25/ or the unary& operator, a function designator with type ``function returning type'' is converted to an expression that has type ``pointer to function returning
    type .''", )3.2.2.1)
    "The expression that denotes the called function/29/ shall have type
    pointer to function ..." (3.3.2.2).
    The net result was that func() still works, and (*funcptr)(), because in
    both cases the function designator getsimplicitly converted to a
    pointer, which is good for backwards compatibility, but funcptr() also
    works, because funcptr is already a pointer.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Bart on Tue Jan 9 21:37:30 2024
    On 2024-01-09, Bart <bc@freeuk.cm> wrote:
    This is the funny thing about C (well one of many funny things):

    * It has types which are pointers to Arrays, Structs and Functions

    * It has formal syntax to dereference all of those

    * But, that syntax is never used!

    This is what I mean:

    Type Formal Syntax Idiomatic Use

    A Pointer to Array (*A)[i] A[i]

    P Pointer to Struct (*P).m P->m

    F Pointer to Function (*F)(x) F(x)


    You only ever see syntax in the last column, hardly ever that formal style.

    The (*P).m can occur in macro-generated code. E.g. you could have a:

    #define task_self (*task_get_self())

    so then you can access task_self.priority or whatever.

    The syntax is necessary for completeness, even if the sugar is
    almost always used in open coding.

    Which is probably just as well, as it's pretty ugly, and harder to type.

    Syntactic sugars or shortcuts that are almost always used are found
    in other languages.

    Lisp programmers rarely type (quote x) instead of 'x. It's important
    that the underlying syntax is pinned down, though.

    With arrays, people don't even bother with actual array pointers.

    Array pointers are cumbersome and error prone due to the leaky
    abstraction. Under most pointer abstractions, if T is typedef-ed
    as a pointer to something and we have a T P, we know that *P
    is that something. Not so with arrays; *P designates an array which
    sneakily decays to the first element.

    Structs have that funny -> operator, and Functions have those special rules.

    At least, with P->m, you know that P must be a pointer to a struct, but
    you can't tell with those others.

    Or, well union. If you see a P->n, you don't know whether it's the same
    as P->m or distinct because P could be a pointer to a union.


    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Keith Thompson on Tue Jan 9 21:51:25 2024
    On 2024-01-09, Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:
    Kaz Kylheku <433-929-6894@kylheku.com> writes:
    On 2024-01-09, Bart <bc@freeuk.cm> wrote:
    Now C does something odd: if T happens to be a function type, it
    immediately turns that result back into *T.

    That's one way to have distinct concepts of function type and pointer to
    that type, while preserving most the assembly language semantics of
    working with functions as addresses.

    On my x86_64 system with gcc, these lines of C:
    func();
    funcptr();
    result in these call instructions:
    call func
    call *%rdx

    That's a silly quirk of AT&T syntax; not reflected in the Intel one:

    See here:

    https://en.wikipedia.org/wiki/Indirect_branch#Example_assembler_syntax

    It looks as if what *%rdx is doing is fetching an address from the
    location pointed at by rdx, and then jumping to that address.

    Most of the other examples in that Wikipedia page have any superfluous
    operator syntax on the operand suggesting extra indirection.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Ike Naar@21:1/5 to Bart on Tue Jan 9 21:51:57 2024
    On 2024-01-09, Bart <bc@freeuk.cm> wrote:
    This is the funny thing about C (well one of many funny things):

    * It has types which are pointers to Arrays, Structs and Functions

    * It has formal syntax to dereference all of those

    * But, that syntax is never used!

    This is what I mean:

    Type Formal Syntax Idiomatic Use

    A Pointer to Array (*A)[i] A[i]

    If A has type pointer-to-array, (*A)[i] and A[i] are not the same thing: (*A)[i] is an array element
    A[i] is an entire array

    Here's a concrete example, where A is a pointer to an array [42] of double:

    $ cat prog.c
    #include <stdio.h>
    int main(void)
    {
    double array[42];
    double (*A)[42] = &array;
    int const i = 0;
    printf("sizeof (*A)[i] = %zu\n", sizeof (*A)[i]);
    printf("sizeof A[i] = %zu\n", sizeof A[i]);
    return 0;
    }
    $ cc -pedantic -Wall -Wextra -o prog prog.c
    $ ./prog
    sizeof (*A)[i] = 8
    sizeof A[i] = 336

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to Bart on Tue Jan 9 21:55:34 2024
    Bart <bc@freeuk.cm> writes:
    On 09/01/2024 21:15, Keith Thompson wrote:
    Bart <bc@freeuk.cm> writes:
    [...]
    I did then try to fix that, but stopped because of interactions with
    actual zero-length arrays (I would need to be fully immersed to make
    the changes).

    Then the next day I found that C doesn't even /have/ zero-length
    arrays: you can't have an empty array; how about that? I thought C
    was zero-based!
    [...]

    Have you considered learning the language before trying to implement it?

    Which version of C do you suggest; the one where:

    #include <stdio.h>

    int main(void) {
    int A[0];
    int B[]={};

    printf("%zu\n", sizeof(A));
    printf("%zu\n", sizeof(B));
    }

    compiles fine with tcc, gcc and clang, and displays 0 for the sizes?

    Or the one where those produce warnings? Or the one where it actually fails?

    All of them. You've been told over, and over, and over.

    $ cc --pedantic-errors -o /tmp/a /tmp/t.c
    /tmp/t.c: In function 'main':
    /tmp/t.c:4:7: error: ISO C forbids zero-size array 'A' [-Wpedantic]
    int A[0];
    ^
    /tmp/t.c:5:11: error: ISO C forbids empty initializer braces [-Wpedantic]
    int B[]={};
    ^
    /tmp/t.c:5:7: error: zero or negative size array 'B'
    int B[]={};
    ^
    $

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Bart on Tue Jan 9 22:12:40 2024
    On 2024-01-09, Bart <bc@freeuk.cm> wrote:
    On 09/01/2024 18:56, David Brown wrote:
    On 09/01/2024 18:46, Bart wrote:
    On 09/01/2024 14:56, David Brown wrote:
    On 09/01/2024 12:11, Bart wrote:

    Not at all. You are defending some crazy anomaly, that you find
    nowhere else, for some insubstantial reason.

    I'm saying it doesn't matter.  I have never heard it mentioned,
    anywhere, except by you.  It is completely irrelevant.

    So you see this in code:

        (*F)(x);

    and don't assume that F must be a pointer to function; why not? I
    thought you wanted that transparency?

    Yes, I assume F is a pointer to a function - because I assume, unless
    proven otherwise, that the author of the code is not a complete moron or
    an evil maniac doing his or her best to confuse people.

    Maybe F used to be a pointer, but is now a normal function, and the code
    was not updated. Or maybe a normal function named F is now shadowing the
    more global function pointer F.

    That is a risk. It's something that could be diagnosed if it was enough
    of a problem for enough people.

    GCC can diagnose if a declaration of a function parameter uses
    a different style from the definition:

    void f(int (int));

    void f(int (*pfn)(int)) { ... }

    There could easily be a warning when a function is dereferenced.

    Someone who wants to always write (*pfn)(args, ...) could benefit
    from a related warning when pfn(args, ...) is used instead.

    Don't tell me: gcc has some option to warn of using octal literals. So
    your 'successful' language relies on extensive extra tools (all the
    stuff in that support truck) to keep it useable.

    Unfortunately, it is a cynical observation that languages that don't
    require tools tend not to attract people.

    If you want popularity, you need to make some crap that causes people to
    have to band together and generate hacks to solve this and that and
    share those hacky solutions with each other.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to Scott Lurndal on Tue Jan 9 22:22:49 2024
    On 09/01/2024 21:55, Scott Lurndal wrote:
    Bart <bc@freeuk.cm> writes:

    Which version of C do you suggest; the one where:

    #include <stdio.h>

    int main(void) {
    int A[0];
    int B[]={};

    printf("%zu\n", sizeof(A));
    printf("%zu\n", sizeof(B));
    }

    compiles fine with tcc, gcc and clang, and displays 0 for the sizes?

    Or the one where those produce warnings? Or the one where it actually fails?

    All of them. You've been told over, and over, and over.

    Been told what? That I should produce a compiler that, at anyone's whim,
    can either pass, fail or warn about the same piece of code?

    $ cc --pedantic-errors -o /tmp/a /tmp/t.c
    /tmp/t.c: In function 'main':
    /tmp/t.c:4:7: error: ISO C forbids zero-size array 'A' [-Wpedantic]
    int A[0];
    ^
    /tmp/t.c:5:11: error: ISO C forbids empty initializer braces [-Wpedantic]
    int B[]={};
    ^
    /tmp/t.c:5:7: error: zero or negative size array 'B'
    int B[]={};
    ^
    $

    So, DOES C HAVE ZERO-LENGTH ARRAYS OR NOT?

    It's a really simple question!

    Because this program easily passes:

    int A[0];

    This one doesn't, qne without needing to use -pedantic or -pedantic-errors:

    int A[-1];

    If the answer is No, why is gcc so reluctant to complain about it
    compared with the -1 size?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From James Kuyper@21:1/5 to Bart on Tue Jan 9 17:37:09 2024
    Bart <bc@freeuk.cm> writes:
    On 09/01/2024 21:15, Keith Thompson wrote:
    Bart <bc@freeuk.cm> writes:
    [...]
    I did then try to fix that, but stopped because of interactions with
    actual zero-length arrays (I would need to be fully immersed to make
    the changes).

    Then the next day I found that C doesn't even /have/ zero-length
    arrays: you can't have an empty array; how about that? I thought C
    was zero-based!
    [...]
    Have you considered learning the language before trying to implement
    it?

    Which version of C do you suggest; the one where:

    #include <stdio.h>

    int main(void) {
    int A[0];
    int B[]={};

    printf("%zu\n", sizeof(A));
    printf("%zu\n", sizeof(B));
    }

    compiles fine with tcc, gcc and clang, and displays 0 for the sizes?

    Or the one where those produce warnings? Or the one where it actually fails?

    So, if C doesn't have zero-length arrays, why don't I get a fatal error?

    2 Reasons.
    First of all, the only thing that the C standard requires for such code
    is a diagnostic message. It doesn't require that the code compile, and
    it also doesn't require that it not compile. If an implementation
    chooses to create an executable from such code, and if you choose to
    execute it, the standard imposes no requirements on what behavior will
    occur when you do so.

    The fact that you didn't get the required diagnostic message is because
    none of those programs conforms to any version of the C standard in it's default mode. Since you've made it clear that putting them into
    conforming mode is too complicated for you to understand, you're stuck
    with non-conforming behavior. You could, with equal (lack of)
    justification, use a fortran compiler, which also fails to conform to
    the C standard.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to James Kuyper on Tue Jan 9 23:27:31 2024
    On 09/01/2024 22:37, James Kuyper wrote:

    The fact that you didn't get the required diagnostic message is because
    none of those programs conforms to any version of the C standard in it's default mode. Since you've made it clear that putting them into
    conforming mode is too complicated for you to understand,

    So why doesn't a /C/ compiler put itself into conforming mode?

    It seems THAT is too hard for it to understand!

    Seriously, why don't they do that?

    Then lots of people who don't bother with those fiddly options don't get
    the wrong impression about what C actually allows.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From James Kuyper@21:1/5 to Bart on Tue Jan 9 20:05:22 2024
    Bart <bc@freeuk.cm> writes:
    On 09/01/2024 22:37, James Kuyper wrote:
    The fact that you didn't get the required diagnostic message is because
    none of those programs conforms to any version of the C standard in it's
    default mode. Since you've made it clear that putting them into
    conforming mode is too complicated for you to understand,

    So why doesn't a /C/ compiler put itself into conforming mode?

    You didn't use any C compilers, at least, you didn't use them as C
    compilers. At least two of them (I don't know about tcc) can compile
    standard C, but only if you instruct them to do so. When you fail to
    tell them so, they are not standard C compilers, they are compilers for non-standard variants of C.

    It seems THAT is too hard for it to understand!

    Seriously, why don't they do that?

    Because they don't want to. The language that they compile by default is
    a language that they like better than standard C. People like me who
    value standard conformance are sufficiently common that they provide
    options which enable standard conformance, but their target audience
    WANTS support for a C-like language that they like better than standard
    C, and that's what they provide.

    Then lots of people who don't bother with those fiddly options don't
    get the wrong impression about what C actually allows.

    Most people who don't bother checking out the available options are
    generally primarily interested in knowing what the compiler they are
    actually using allows - if they cared about standard conformance, they'd
    check whether the compiler claimed to be standard conforming, and would
    quickly notice that the compiler only makes such claims when invoked
    with particular options. People who don't bother making such checks,
    despite wanting standard conformance, quickly learn that they don't
    actually get it - except for you, of course. That's something you seem incapable of learning.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to Keith Thompson on Wed Jan 10 00:40:11 2024
    On 10/01/2024 00:05, Keith Thompson wrote:
    Bart <bc@freeuk.cm> writes:
    On 09/01/2024 22:37, James Kuyper wrote:
    The fact that you didn't get the required diagnostic message is because
    none of those programs conforms to any version of the C standard in it's >>> default mode. Since you've made it clear that putting them into
    conforming mode is too complicated for you to understand,

    So why doesn't a /C/ compiler put itself into conforming mode?

    It seems THAT is too hard for it to understand!

    Seriously, why don't they do that?

    Seriously, why are you asking us?

    Then lots of people who don't bother with those fiddly options don't
    get the wrong impression about what C actually allows.

    That's a question about C compilers, not about the C language. I don't
    think anyone here works on any of the major C compilers, so you're not
    likely to get a definitive answer here.

    You have your own C or C-like compiler, don't you? Does it attempt to
    be fully conforming by default? Don't you have your own reasons for
    that decision?

    The first version did a better job than gcc of outlawing what I
    considered out-dated or dangerous features WITHOUT NEEDING TO BE TOLD.

    However because some legacy programs still used them (often
    inadvertently because people simply didn't know about them; /their/
    compiler said nothing), it was necessary to opt-in to build them.

    On the update I did a few months ago, I decided I didn't care any more.
    The option was removed. C is a lost cause.


    I *think* the general attitude of the gcc maintainers has been that
    gcc's useful extensions are more important than ISO C conformance, and
    that there's usually not much reason to use a C compiler other than gcc.


    That's based on my vague memory of something I read some years ago.
    clang tries to be closely compatible with gcc. tcc is small enough that
    it doesn't claim to be conforming.

    However, gcc does make it easy (yes, it's easy)

    An easy compiler is one where you just do:

    gcc prog

    and not, at the very least:

    gcc prog.c -prog.exe -std=c11 -pedantic-errors

    Meanwhile I routinely test C programs on half a dozen compilers. I can't
    be bothered with all this crap.

    Here's a source file; it's C code; now go and compile it for me, and
    tell me if there is anything badly wrong. And I mean properly wrong not
    some ******* unused label which gets lumped in with a missing function prototype so that it thinks all the arguments are ints.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Bart on Wed Jan 10 01:32:24 2024
    On 2024-01-09, Bart <bc@freeuk.cm> wrote:
    On 09/01/2024 22:37, James Kuyper wrote:

    The fact that you didn't get the required diagnostic message is because
    none of those programs conforms to any version of the C standard in it's
    default mode. Since you've made it clear that putting them into
    conforming mode is too complicated for you to understand,

    So why doesn't a /C/ compiler put itself into conforming mode?

    That's like asking, why don't they just switch to the Tokyo dialect
    in Osaka?

    Vendors like their dialect of C and promote that in their product.

    Users who want to write portable code and be alerted when they stray
    from that program are the ones who have to configure something.

    The GNU Coding Standards states it explicitly:

    https://www.gnu.org/prep/standards/standards.html#Non_002dGNU-Standards

    "For instance, Standard C says that nearly all extensions to C are
    prohibited. How silly! GCC implements many extensions, some of which
    were later adopted as part of the standard. If you want these
    constructs to give an error message as “required” by the standard, you
    must specify ‘--pedantic’, which was implemented only so that we can
    say “GCC is a 100% implementation of the standard”, not because there
    is any reason to actually use it.

    There you go; the GNU project doesn't think you have any reason to
    identify situations when your program violates ISO C, such that it
    requires a diagnostic and is not required to be translated.

    Then lots of people who don't bother with those fiddly options don't get
    the wrong impression about what C actually allows.

    What C allows is for dialects which can be called some kind of C.
    Microsoft C, GNU C, ..

    If people experiment with GNU C, and thus what GNU C allows, but then
    think C allows it, they are simply wrong; they don't understand the
    difference between a common language and a dialect.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to Bart on Wed Jan 10 02:00:25 2024
    Bart <bc@freeuk.cm> writes:
    On 10/01/2024 00:05, Keith Thompson wrote:
    Bart <bc@freeuk.cm> writes:

    An easy compiler is one where you just do:

    gcc prog

    and not, at the very least:

    gcc prog.c -prog.exe -std=c11 -pedantic-errors


    $ functions c
    function c
    {
    gcc -o "$1" -std=c11 -pedantic-errors "$1".c
    }
    $ cat a.c
    #include <stdio.h>

    int
    main(int argc, const char **argv, const char **envp)
    {

    printf("Hello World\n");

    return 0;
    }
    $ c a
    $ ./a
    Hello World

    Can't get any more concise than that.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From tTh@21:1/5 to Bart on Wed Jan 10 02:57:06 2024
    On 1/10/24 01:40, Bart wrote:
    An easy compiler is one where you just do:

       gcc prog

    and not, at the very least:

       gcc prog.c -prog.exe -std=c11 -pedantic-errors

    Meanwhile I routinely test C programs on half a dozen compilers. I can't
    be bothered with all this crap.

    So do as all of us do : put this crap in a Makefile or
    a shell script, and forget about it.

    tTh

    --
    +---------------------------------------------------------------------+
    | https://tube.interhacker.space/a/tth/video-channels | +---------------------------------------------------------------------+

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Bart on Wed Jan 10 01:54:10 2024
    On 2024-01-10, Bart <bc@freeuk.cm> wrote:
    On 10/01/2024 00:05, Keith Thompson wrote:
    Bart <bc@freeuk.cm> writes:
    The first version did a better job than gcc of outlawing what I
    considered out-dated or dangerous features WITHOUT NEEDING TO BE TOLD.

    And you don't think two or more people could disagree about they
    consider outdated or dangerous, such that they would have to discuss
    it, agree on some requirements and write them down?

    However because some legacy programs still used them (often
    inadvertently because people simply didn't know about them; /their/
    compiler said nothing), it was necessary to opt-in to build them.

    But, so, if people wrote compilers based on what they consider
    outdated or dangerous, without being told, it would never
    happen that something that worked silently one one compiler
    is considered outdated or dangerous on another, yet that other one
    begrudgingly has to support it?

    On the update I did a few months ago, I decided I didn't care any more.
    The option was removed. C is a lost cause.

    I *think* the general attitude of the gcc maintainers has been that
    gcc's useful extensions are more important than ISO C conformance, and
    that there's usually not much reason to use a C compiler other than gcc.


    That's based on my vague memory of something I read some years ago.
    clang tries to be closely compatible with gcc. tcc is small enough that
    it doesn't claim to be conforming.

    However, gcc does make it easy (yes, it's easy)

    An easy compiler is one where you just do:

    An easy compiler is one that tells you where all your use-after-free
    errors, memory leaks, race conditions and out-of-range arithmetic are
    hiding. Running the compiler isn't the hard part of software
    development.

    gcc prog

    Ok, so then if that rejects non-ISO language extensions, all the users
    of GNU C have to use -std=gnu11 or whatever.

    and not, at the very least:

    gcc prog.c -prog.exe -std=c11 -pedantic-errors

    The GNU project has clearly articulated that -pedantic-errors is
    there for the pedants, and that there is no reason to use it.

    Meanwhile I routinely test C programs on half a dozen compilers. I can't
    be bothered with all this crap.

    Here's a source file; it's C code; now go and compile it for me, and
    tell me if there is anything badly wrong. And I mean properly wrong not
    some ******* unused label which gets lumped in with a missing function prototype so that it thinks all the arguments are ints.

    But someone thought that an unused label indicates that something is
    wrong, and implemented a check WITHOUT BEING TOLD.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to Keith Thompson on Wed Jan 10 02:04:24 2024
    On 10/01/2024 00:49, Keith Thompson wrote:
    Bart <bc@freeuk.cm> writes:

    You clearly do care a great deal. You care enough to spend a whole lot
    of time and effort posting in a newsgroup that discusses the language
    you say you don't care about, and refusing to learn things that we tell
    you over and over again.

    No, the group has been inactive for a long time and has recently flared
    up again.

    Yes, I find it annoying when people try to pretend that black is white,
    and I like to argue back. Like when every terrible feature of C is
    really a godsend, and every saner idea of mine is worthless.



    But I know how to make it do so, and I don't forget the relevant options
    5 minutes after someone tells me about them.

    Good for you. Except that everyone does it differently. I come across a
    LOT of open source code. Some of which I've tried to compile with my
    project. So what options do /I/ use? (Hint: there aren't any! Tcc
    doesn't have many either.)

    Knowing a million incantations to feed to gcc doesn't help me at all.
    The people who write the programs I try and build care little about C
    versions and dialects.

    Meanwhile I routinely test C programs on half a dozen compilers. I
    can't be bothered with all this crap.

    You use half a dozen compilers, you can't be bothered to use them
    properly, and you complain every time someone tries to toll you how to
    do so.

    Feel free to waste your time, but please please stop wasting ours.

    I spent some minutes trying to find where this zero-length array
    business originated. It was from you:

    KT:
    No, it doesn't. C doesn't have zero-length arrays.

    You say "C", but don't say which version or which common dialect.
    Apperently the most commonly used dialect is gnu C, so for lots of
    people, "C" does have zero-length arrays as far as they are concerned.

    So that was a rabbit-hole I wish I hadn't gone down.

    I'm still none the wiser. Can I actually use zero-length arrays? Yes,
    No, Maybe, Only with this or that compiler or that bunch of options.

    That's useful.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Bart@21:1/5 to Scott Lurndal on Wed Jan 10 02:14:17 2024
    On 10/01/2024 02:00, Scott Lurndal wrote:
    Bart <bc@freeuk.cm> writes:
    On 10/01/2024 00:05, Keith Thompson wrote:
    Bart <bc@freeuk.cm> writes:

    An easy compiler is one where you just do:

    gcc prog

    and not, at the very least:

    gcc prog.c -prog.exe -std=c11 -pedantic-errors


    $ functions c
    function c
    {
    gcc -o "$1" -std=c11 -pedantic-errors "$1".c
    }
    $ cat a.c
    #include <stdio.h>

    int
    main(int argc, const char **argv, const char **envp)
    {

    printf("Hello World\n");

    return 0;
    }
    $ c a
    $ ./a
    Hello World

    Can't get any more concise than that.

    Great. Now tell the gcc people that's what it should do ANYWAY.

    It can't get simpler than that.

    gcc is already a compiler driver, now we have to write a compile driver
    driver?

    I currently use these compilers (not for long, I need to get back to my
    real work):

    Produces

    mcc prog.c prog.exe
    tcc prog.c prog.exe
    lc64 prog.c prog.exe
    dmc prog.c prog.exe
    gcc prog.c a.exe
    gcc prog.c -oprog prog.exe

    Trust gcc to be different! Those other products don't need all that palaver.

    Two invocations of gcc, neither is right; it's either the wrong output
    or needs additional options.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to tTh on Wed Jan 10 05:28:43 2024
    On 2024-01-10, tTh <tth@none.invalid> wrote:
    On 1/10/24 01:40, Bart wrote:
    An easy compiler is one where you just do:

       gcc prog

    and not, at the very least:

       gcc prog.c -prog.exe -std=c11 -pedantic-errors

    Meanwhile I routinely test C programs on half a dozen compilers. I can't
    be bothered with all this crap.

    So do as all of us do : put this crap in a Makefile or
    a shell script, and forget about it.

    Please don't get Bart started on makefiles!

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to Kaz Kylheku on Wed Jan 10 06:28:20 2024
    On Wed, 10 Jan 2024 05:28:43 -0000 (UTC), Kaz Kylheku wrote:

    Please don't get Bart started on makefiles!

    Does he prefer Ninja?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Bart on Wed Jan 10 08:21:53 2024
    On 09/01/2024 21:52, Bart wrote:
    On 09/01/2024 18:56, David Brown wrote:
    On 09/01/2024 18:46, Bart wrote:
    On 09/01/2024 14:56, David Brown wrote:
    On 09/01/2024 12:11, Bart wrote:

    Not at all. You are defending some crazy anomaly, that you find
    nowhere else, for some insubstantial reason.

    I'm saying it doesn't matter.  I have never heard it mentioned,
    anywhere, except by you.  It is completely irrelevant.

    So you see this in code:

        (*F)(x);

    and don't assume that F must be a pointer to function; why not? I
    thought you wanted that transparency?

    Yes, I assume F is a pointer to a function - because I assume, unless
    proven otherwise, that the author of the code is not a complete moron
    or an evil maniac doing his or her best to confuse people.

    Maybe F used to be a pointer, but is now a normal function, and the code
    was not updated. Or maybe a normal function named F is now shadowing the
    more global function pointer F.

    And maybe the program was written by flying pigs.

    Inventing convoluted scenarios does not reduce your obsession over an irrelevant detail that does not bother anyone - even if they (as I do)
    agree that it's odd and not a feature that is useful to real code.

    Some people write bad and incomprehensible code - a (*F)(x) call is then
    the least of your problems. Some people write good and clear code, and
    this kind of call will never occur (when F is not a pointer variable).
    And yes, sometimes even the best programmers will make a mistake or
    forget something, and one in a million such mistakes might be around
    this particular syntactic feature. I think we can live with those odds.


    <Snip off-topic self-congratulations>

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Lawrence D'Oliveiro on Wed Jan 10 09:50:42 2024
    On 10/01/2024 07:28, Lawrence D'Oliveiro wrote:
    On Wed, 10 Jan 2024 05:28:43 -0000 (UTC), Kaz Kylheku wrote:

    Please don't get Bart started on makefiles!

    Does he prefer Ninja?

    He prefers whining about what he has to type by hand. He'd rather spend thousands of times more time, effort and keypresses complaining to
    people here (none of whom are, AFAIK, the author of any "make" tool)
    than he would need in order to write a little .bat file on his Windows
    system.

    It's incredible, really.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Bart on Wed Jan 10 09:37:42 2024
    On 09/01/2024 23:22, Bart wrote:
    On 09/01/2024 21:55, Scott Lurndal wrote:
    Bart <bc@freeuk.cm> writes:

    Which version of C do you suggest; the one where:

       #include <stdio.h>

       int main(void) {
           int A[0];
           int B[]={};

           printf("%zu\n", sizeof(A));
           printf("%zu\n", sizeof(B));
       }

    compiles fine with tcc, gcc and clang, and displays 0 for the sizes?

    Or the one where those produce warnings? Or the one where it actually
    fails?

    All of them.  You've been told over, and over, and over.

    Been told what? That I should produce a compiler that, at anyone's whim,
    can either pass, fail or warn about the same piece of code?

    No, you have been told:

    1. C is a language defined by the standards. Without further
    qualification, "C" refers to the latest published ISO standard -
    currently C17. But it's also fine to refer to specific standards, such
    as C99. The standards define the language syntax, constraints, required diagnostics (implementations are free to choose warnings or hard
    errors), and standard library specifications. There are also many
    pre-standard C versions, for which "K&R C" is /almost/ a standard.

    2. Almost all C compilers implement some extensions by default. These extensions are not C, but are compiler-specific language variants. Some
    people find them useful, other people prefer to stick to standard C.
    Some extensions are so widely implemented that they may be considered a pseudo-standard, others are very compiler or target specific.

    3. Almost all C compilers have default warnings and errors that do not
    match the standards requirements for C. Often they do this in both
    directions - failing to issue diagnostics on things that the standards
    require, and also issuing errors (halting compilation) for things that
    the standard allows. Almost all C compilers allow you to tune warnings
    and errors.

    4. Some C compilers aim to provide conforming modes, often for several
    standard versions, even though they are non-conforming (see 2 and 3
    above) by default. Others don't try to conform to any particular C
    standard, and can only very loosely be called a C compiler.

    5. A "C implementation" needs a compiler, a standard library, headers, a linker, perhaps an assembler, and a way to run the program on the
    target. These might be provided together, or combined from different
    places. For example, Microsoft provides everything with their MSVC
    tools. For gcc-based toolchains, GCC writes the compiler but does not
    provide binaries. The assembler and linker often come from the binutils project (which again does not provide binaries), but other assemblers
    and linkers may be used. Various libraries may be used, depending on
    the target, including glibc, newlib, musl, newlib-nano, redlib, avrlib,
    and many others. Users can get the parts individually, or more often
    they get packaged toolchains from Debian, Redhat, TDM, mingw-64, MS WSL,
    NXP, TI, Microchip, or many others according to their needs.


    Is any of that new to you? Can you honestly say you have not been told
    all this, many, many times?

    You /know/ how to make gcc and clang compile standard C. Yet you insist
    on faking ignorance. You are either the most thick-witted programmer
    around, or you are a dishonest troll who goes out of their way to spread
    FUD. And we know you are not thick-witted.




    $ cc --pedantic-errors -o /tmp/a /tmp/t.c
    /tmp/t.c: In function 'main':
    /tmp/t.c:4:7: error: ISO C forbids zero-size array 'A' [-Wpedantic]
        int A[0];
            ^
    /tmp/t.c:5:11: error: ISO C forbids empty initializer braces [-Wpedantic]
        int B[]={};
                ^
    /tmp/t.c:5:7: error: zero or negative size array 'B'
        int B[]={};
            ^
    $

    So, DOES C HAVE ZERO-LENGTH ARRAYS OR NOT?


    No.

    It's a really simple question!


    With a really simple answer, known to anyone who has learned a
    reasonable understanding of the C language - and certainly known to
    anyone who has read the relevant part of the standard to see the answer
    for themselves. (It is in 6.7.6.2p1 - the section with the cryptic and
    barely incomprehensible title "Array declarators", in the chapter titled "Declarations".)

    Because this program easily passes:

      int A[0];

    This one doesn't, qne without needing to use -pedantic  or
    -pedantic-errors:

      int A[-1];

    If the answer is No, why is gcc so reluctant to complain about it
    compared with the -1 size?


    Zero-length arrays are a gcc extension. You can read about them here,
    under the mysterious and hard-to-find section of the gcc manual titled
    "Arrays of Length Zero" :

    <https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html>

    There are a large number of features of C99 that started off as gcc
    extensions in the 90's, before being standardised for C99. Some were
    used pretty much directly, others where changed or adapted before standardisation. Zero-length arrays were not incorporated in C99, but
    their main use-case was taken with a slightly modified syntax as
    flexible array struct members. gcc has an entirely reasonable policy of
    not removing extensions that existing code may rely on, even if there
    are newer standard ways to get a similar effect.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Chris M. Thomasson on Wed Jan 10 11:10:17 2024
    On 10/01/2024 08:40, Chris M. Thomasson wrote:
    On 1/9/2024 9:28 PM, Kaz Kylheku wrote:
    On 2024-01-10, tTh <tth@none.invalid> wrote:
    On 1/10/24 01:40, Bart wrote:
    An easy compiler is one where you just do:

         gcc prog

    and not, at the very least:

         gcc prog.c -prog.exe -std=c11 -pedantic-errors

    Meanwhile I routinely test C programs on half a dozen compilers. I
    can't
    be bothered with all this crap.

         So do as all of us do : put this crap in a Makefile or
         a shell script, and forget about it.

    Please don't get Bart started on makefiles!


    Don't get me started about freaking out for some minutes when I failed
    to use a god damn tab! My makefile would not work. God damn it! Ahhhh,
    that was around 20 years ago.

    The author of "make" described the distinction between tabs and spaces
    to be his worst mistake ever - but use of the tool took off too quickly
    for him to change it.

    The difference between Bart and everyone else is that when other people
    make a tab mistake, they learn from it - and even 20 years on, you still remember. Bart seems to forget things seconds after someone tells him
    how to use any program that he has not written himself. (It takes him a
    little longer to forget how his own programs and languages work.)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Bart on Wed Jan 10 11:16:42 2024
    On 10/01/2024 03:14, Bart wrote:
    On 10/01/2024 02:00, Scott Lurndal wrote:
    Bart <bc@freeuk.cm> writes:
    On 10/01/2024 00:05, Keith Thompson wrote:
    Bart <bc@freeuk.cm> writes:

    An easy compiler is one where you just do:

        gcc prog

    and not, at the very least:

        gcc prog.c -prog.exe -std=c11 -pedantic-errors


    $ functions c
    function c
    {
       gcc -o "$1" -std=c11 -pedantic-errors "$1".c
    }
    $ cat a.c
    #include <stdio.h>

    int
    main(int argc, const char **argv, const char **envp)
    {

         printf("Hello World\n");

         return 0;
    }
    $ c a
    $ ./a
    Hello World

    Can't get any more concise than that.

    Great. Now tell the gcc people that's what it should do ANYWAY.

    But it is /not/ what gcc should do.

    You seem to be mixing up "what Bart wants" with "what countless other
    people want". Write your own tools to revolve around your own selfish
    needs if that's your preference, but don't expect everyone else to
    change their worlds to suit /you/.


    Scott wrote that as an example that might suit you. I am confident that
    the compiler options he mostly uses are different from that - and that
    he uses a variety of different options and different times, and that
    they are different from the options /I/ use or anyone else uses. That's
    why anyone who can actually use a compiler picks their options
    themselves and uses tools to track them (whether it is a bash function,
    a bat file, a makefile, an IDE, or any one of fifty different solutions
    that are far better than whining to the wrong people - or even whining
    to the right people).

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Kaz Kylheku on Wed Jan 10 11:23:30 2024
    On 09/01/2024 23:12, Kaz Kylheku wrote:
    On 2024-01-09, Bart <bc@freeuk.cm> wrote:


    Don't tell me: gcc has some option to warn of using octal literals. So
    your 'successful' language relies on extensive extra tools (all the
    stuff in that support truck) to keep it useable.

    Unfortunately, it is a cynical observation that languages that don't
    require tools tend not to attract people.

    A less cynical observation would suggest that a successful language is
    one that is used a lot, and therefore attracts additional tools that
    make its use better.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to David Brown on Wed Jan 10 12:12:38 2024
    On 10/01/2024 08:37, David Brown wrote:
    On 09/01/2024 23:22, Bart wrote:

    1. C is a language defined by the standards.  Without further
    qualification, "C" refers to the latest published ISO standard -
    currently C17.  But it's also fine to refer to specific standards, such
    as C99.  The standards define the language syntax, constraints, required diagnostics (implementations are free to choose warnings or hard
    errors), and standard library specifications.  There are also many pre-standard C versions, for which "K&R C" is /almost/ a standard.

    2. Almost all C compilers implement some extensions by default.  These extensions are not C, but are compiler-specific language variants.  Some people find them useful, other people prefer to stick to standard C.
    Some extensions are so widely implemented that they may be considered a pseudo-standard, others are very compiler or target specific.

    3. Almost all C compilers have default warnings and errors that do not
    match the standards requirements for C.  Often they do this in both directions - failing to issue diagnostics on things that the standards require, and also issuing errors (halting compilation) for things that
    the standard allows.  Almost all C compilers allow you to tune warnings
    and errors.

    4. Some C compilers aim to provide conforming modes, often for several standard versions, even though they are non-conforming (see 2 and 3
    above) by default.  Others don't try to conform to any particular C standard, and can only very loosely be called a C compiler.

    5. A "C implementation" needs a compiler, a standard library, headers, a linker, perhaps an assembler, and a way to run the program on the
    target.  These might be provided together, or combined from different places.

    My original implementation for Windows used these two files only:

    bcc.exe (about 1MB)
    msvcrt.dll

    bcc.exe includes the compiler, headers and assembler (there was no linker).

    My current one splits it up into 4 files:

    mcc.exe (264KB)
    aa.exe (Assembler, 94KB, still no linker)
    msvcrt.dll
    windows.h (optional)

    A linker is only needed to statically combine my code with that from
    other compilers.

      For example, Microsoft provides everything with their MSVC
    tools.  For gcc-based toolchains, GCC writes the compiler but does not provide binaries.  The assembler and linker often come from the binutils project (which again does not provide binaries), but other assemblers
    and linkers may be used.  Various libraries may be used, depending on
    the target, including glibc, newlib, musl, newlib-nano, redlib, avrlib,
    and many others.  Users can get the parts individually, or more often
    they get packaged toolchains from Debian, Redhat, TDM, mingw-64, MS WSL,
    NXP, TI, Microchip, or many others according to their needs.

    Other smaller C Windows compilers (lccwin, pellesc, dmc are older ones,
    then there is tcc) are self-contained without any of that complexity, or
    the need to divy up the implementation into assorted parts that all live
    in different places or that have to be individually sourced.


    You /know/ how to make gcc and clang compile standard C.  Yet you insist
    on faking ignorance.  You are either the most thick-witted programmer around, or you are a dishonest troll who goes out of their way to spread FUD.  And we know you are not thick-witted.

    Suppose you were given a C program to build. There are no instructions
    (or there are instructions, but they are encoded inside some script in a proprietory language that you don't understand and don't have a tool for).

    How do you know what to tell gcc to compile it?

    Suppose you took a program that you know perfectly well how to compile
    with gcc, but you some reason you don't have it (maybe all instances of
    gcc vanished overnight after some power blockout).

    You have one or two lesser compilers with not many options; what do you
    tell those how to compile that program?

    (I'll make it easy and suggest neither program depends on gnu extensions.)

    Both examples are basically my situation. I'm either building other
    people' software, using a range of compilers (so if it works with A and
    B with no special options, I'm not interested in pandering to gcc with a
    huge laundry list of instructions)...

    ... or I'm generating C code of my own, which used to be compilable with
    half a dozen C compilers with NO SPECIAL OPTIONS, but now I support 3,
    and one or two of them might need a special option.

    So, in my world, I really don't want to know about all that gcc palaver.

    (You go to rent a car. In most cases all you need is a key to open the
    door and turn on the ignition. Except the car made by gcc, which needs a
    dozen different keys, and a long list of configuration options to be set
    first: wheels:4; LHD or RHD: RHD, etc.

    You see my frustration? Using the C language should be driving a car.)

    You already know I have no idea about what version of C is involved,
    only that it needs to pass with the compilers I list using the options
    (or lack of options) that I stipulate at the top of the source file.

    But you will be able to infer that I don't depend in gnu C extensions.

    Zero-length arrays are a gcc extension.

    I wonder how many thousands of lines of code it took to implement such
    an extension?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to bart on Wed Jan 10 14:17:49 2024
    On 10/01/2024 13:12, bart wrote:
    On 10/01/2024 08:37, David Brown wrote:
    On 09/01/2024 23:22, Bart wrote:

    1. C is a language defined by the standards.  Without further
    qualification, "C" refers to the latest published ISO standard -
    currently C17.  But it's also fine to refer to specific standards,
    such as C99.  The standards define the language syntax, constraints,
    required diagnostics (implementations are free to choose warnings or
    hard errors), and standard library specifications.  There are also
    many pre-standard C versions, for which "K&R C" is /almost/ a standard.

    2. Almost all C compilers implement some extensions by default.  These
    extensions are not C, but are compiler-specific language variants.
    Some people find them useful, other people prefer to stick to standard
    C. Some extensions are so widely implemented that they may be
    considered a pseudo-standard, others are very compiler or target
    specific.

    3. Almost all C compilers have default warnings and errors that do not
    match the standards requirements for C.  Often they do this in both
    directions - failing to issue diagnostics on things that the standards
    require, and also issuing errors (halting compilation) for things that
    the standard allows.  Almost all C compilers allow you to tune
    warnings and errors.

    4. Some C compilers aim to provide conforming modes, often for several
    standard versions, even though they are non-conforming (see 2 and 3
    above) by default.  Others don't try to conform to any particular C
    standard, and can only very loosely be called a C compiler.

    5. A "C implementation" needs a compiler, a standard library, headers,
    a linker, perhaps an assembler, and a way to run the program on the
    target.  These might be provided together, or combined from different
    places.

    My original implementation for Windows used these two files only:


    No one cares. It does not matter how any particular C implementation
    does it, it only matters that there are many ways to package them. I
    gave examples purely as examples. The question is, do you understand
    what I wrote above, and is anything there new or surprising to you?


      For example, Microsoft provides everything with their MSVC tools.
    For gcc-based toolchains, GCC writes the compiler but does not provide
    binaries.  The assembler and linker often come from the binutils
    project (which again does not provide binaries), but other assemblers
    and linkers may be used.  Various libraries may be used, depending on
    the target, including glibc, newlib, musl, newlib-nano, redlib,
    avrlib, and many others.  Users can get the parts individually, or
    more often they get packaged toolchains from Debian, Redhat, TDM,
    mingw-64, MS WSL, NXP, TI, Microchip, or many others according to
    their needs.


    No one cares.



    You /know/ how to make gcc and clang compile standard C.  Yet you
    insist on faking ignorance.  You are either the most thick-witted
    programmer around, or you are a dishonest troll who goes out of their
    way to spread FUD.  And we know you are not thick-witted.

    Suppose you were given a C program to build. There are no instructions
    (or there are instructions, but they are encoded inside some script in a proprietory language that you don't understand and don't have a tool for).

    How do you know what to tell gcc to compile it?

    I might make some guesses, because I know the typical flags needed for
    gcc (as do you, as you have been told them countless times). If these
    don't work and there are no clear instructions, I might ask the program developers for more information. I might google for it, or ask
    generally in appropriate forums (which comp.lang.c is not).

    Without the slightest doubt, I can say that if it were possible to build
    it using /your/ C compiler, and does not use extensions unsupported by
    gcc, then I could get it to build with gcc without much trouble. I
    think the same would apply to pretty much anyone who has some experience
    using gcc from the command line.


    Suppose you took a program that you know perfectly well how to compile
    with gcc, but you some reason you don't have it (maybe all instances of
    gcc vanished overnight after some power blockout).

    Now you sound as silly as that C90 fanatic who turns up here occasionally.

    <snip>


    Zero-length arrays are a gcc extension.

    I wonder how many thousands of lines of code it took to implement such
    an extension?


    I have no idea. It was a good idea at the time, because C90 did not
    have flexible array members. It is now pretty much unnecessary, though
    I am sure some people use "[0]" instead of "[]" in their flexible array
    struct members. And some might take advantage of gcc's slightly greater flexibility here than the C standards define.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to David Brown on Wed Jan 10 14:31:35 2024
    On 10/01/2024 13:17, David Brown wrote:
    On 10/01/2024 13:12, bart wrote:


      For example, Microsoft provides everything with their MSVC tools.
    For gcc-based toolchains, GCC writes the compiler but does not
    provide binaries.  The assembler and linker often come from the
    binutils project (which again does not provide binaries), but other
    assemblers and linkers may be used.  Various libraries may be used,
    depending on the target, including glibc, newlib, musl, newlib-nano,
    redlib, avrlib, and many others.  Users can get the parts
    individually, or more often they get packaged toolchains from Debian,
    Redhat, TDM, mingw-64, MS WSL, NXP, TI, Microchip, or many others
    according to their needs.


    No one cares.

    But you cared enough to list all those complicated ways that some have implemented C. It sounded almost lovingly.

    It's as though you're looking down disdainfully at all those seeking to
    produce smaller simpler products: Here, THIS is how you produce a C
    compiler! It has to be Big! And Slow! It has to be Complicated! It has
    to have lots of Buttons!

    You know, the most remarkable and impressive product for me is the Tiny
    C compiler. It's Small. It's Fast. It's Simple.

    For my generated C code, only 3 files of Tiny C, totalling 230KB, are
    needed to turn it near-instantly into executable code. As a compiler
    backend target, it's a solution that is 100 times smaller and 100 times
    faster than LLVM.

    Some people have different opinions about this stuff. C isn't your own proprietory language that should only be processed with your stipulated
    tool sets.

    Anybody can implement their own ideas of how it should work. There are
    reasons why those people put considerable efforts into those smaller, self-contained and more user-friendly products.

    Oh, I forgot, you don't care.

    OK. Well I don't really care about what you're into either! I certainly
    don't want to fuck about with -Wextra -Wpedantic -Wall -std=this -ofile
    and all that nonsense, when a 10 seconds earlier I'd simpler done 'tcc
    prog.c'.

    For me a compiler should be as utilitarian and simple to use as a light
    switch.

    Without the slightest doubt, I can say that if it were possible to build
    it using /your/ C compiler, and does not use extensions unsupported by
    gcc, then I could get it to build with gcc without much trouble.

    You seem to be acknowledging the benefits of passing a code base through
    a lesser compiler.


      I
    think the same would apply to pretty much anyone who has some experience using gcc from the command line.


    Suppose you took a program that you know perfectly well how to compile
    with gcc, but you some reason you don't have it (maybe all instances
    of gcc vanished overnight after some power blockout).

    Now you sound as silly as that C90 fanatic who turns up here occasionally.

    There are any number of actual scenarios where that can happen. That is,
    you don't have access to your favourite compiler.

    I actually can supply a C program, and the C compiler needed to build it
    into an executable, on a floppy disk, in most cases. It can be as little
    as 3 files (source, compiler, assembler); it will be instantly obvious
    if there's one missing, and what the implications would be.

    What is the minimum number of actual (ie. not ZIP) files needed to
    bundle gcc in the same way? Give me a number. (I guess you don't know; I thought you knew your tools inside out!)

    I wonder how many thousands of lines of code it took to implement such
    an extension?


    I have no idea.  It was a good idea at the time, because C90 did not
    have flexible array members.  It is now pretty much unnecessary, though
    I am sure some people use "[0]" instead of "[]" in their flexible array struct members.  And some might take advantage of gcc's slightly greater flexibility here than the C standards define.

    I was thinking more of this:

    void* table[] = {
    // &a,
    // &b,
    // &c,
    };

    Temporarily comment out those entries (maybe they don't exist yet, or to
    test how well program logic copes with an empty list).

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to David Brown on Wed Jan 10 14:49:24 2024
    David Brown <david.brown@hesbynett.no> writes:
    On 10/01/2024 03:14, Bart wrote:
    On 10/01/2024 02:00, Scott Lurndal wrote:
    Bart <bc@freeuk.cm> writes:
    On 10/01/2024 00:05, Keith Thompson wrote:
    Bart <bc@freeuk.cm> writes:

    An easy compiler is one where you just do:

        gcc prog

    and not, at the very least:

        gcc prog.c -prog.exe -std=c11 -pedantic-errors


    $ functions c
    function c
    {
       gcc -o "$1" -std=c11 -pedantic-errors "$1".c
    }
    $ cat a.c
    #include <stdio.h>

    int
    main(int argc, const char **argv, const char **envp)
    {

         printf("Hello World\n");

         return 0;
    }
    $ c a
    $ ./a
    Hello World

    Can't get any more concise than that.

    Great. Now tell the gcc people that's what it should do ANYWAY.

    But it is /not/ what gcc should do.

    You seem to be mixing up "what Bart wants" with "what countless other
    people want". Write your own tools to revolve around your own selfish
    needs if that's your preference, but don't expect everyone else to
    change their worlds to suit /you/.


    Scott wrote that as an example that might suit you.

    I am confident that
    the compiler options he mostly uses are different from that - and that
    he uses a variety of different options and different times, and that
    they are different from the options /I/ use or anyone else uses.

    Indeed. I use a Makefile and the Makefile.defs include alone has 373
    lines - something sure to piss Bart off. Of course, the project
    has:

    SLOC Directory SLOC-by-Language (Sorted)
    7316068 include ansic=7274603,cpp=41465
    899374 tests python=763294,ansic=82789,asm=34873,cpp=18013,sh=405 885492 io cpp=603113,ansic=281285,python=466,sh=324,asm=304 133342 processor cpp=131855,python=1487
    133153 3rd_party cpp=133033,sh=78,python=42
    26803 tools python=17834,cpp=4300,sh=1903,ansic=1459,perl=1199,
    ruby=108
    16754 gen ansic=16754
    10955 platform cpp=10955
    8392 common cpp=8392
    5290 bin cpp=5118,python=172
    2204 cpc cpp=2204
    1883 top_dir cpp=1883
    1430 noc cpp=1430
    560 shim cpp=560

    SLOC doesn't include whitespace or comments.

    That's about 10 million lines of code across several hundred source
    and include files.

    Yet, the entire application can be built with

    $ make

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to bart on Wed Jan 10 14:51:23 2024
    bart <bc@freeuk.com> writes:

    My original implementation for Windows used these two files only:

    bcc.exe (about 1MB)


    Why do you think anyone here cares?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to bart on Wed Jan 10 16:51:41 2024
    On 10/01/2024 15:31, bart wrote:
    On 10/01/2024 13:17, David Brown wrote:
    On 10/01/2024 13:12, bart wrote:


      For example, Microsoft provides everything with their MSVC tools.
    For gcc-based toolchains, GCC writes the compiler but does not
    provide binaries.  The assembler and linker often come from the
    binutils project (which again does not provide binaries), but other
    assemblers and linkers may be used.  Various libraries may be used,
    depending on the target, including glibc, newlib, musl, newlib-nano,
    redlib, avrlib, and many others.  Users can get the parts
    individually, or more often they get packaged toolchains from
    Debian, Redhat, TDM, mingw-64, MS WSL, NXP, TI, Microchip, or many
    others according to their needs.


    No one cares.

    But you cared enough to list all those complicated ways that some have implemented C. It sounded almost lovingly.

    I made no comment on what I liked or did not like about it - it was a
    factual list. I made it for /your/ benefit.

    I don't care about how different C toolchains are made, or by whom, or
    whether they are big or small. I care that the ones /I/ need are
    obtainable in a suitable manner for /my/ needs, running on /my/ choice
    of OS, and for /my/ choice of target. They must be available at an
    appropriate cost, and it must be possible to archive them indefinitely
    and use them without any awkward "license protect" systems. But it does
    not bother me if the package is 1 MB or 1 GB, 1 program or 1000 programs
    - those are not relevant concerns for professional programmers.

    I do, however, try my best to help people in groups like this. That
    includes writing posts to try to help and educate /you/. It gets
    increasingly frustrating, however.

    You know, the most remarkable and impressive product for me is the Tiny
    C compiler. It's Small. It's Fast. It's Simple.

    It is almost entirely useless, except for the very, very few people who
    /need/ a C compiler that is very small. In the days of 3.5" floppy
    disks for rescue disks, it was useful. In the days of 16 GB USB flash
    devices costing pennies, it is irrelevant.

    It is still remarkable, and still impressive - I have no disagreements
    there. It will always be an impressive achievement - that will not
    change with time. It might also be fun to play with. But it is still basically useless. It is no longer a particularly useful or needed
    tool. Lots of C compilers were once relevant, and now are not, except
    perhaps for a few very niche uses.


    Anybody can implement their own ideas of how it should work. There are reasons why those people put considerable efforts into those smaller, self-contained and more user-friendly products.

    "User-friendly" /is/ something worth caring about. So is ease of
    installation. Small size, self-contained binaries - those are not of
    concern to users. It is absolutely a good thing for many people that a
    tool is easy to understand and use - but no one cares what goes on
    behind the scenes to make it work, as long as it works fast enough to be practical on sensible computers. (A half-arsed compiler that implements
    some unknown and undocumented subset of C, with random changes at the
    whim of the developer, cannot ever be "user-friendly" for anyone other
    than said developer.)

    To some extent, "user-friendly" will be at odds with flexibility and
    features, so there is a balance to be found.



    For me a compiler should be as utilitarian and simple to use as a light switch.

    For me, a compiler should be something found in the real world and not a deluded fantasy.


    Without the slightest doubt, I can say that if it were possible to
    build it using /your/ C compiler, and does not use extensions
    unsupported by gcc, then I could get it to build with gcc without much
    trouble.

    You seem to be acknowledging the benefits of passing a code base through
    a lesser compiler.


    You seem to be inventing things that I never wrote or implied.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From tTh@21:1/5 to bart on Wed Jan 10 17:46:37 2024
    On 1/10/24 15:31, bart wrote:

    What is the minimum number of actual (ie. not ZIP) files needed to
    bundle gcc in the same way? Give me a number. (I guess you don't know; I thought you knew your tools inside out!)

    $ sudo apt install gcc

    --
    +---------------------------------------------------------------------+
    | https://tube.interhacker.space/a/tth/video-channels | +---------------------------------------------------------------------+

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From James Kuyper@21:1/5 to Bart on Wed Jan 10 11:22:23 2024
    Bart <bc@freeuk.cm> writes:
    On 10/01/2024 00:49, Keith Thompson wrote:
    ...
    I spent some minutes trying to find where this zero-length array
    business originated. It was from you:

    No, it originated from the C standard: "... the [ and ] may delimit an expression ... If the expression is a constant expression, it shall have
    a value greater than zero." (6.7.6.2p1).

    No, it doesn't. C doesn't have zero-length arrays.

    You say "C", but don't say which version or which common
    dialect.

    This group is not specific to any particular compiler. Therefore, unless otherwise specified, when I refer to C, I an usually referring to
    standard C unless otherwise specified, and more particularly, to the
    latest approved version of C, unless otherwise specified. Many other
    people follow similar policies. There's very little (if anything) that
    can usefully be said about all variants of C - the only meaningful
    things you can say are about particular variants.

    ... Apperently the most commonly used dialect is gnu C,

    The Gnu C compiler is one of the most widely used, but a lot of people
    use it to compile standard C rather than Gnu C - unlike you, most
    programmers don't find it particularly difficult to figure out how to
    arrange that. I have no idea whether it's more or less popular than
    standard C.

    I'm still none the wiser. Can I actually use zero-length arrays? Yes,
    No, Maybe, Only with this or that compiler or that bunch of options.

    You can only use zero-length arrays with particular compilers. The ones
    that do allow it, generally use options to disable that feature, not to
    enable it.

    You have two main options:
    1. Write separate versions of your code for each compiler you target,
    possibly controlled by conditional compilation or makefiles.
    2. Choose a particular version of the C standard, and write code that
    works with that version. Then, for each target compiler, figure out how
    to put it into a mode where it will compile according to that version.
    The standard leaves many things unspecified, but it's quite feasible to
    write code that has the desired behavior regardless of which choice the compiler makes for any unspecified behavior.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Scott Lurndal on Wed Jan 10 18:13:01 2024
    On 10/01/2024 14:49, Scott Lurndal wrote:
    David Brown <david.brown@hesbynett.no> writes:
    On 10/01/2024 03:14, Bart wrote:
    On 10/01/2024 02:00, Scott Lurndal wrote:
    Bart <bc@freeuk.cm> writes:
    On 10/01/2024 00:05, Keith Thompson wrote:
    Bart <bc@freeuk.cm> writes:

    An easy compiler is one where you just do:

        gcc prog

    and not, at the very least:

        gcc prog.c -prog.exe -std=c11 -pedantic-errors


    $ functions c
    function c
    {
       gcc -o "$1" -std=c11 -pedantic-errors "$1".c
    }
    $ cat a.c
    #include <stdio.h>

    int
    main(int argc, const char **argv, const char **envp)
    {

         printf("Hello World\n");

         return 0;
    }
    $ c a
    $ ./a
    Hello World

    Can't get any more concise than that.

    Great. Now tell the gcc people that's what it should do ANYWAY.

    But it is /not/ what gcc should do.

    You seem to be mixing up "what Bart wants" with "what countless other
    people want". Write your own tools to revolve around your own selfish
    needs if that's your preference, but don't expect everyone else to
    change their worlds to suit /you/.


    Scott wrote that as an example that might suit you.

    I am confident that
    the compiler options he mostly uses are different from that - and that
    he uses a variety of different options and different times, and that
    they are different from the options /I/ use or anyone else uses.

    Indeed. I use a Makefile and the Makefile.defs include alone has 373
    lines - something sure to piss Bart off. Of course, the project
    has:

    SLOC Directory SLOC-by-Language (Sorted)
    7316068 include ansic=7274603,cpp=41465
    899374 tests python=763294,ansic=82789,asm=34873,cpp=18013,sh=405 885492 io cpp=603113,ansic=281285,python=466,sh=324,asm=304 133342 processor cpp=131855,python=1487
    133153 3rd_party cpp=133033,sh=78,python=42
    26803 tools python=17834,cpp=4300,sh=1903,ansic=1459,perl=1199,
    ruby=108
    16754 gen ansic=16754
    10955 platform cpp=10955
    8392 common cpp=8392
    5290 bin cpp=5118,python=172
    2204 cpc cpp=2204
    1883 top_dir cpp=1883
    1430 noc cpp=1430
    560 shim cpp=560

    SLOC doesn't include whitespace or comments.

    That's about 10 million lines of code across several hundred source
    and include files.

    Yet, the entire application can be built with

    $ make

    I bet you can't. There's something missing. Unless the implicit file
    that make uses happens to be in that '$' directory. Usually you have to navigate to the project first.

    So of the two parts a typical build needs: the program, script or
    process that is launched to do the work, and the input needed, one has
    already been provided.

    A more sensible way would be to require:

    make inputfile

    Then you could build stuff from anywhere.

    BTW well done for discovering that, if a build sequence requires
    multiple commands to be executed, you can put those into a 'script' that requires a single invocation.

    Just as long as you realise that this isn't doing away with those
    multiple steps, it's hiding them away not simplifying, and adding ONE
    MORE step to the process.

    The stuff I do is genuine simplification.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Scott Lurndal on Wed Jan 10 17:58:46 2024
    On 10/01/2024 14:51, Scott Lurndal wrote:
    bart <bc@freeuk.com> writes:

    My original implementation for Windows used these two files only:

    bcc.exe (about 1MB)


    Why do you think anyone here cares?

    Why do you think anyone cares about your makefiles?

    It's a counter-example to somebody who might think implementations
    really need to be as elaborate as DB was suggesting.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to David Brown on Wed Jan 10 18:57:56 2024
    On 10/01/2024 15:51, David Brown wrote:
    On 10/01/2024 15:31, bart wrote:

    It is almost entirely useless, except for the very, very few people who /need/ a C compiler that is very small.  In the days of 3.5" floppy
    disks for rescue disks, it was useful.  In the days of 16 GB USB flash devices costing pennies, it is irrelevant.

    16 GB can hold a great deal of useful DATA: images, audio and video for example. It also tends to be consumed sequentially.

    But it is a mistake to think it makes it OK have programs, ie. CODE,
    that are 0.1GB, 1GB or 10GB in size. Code complexity doesn't scale
    linearly as data does.

    It means complicated, slow, cumbersome programs, that require extra
    power and memory resources. And buggy ones.

    It is still remarkable, and still impressive - I have no disagreements there.  It will always be an impressive achievement - that will not
    change with time.  It might also be fun to play with.  But it is still basically useless.  It is no longer a particularly useful or needed
    tool.  Lots of C compilers were once relevant, and now are not, except perhaps for a few very niche uses.


    Anybody can implement their own ideas of how it should work. There are
    reasons why those people put considerable efforts into those smaller,
    self-contained and more user-friendly products.

    "User-friendly" /is/ something worth caring about.  So is ease of installation.  Small size, self-contained binaries - those are not of concern to users.

    It means I can email you both a source file, and the compiler or
    interpreter needed to build or run it (setting aside AV issues). Imagine supplying gcc or CPython as an email attachment!

    Somebody else may be implementing a language with C backend. Rather than requiring their users to install a C system which will dwarf there own
    product, or that is so slow that it would be like hitting a break wall
    as soon as they invoke it, a small, fast C compiler could be just
    bundled and invoked transparently as there is no disproportionate latency.


    It is absolutely a good thing for many people that a
    tool is easy to understand and use - but no one cares what goes on
    behind the scenes to make it work, as long as it works fast enough to be practical on sensible computers.  (A half-arsed compiler that implements some unknown and undocumented subset of C, with random changes at the
    whim of the developer, cannot ever be "user-friendly" for anyone other
    than said developer.)

    This is what I mean by user-friendly:

    c:\c>gcc hello
    C:\tdm\bin\ld.exe: cannot find hello: No such file or directory
    collect2.exe: error: ld returned 1 exit status

    c:\c>gcc hello.c

    c:\c>hello
    'hello' is not recognized as an internal or external command,
    operable program or batch file.

    c:\c>gcc hello.c -hello
    gcc: error: unrecognized command-line option '-h'

    c:\c>gcc hello.c -ohello

    c:\c>hello
    Hello, World! 00007ff6923d13b4

    The above tries to compile hello.c with gcc. The first attempt doesn't
    work. The second does, as no error is displayed. But what exactly has it compiled it to? As there is no file called 'hello.exe.

    It needs '-o'. The third attempt has a typo, the fourth finally works.

    The user friendly version is this:

    c:\c>mcc hello # same input as given to gcc
    Compiling hello.c to hello.exe

    c:\c>hello
    Hello, World! 0000000000401000

    The compiler doesn't need the extension. It tells you exactly what it is
    doing, incuding where it will write the output.

    Please don't talk to me about user-friendly, you don't seem to have a clue.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to bart on Wed Jan 10 19:16:55 2024
    bart <bc@freeuk.com> writes:
    On 10/01/2024 14:51, Scott Lurndal wrote:
    bart <bc@freeuk.com> writes:

    My original implementation for Windows used these two files only:

    bcc.exe (about 1MB)


    Why do you think anyone here cares?

    Why do you think anyone cares about your makefiles?

    What makes you think I care if anyone cares about my makefiles?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Keith Thompson on Wed Jan 10 19:24:00 2024
    On 10/01/2024 18:39, Keith Thompson wrote:
    bart <bc@freeuk.com> writes:
    On 10/01/2024 14:49, Scott Lurndal wrote:
    [...]
    Yet, the entire application can be built with
    $ make

    I bet you can't. There's something missing. Unless the implicit file
    that make uses happens to be in that '$' directory. Usually you have
    to navigate to the project first.

    That '$' is a shell prompt, not a directory.

    Yes, you have to "cd" (like "chdir" on Windows) to the project directory before typing "make". You also have to make sure the computer is
    powered on, login, launch a shell, and maybe one or two other things
    before you get to that point.

    You're missing the point. SL is making a big deal about the fact that
    you can type 'make' without providing any apparent input. But that input
    is provided when you use 'cd' to get the relevant folder.

    Or do you think that the 'make' command provided will build that
    specific project irrespective of the CWD?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to bart on Wed Jan 10 20:27:43 2024
    On 10/01/2024 18:58, bart wrote:
    On 10/01/2024 14:51, Scott Lurndal wrote:
    bart <bc@freeuk.com> writes:

    My original implementation for Windows used these two files only:

        bcc.exe           (about 1MB)


    Why do you think anyone here cares?

    Why do you think anyone cares about your makefiles?


    Scott was trying to help you (and if you had done him the courtesy of
    reading his post, you'd see it was not using make).

    It's a counter-example to somebody who might think implementations
    really need to be as elaborate as DB was suggesting.


    Again, you are making stuff up. I never suggested C implementations
    need to be elaborate. I said they could be elaborate, or not, but that
    no one really cares about sizes or numbers of files. (I agree that some
    people care about being complicated to use. gcc is only complicated if
    you choose to use a lot of options - very few are needed, and most
    people capable of programming in C can google for their gcc option needs
    in a few minutes at most.)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to David Brown on Wed Jan 10 19:23:57 2024
    On 2024-01-10, David Brown <david.brown@hesbynett.no> wrote:
    On 09/01/2024 23:12, Kaz Kylheku wrote:
    On 2024-01-09, Bart <bc@freeuk.cm> wrote:


    Don't tell me: gcc has some option to warn of using octal literals. So
    your 'successful' language relies on extensive extra tools (all the
    stuff in that support truck) to keep it useable.

    Unfortunately, it is a cynical observation that languages that don't
    require tools tend not to attract people.

    A less cynical observation would suggest that a successful language is
    one that is used a lot, and therefore attracts additional tools that
    make its use better.

    Also, there is no language for which the implementations have the final
    word on any program, such that the users cannot imagine needing new
    kinds of diagnostics.

    What is a diagnostic? It's something which evaluate a
    truth-valued proposition about a program, and reports if it
    is true.

    A program has a large number of properties about which true propositions
    can be stated. Any of those truths could potentially be a useful
    diagnostic to someone.

    It is not realistic to expect that a language specification can state
    all the propositions that may be diagnosed, such that no others may be diagnosed.

    Furthermore, users may want to be informed about certain programs which
    are correct by the language, but which have some undesirable property,
    specific to their requirements, such as a local coding standard or
    even application-specific requirements.

    E.g. a C program which puts sensitive info into an improperly procured temporary file might be correct by ISO C, but we can imagine a
    sophisticated diagnostic identifying the situation.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to bart on Wed Jan 10 20:42:52 2024
    On 10/01/2024 20:24, bart wrote:
    On 10/01/2024 18:39, Keith Thompson wrote:
    bart <bc@freeuk.com> writes:
    On 10/01/2024 14:49, Scott Lurndal wrote:
    [...]
    Yet, the entire application can be built with
    $ make

    I bet you can't. There's something missing. Unless the implicit file
    that make uses happens to be in that '$' directory. Usually you have
    to navigate to the project first.

    That '$' is a shell prompt, not a directory.

    Yes, you have to "cd" (like "chdir" on Windows) to the project directory
    before typing "make".  You also have to make sure the computer is
    powered on, login, launch a shell, and maybe one or two other things
    before you get to that point.

    You're missing the point. SL is making a big deal about the fact that
    you can type 'make' without providing any apparent input. But that input
    is provided when you use 'cd' to get the relevant folder.

    Or do you think that the 'make' command provided will build that
    specific project irrespective of the CWD?

    The input is the makefile, and all the source files. Did you think
    Scott was claiming that just typing "make" somewhere on any Linux
    machine would build his vast project, without having the makefile(s) or
    source files available or being in the right directory?

    And are you still so ignorant about "make" that you think it is
    equivalent to a script?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Kaz Kylheku on Wed Jan 10 20:46:35 2024
    On 10/01/2024 20:23, Kaz Kylheku wrote:
    On 2024-01-10, David Brown <david.brown@hesbynett.no> wrote:
    On 09/01/2024 23:12, Kaz Kylheku wrote:
    On 2024-01-09, Bart <bc@freeuk.cm> wrote:


    Don't tell me: gcc has some option to warn of using octal literals. So >>>> your 'successful' language relies on extensive extra tools (all the
    stuff in that support truck) to keep it useable.

    Unfortunately, it is a cynical observation that languages that don't
    require tools tend not to attract people.

    A less cynical observation would suggest that a successful language is
    one that is used a lot, and therefore attracts additional tools that
    make its use better.

    Also, there is no language for which the implementations have the final
    word on any program, such that the users cannot imagine needing new
    kinds of diagnostics.

    What is a diagnostic? It's something which evaluate a
    truth-valued proposition about a program, and reports if it
    is true.

    A program has a large number of properties about which true propositions
    can be stated. Any of those truths could potentially be a useful
    diagnostic to someone.

    It is not realistic to expect that a language specification can state
    all the propositions that may be diagnosed, such that no others may be diagnosed.

    Furthermore, users may want to be informed about certain programs which
    are correct by the language, but which have some undesirable property, specific to their requirements, such as a local coding standard or
    even application-specific requirements.

    E.g. a C program which puts sensitive info into an improperly procured temporary file might be correct by ISO C, but we can imagine a
    sophisticated diagnostic identifying the situation.


    Agreed (on all points).

    People have wildly different requirements. The gcc flags I want for my
    builds would be entirely unsuitable for the builds you do, and vice
    versa. Bart's idea that we can all use a single simple command is
    because he lives in a tiny little bubble with himself at the centre, and
    other people's needs or wants are so irrelevant to him that he doesn't
    even see that they exist. As far as Bart's concerned, it's easier to
    change his language and compiler than to use a flag when invoking a tool.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to bart on Wed Jan 10 20:55:35 2024
    On 10/01/2024 19:57, bart wrote:
    On 10/01/2024 15:51, David Brown wrote:
    On 10/01/2024 15:31, bart wrote:

    It is almost entirely useless, except for the very, very few people
    who /need/ a C compiler that is very small.  In the days of 3.5"
    floppy disks for rescue disks, it was useful.  In the days of 16 GB
    USB flash devices costing pennies, it is irrelevant.

    16 GB can hold a great deal of useful DATA: images, audio and video for example. It also tends to be consumed sequentially.

    What the *beep* are you talking about? Does your compiler consist of 1
    MB of program and 16 GB of video about how much better it is than
    everything else?

    The prime use of tcc, when it had a use, was for rescue disks and other situations when you needed to boot from small mediums and have a running
    system of some sort without installing on a hard disk. That was
    originally a floppy, so tiny tools were vital. Then boot CDs became
    popular - size was no longer an issue unless you wanted a mini CD. By
    the time DVD's were common for the job, size of tools was irrelevant.

    I've just checked my main IT supplier - the /cheapest/ USB stick they
    have is 32 GB.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to bart on Wed Jan 10 20:20:27 2024
    On 10/01/2024 19:24, bart wrote:
    On 10/01/2024 18:39, Keith Thompson wrote:
    bart <bc@freeuk.com> writes:
    On 10/01/2024 14:49, Scott Lurndal wrote:
    [...]
    Yet, the entire application can be built with
    $ make

    I bet you can't. There's something missing. Unless the implicit file
    that make uses happens to be in that '$' directory. Usually you have
    to navigate to the project first.

    That '$' is a shell prompt, not a directory.

    Yes, you have to "cd" (like "chdir" on Windows) to the project directory
    before typing "make".  You also have to make sure the computer is
    powered on, login, launch a shell, and maybe one or two other things
    before you get to that point.

    You're missing the point. SL is making a big deal about the fact that
    you can type 'make' without providing any apparent input. But that input
    is provided when you use 'cd' to get the relevant folder.

    Or do you think that the 'make' command provided will build that
    specific project irrespective of the CWD?

    Let me put it another way; how does:

    $ make

    know it is to build that project, and not any other? The default input
    is presumably "./makefile", with the key bit being that ".".

    So, when claiming that you only need to type one thing to start the
    process, it is disingenuous to leave out that part out.

    After all, if you wanted to build project A, and, separately, project B,
    you can't do both of them like this:

    $ make
    $ make

    We've covered this before. While I quite like sensible defaults, where C compilers tend to be sticklers for dotting all the Is, 'make' goes a
    little too far the other way.

    Before typing 'make', you'd better be sure you're in the right place!

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to David Brown on Wed Jan 10 20:49:03 2024
    On 10/01/2024 19:55, David Brown wrote:
    On 10/01/2024 19:57, bart wrote:
    On 10/01/2024 15:51, David Brown wrote:
    On 10/01/2024 15:31, bart wrote:

    It is almost entirely useless, except for the very, very few people
    who /need/ a C compiler that is very small.  In the days of 3.5"
    floppy disks for rescue disks, it was useful.  In the days of 16 GB
    USB flash devices costing pennies, it is irrelevant.

    16 GB can hold a great deal of useful DATA: images, audio and video
    for example. It also tends to be consumed sequentially.

    What the *beep* are you talking about?  Does your compiler consist of 1
    MB of program and 16 GB of video about how much better it is than
    everything else?

    The prime use of tcc, when it had a use, was for rescue disks and other situations when you needed to boot from small mediums and have a running system of some sort without installing on a hard disk.  That was
    originally a floppy, so tiny tools were vital.  Then boot CDs became
    popular - size was no longer an issue unless you wanted a mini CD.  By
    the time DVD's were common for the job, size of tools was irrelevant.

    I've just checked my main IT supplier - the /cheapest/ USB stick they
    have is 32 GB.


    I'm starting to wonder how dense you can be. But I know that's not the
    the case; just unreceptive to certain concepts or unwilling to consider
    them.

    You are claiming that there is no point in limiting the size of code,
    because after all you can buy 16GB or 32GB memory sticks.

    So I'm not sure what your point is. Does the prevalence of very cheap
    storage make it OK to have code that is 10, 100 or 1000 times bigger
    than it need be?

    Apparently so, according to you. Because of course there are no
    consequences of that.

    BTW these are some programs which are part of my gcc:

    nasm.exe 1.4MB
    ld.exe 1.5MB
    as.exe 1.6MB

    The first one, perhaps first two, will just fit onto one floppy, which I
    think is 1.44MiB. The last is just over.

    So three standalone programs that are still, in 2024, at floppy disk
    scale. Why is that, given that you can buy 32,000MB storages devices for 'pennies'?

    Note that I did not write these programs. I don't have a monopoly on
    writing smallish, self-contained applications.

    Will you also dismiss those apps for being too small to be of
    consequence? Or will you admit that sometimes the scale of a task isn't
    great enough to warrant a huge executable?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to bart on Wed Jan 10 21:43:37 2024
    On 2024-01-10, bart <bc@freeuk.com> wrote:
    So, when claiming that you only need to type one thing to start the
    process, it is disingenuous to leave out that part out.

    That's just knowing where the project is. That's something external to
    the project; it's not a build secret hidden in the project itself, but
    likely something the user themselves chose.

    It's the same for any project, in any language using any build
    procedure; they all have a location, and the first step is usually
    changing to that location. Some users will cd to the project root even
    before looking at any instructions.

    (Perhaps the instructions will tell them smoething else, like
    create a build directory somewhere, change to /that/ directory and
    from the reference some build script in the unpacked tree.)

    Before typing 'make', you'd better be sure you're in the right place!

    The instructions for that project can't even tell you what that is.

    It's "wherever you unpacked/cloned the project".

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Kaz Kylheku on Wed Jan 10 22:36:41 2024
    On 10/01/2024 21:43, Kaz Kylheku wrote:
    On 2024-01-10, bart <bc@freeuk.com> wrote:
    So, when claiming that you only need to type one thing to start the
    process, it is disingenuous to leave out that part out.

    That's just knowing where the project is. That's something external to
    the project; it's not a build secret hidden in the project itself, but
    likely something the user themselves chose.

    It's the same for any project, in any language using any build
    procedure; they all have a location, and the first step is usually
    changing to that location. Some users will cd to the project root even
    before looking at any instructions.

    (Perhaps the instructions will tell them smoething else, like
    create a build directory somewhere, change to /that/ directory and
    from the reference some build script in the unpacked tree.)

    Before typing 'make', you'd better be sure you're in the right place!

    The instructions for that project can't even tell you what that is.

    It's "wherever you unpacked/cloned the project".


    I once claimed that with the build schemes I prefer, you usually have to
    type only two things:

    mm prog # my language

    mcc @prog # C; also works across compilers

    bcc -auto prog # my older compiler for projects written
    # to certain rules

    But 'make' was claimed to be superior because you only had to type one
    thing:

    make

    Never mind that 'makefile' might contains 100s or even 1000s of lines in cryptic syntax that you have to write first, while, in my first example,
    no such file is needed. And in the second, it is a mere list of source
    files.

    At least with my schemes, I can do:

    mm prog1
    mm prog2
    mm \loc\prog3

    All from the same place. Also, if you are in the wrong location, it will
    likely pick up the error, as there will be no 'prog1'. With make, it'll
    spend the next 30 minutes building the wrong project!

    (On one project that came up a few months ago, there were two makefiles,
    one inside a nested folder. But github or something only showed the the
    nested folder, or something like that.

    It caused confusion, and made things go badly wrong, things that would
    have been picked up if 'make' needed the name of an input, as you surely wouldn't have used the same name for both.

    This seems common in Unix: let's call every input file 'makefile', and
    every output file 'a.out'!)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From tTh@21:1/5 to bart on Thu Jan 11 00:30:02 2024
    On 1/10/24 19:57, bart wrote:

    The above tries to compile hello.c with gcc. The first attempt doesn't
    work. The second does, as no error is displayed. But what exactly has it compiled it to? As there is no file called 'hello.exe.

    o ____ _____ _____ __ __
    o | _ \ |_ _| | ___| | \/ |
    o | |_) | | | | |_ | |\/| |
    o | _ < | | | _| | | | |
    o |_| \_\ |_| |_| |_| |_|
    o



    --
    +---------------------------------------------------------------------+
    | https://tube.interhacker.space/a/tth/video-channels | +---------------------------------------------------------------------+

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to tTh on Thu Jan 11 01:14:19 2024
    On 10/01/2024 23:30, tTh wrote:
    On 1/10/24 19:57, bart wrote:

    The above tries to compile hello.c with gcc. The first attempt doesn't
    work. The second does, as no error is displayed. But what exactly has
    it compiled it to? As there is no file called 'hello.exe.

      o     ____    _____   _____   __  __
      o    |  _ \  |_   _| |  ___| |  \/  |
      o    | |_) |   | |   | |_    | |\/| |
      o    |  _ <    | |   |  _|   | |  | |
      o    |_| \_\   |_|   |_|     |_|  |_|
      o


    I see. It doesn't matter how complex, unfriendly, inconvenient or
    error-prone using a piece of software is, it's all fine so long as you
    tell the user:

    R T F M?

    That makes up for designing it properly?

    Instead of getting rid of unnecessary hoops you have to jump through,
    you'd just write a thicker instruction manual and sell more training
    courses. (And force people to use 'make' - another dozen hoops.)

    Plus of course you can command more pay for mastering that contrived complexity.

    What /I/ do is strive to get rid of those hoops.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to bart on Thu Jan 11 02:29:36 2024
    On 2024-01-10, bart <bc@freeuk.com> wrote:
    This is what I mean by user-friendly:

    c:\c>gcc hello
    C:\tdm\bin\ld.exe: cannot find hello: No such file or directory
    collect2.exe: error: ld returned 1 exit status

    c:\c>gcc hello.c

    c:\c>hello
    'hello' is not recognized as an internal or external command,
    operable program or batch file.

    c:\c>gcc hello.c -hello
    gcc: error: unrecognized command-line option '-h'

    c:\c>gcc hello.c -ohello

    It's been explained already that the Unix people had the same idea
    that there should be a "<command> hello" which infers that a hello
    program can be made from a hello.c (or updated, if it is older).

    They designed a separate program called make for doing this kind of
    inference, and made it a language where you can program your own rules.

    Having put that prerequisite-inferencing behavior into make, they
    weren't about to replicate that job into other tools, like cc.
    That goes against their one-job-one-tool principle.

    GCC comes from the GNU project, which decided to clone the Unix user
    space. Thus, the GNU project provides a make program. If you have the
    GNU system installed all together, as it is intended to be, you should
    be able to type "make hello".

    If you have only "gcc" but no "make", you've not installed all the
    GNU development stuff.

    (I'm of the opinion that a tool which infers prerequisite files from
    targets, and the necessary build commands, is better than repeating that
    logic in the implementation of every compiler-like tool.)

    Fledgling compiler projects which are not part of a Unix replacement
    project do not bundle a make tool. So it makes sense that they try
    to wedge some of those behaviors and use cases into their compiler
    driver program.

    Please don't talk to me about user-friendly, you don't seem to have a clue.

    You don't seem to get that nothing in this area qualifies as being even remotely "user-friendly".

    For starters, none of it is even user-facing.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Paul@21:1/5 to bart on Wed Jan 10 21:39:12 2024
    On 1/10/2024 5:36 PM, bart wrote:
    On 10/01/2024 21:43, Kaz Kylheku wrote:
    On 2024-01-10, bart <bc@freeuk.com> wrote:
    So, when claiming that you only need to type one thing to start the
    process, it is disingenuous to leave out that part out.

    That's just knowing where the project is. That's something external to
    the project; it's not a build secret hidden in the project itself, but
    likely something the user themselves chose.

    It's the same for any project, in any language using any build
    procedure; they all have a location, and the first step is usually
    changing to that location. Some users will cd to the project root even
    before looking at any instructions.

    (Perhaps the instructions will tell them smoething else, like
    create a build directory somewhere, change to /that/ directory and
    from the reference some build script in the unpacked tree.)

    Before typing 'make', you'd better be sure you're in the right place!

    The instructions for that project can't even tell you what that is.

    It's "wherever you unpacked/cloned the project".


    I once claimed that with the build schemes I prefer, you usually have to type only two things:

        mm prog                 # my language

        mcc @prog               # C; also works across compilers

        bcc -auto prog          # my older compiler for projects written
                                # to certain rules

    But 'make' was claimed to be superior because you only had to type one thing:

        make

    As hobby programmers, we were taught the first thing you typed was:

    make -n

    as this would be a simulation, and it would give you some idea what
    would happen next. If someone had made an "a.out" mistake, for the
    very simplest cases, you could spot it.

    However, modern packaging techniques, "make" is seldom ready to run.

    I do have one package on disk here, which is from the old days.
    And is suited to showing what make used to work like.

    /mnt/d/xv-3.10a$ make -n
    echo ""
    echo " Did you remember to 'make depend' first?"
    echo ""
    echo " building xv ..."
    echo ""
    make: *** No rule to make target '/usr/include/X11/Xos.h', needed by 'xv.o'. Stop.

    /mnt/d/xv-3.10a$ make -n depend
    makedepend -- -I/usr/local/include -I/usr/local -Dusl -DUSL -DSVR4 -Di386 -DWINTIF -DSCHEMES_95 -DNARROWPROTO -DSVR4 -DDOJPEG -DDOTIFF -DDOPDS
    -- xv.c xvevent.c xvroot.c xvmisc.c xvimage.c xvcolor.c xvsmooth.c
    xv24to8.c xvgif.c xvpm.c xvinfo.c xvctrl.c xvscrl.c xvalg.c xvgifwr.c
    xvdir.c xvbutt.c xvpbm.c xvxbm.c xvgam.c xvbmp.c xvdial.c xvgraf.c
    xvsunras.c xvjpeg.c xvps.c xvpopup.c xvdflt.c xvtiff.c xvtiffwr.c xvpds.c xvrle.c xviris.c xvgrab.c vprintf.c xvbrowse.c xvtext.c xvpcx.c xviff.c xvtarga.c xvxpm.c xvcut.c xvxwd.c xvfits.c bggen.c vdcomp.c xcmap.c

    After some more fiddling, and the make depend has run, now we try make -n again.

    /mnt/d/xv-3.10a$ make -n
    echo ""
    echo " Did you remember to 'make depend' first?"
    echo ""
    echo " building xv ..."
    echo ""
    cc -O -Xa -Dasm=__asm -DANSICPP -I/usr/local/include -I/usr/local -Dusl -DUSL -DSVR4 -Di386 -DWINTIF -DSCHEMES_95 -DNARROWPROTO -DSVR4 -DDOJPEG -DDOTIFF -DDOPDS -c -o xv.o xv.c
    ... lines deleted
    cc -O -Xa -Dasm=__asm -DANSICPP -I/usr/local/include -I/usr/local -Dusl -DUSL -DSVR4 -Di386 -DWINTIF -DSCHEMES_95 -DNARROWPROTO -DSVR4 -DDOJPEG -DDOTIFF -DDOPDS -c -o xvfits.o xvfits.c
    rm -f xv
    cc -o xv -O -Xa -Dasm=__asm -DANSICPP xv.o xvevent.o xvroot.o xvmisc.o xvimage.o xvcolor.o xvsmooth.o xv24to8.o xvgif.o xvpm.o xvinfo.o xvctrl.o xvscrl.o xvalg.o xvgifwr.o xvdir.o xvbutt.o xvpbm.o xvxbm.o xvgam.o xvbmp.o xvdial.o xvgraf.o xvsunras.o
    xvjpeg.o xvps.o xvpopup.o xvdflt.o xvtiff.o xvtiffwr.o xvpds.o xvrle.o xviris.o xvgrab.o vprintf.o xvbrowse.o xvtext.o xvpcx.o xviff.o xvtarga.o xvxpm.o xvcut.o xvxwd.o xvfits.o -lXext -lX11 -L/usr/local/lib -ljpeg -L/usr/local/lib -ltiff -lm -lsocket -
    lnsl -lgen
    cc -O -Xa -Dasm=__asm -DANSICPP -I/usr/local/include -I/usr/local -Dusl -DUSL -DSVR4 -Di386 -DWINTIF -DSCHEMES_95 -DNARROWPROTO -DSVR4 -DDOJPEG -DDOTIFF -DDOPDS -c -o bggen.o bggen.c
    rm -f bggen
    cc -o bggen -O -Xa -Dasm=__asm -DANSICPP bggen.o -lXext -lX11 -L/usr/local/lib -ljpeg -L/usr/local/lib -ltiff -lm -lsocket -lnsl -lgen
    cc -O -Xa -Dasm=__asm -DANSICPP -I/usr/local/include -I/usr/local -Dusl -DUSL -DSVR4 -Di386 -DWINTIF -DSCHEMES_95 -DNARROWPROTO -DSVR4 -DDOJPEG -DDOTIFF -DDOPDS -c -o vdcomp.o vdcomp.c
    rm -f vdcomp
    cc -o vdcomp -O -Xa -Dasm=__asm -DANSICPP vdcomp.o -lXext -lX11 -L/usr/local/lib -ljpeg -L/usr/local/lib -ltiff -lm -lsocket -lnsl -lgen
    cc -O -Xa -Dasm=__asm -DANSICPP -I/usr/local/include -I/usr/local -Dusl -DUSL -DSVR4 -Di386 -DWINTIF -DSCHEMES_95 -DNARROWPROTO -DSVR4 -DDOJPEG -DDOTIFF -DDOPDS -c -o xcmap.o xcmap.c
    rm -f xcmap
    cc -o xcmap -O -Xa -Dasm=__asm -DANSICPP xcmap.o -lXext -lX11 -L/usr/local/lib -ljpeg -L/usr/local/lib -ltiff -lm -lsocket -lnsl -lgen
    cc -O -Xa -Dasm=__asm -DANSICPP -I/usr/local/include -I/usr/local -Dusl -DUSL -DSVR4 -Di386 -DWINTIF -DSCHEMES_95 -DNARROWPROTO -DSVR4 -DDOJPEG -DDOTIFF -DDOPDS -c -o xvpictoppm.o xvpictoppm.c
    rm -f xvpictoppm
    cc -o xvpictoppm -O -Xa -Dasm=__asm -DANSICPP xvpictoppm.o -lXext -lX11 -L/usr/local/lib -ljpeg -L/usr/local/lib -ltiff -lm -lsocket -lnsl -lgen

    You may be able to tell from that, what the output executables
    will be, if the make finishes. That is an old package, which
    someone restored (like putting a coat of wax on a Bentley).

    Soon, we won't be able to run that any more, when Wayland is all we've got,
    and they quickly torch and set fire to XWayland.

    Paul

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to bart on Thu Jan 11 02:46:15 2024
    On 2024-01-10, bart <bc@freeuk.com> wrote:
    On 10/01/2024 21:43, Kaz Kylheku wrote:
    On 2024-01-10, bart <bc@freeuk.com> wrote:
    So, when claiming that you only need to type one thing to start the
    process, it is disingenuous to leave out that part out.

    That's just knowing where the project is. That's something external to
    the project; it's not a build secret hidden in the project itself, but
    likely something the user themselves chose.

    It's the same for any project, in any language using any build
    procedure; they all have a location, and the first step is usually
    changing to that location. Some users will cd to the project root even
    before looking at any instructions.

    (Perhaps the instructions will tell them smoething else, like
    create a build directory somewhere, change to /that/ directory and
    from the reference some build script in the unpacked tree.)

    Before typing 'make', you'd better be sure you're in the right place!

    The instructions for that project can't even tell you what that is.

    It's "wherever you unpacked/cloned the project".


    I once claimed that with the build schemes I prefer, you usually have to
    type only two things:

    mm prog # my language

    You mean: "cd /path/to/proj/project; mm prog", right?

    Would you say that the instructions for a microwave oven are
    "disingenuous" because they say things like type [1][5][Start]
    to cook something for 15 seconds, without telling you that you
    must first walk to the kitchen, and you must be doing this to that
    specific microwave oven's keypad, and not some unrelated remote
    control?

    mcc @prog # C; also works across compilers

    bcc -auto prog # my older compiler for projects written
    # to certain rules

    But 'make' was claimed to be superior because you only had to type one
    thing:

    make

    Never mind that 'makefile' might contains 100s or even 1000s of lines in

    There doesn't have to be a makefile. In a directory where there is
    nothing but prog.c, "make prog" will run "cc prog.c -o prog".

    It caused confusion, and made things go badly wrong, things that would
    have been picked up if 'make' needed the name of an input, as you surely wouldn't have used the same name for both.

    But in "mm prog1", "prog1" isn't the name of an input; it's an output.

    This seems common in Unix: let's call every input file 'makefile', and
    every output file 'a.out'!)

    The fixed name of the makefile is a great idea. If I could change
    anything, I would call it ".makefile".

    A filesystem directory is an object. It has properties, like
    modification time and permissions. It's also a dictionary of
    entries; those entries are its properties.

    The "Makefile" property of a directory gives the build rules for
    the project files in that directory.

    That's the right way to think about it: the rules belong to a directory
    and so are associated with it by a fixed property name.

    If directory properties are expressed as directory entries, it's nice
    if those entries use dotted names (in my opinion).

    Make programs have a -f option to pick a different name other than
    makefile, Makefile or the others that they search for by default.

    Make programs also have a -C option to use another directory:

    make -C /path/to/project

    this means "change to /path/to/projects" and make the default
    target there. (According to the rules associated with that directory,
    which are in "makefile", or "Makefile", or else some other names
    recognized by that make implementation.)

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to bart on Thu Jan 11 11:26:01 2024
    On 10/01/2024 21:49, bart wrote:
    On 10/01/2024 19:55, David Brown wrote:
    On 10/01/2024 19:57, bart wrote:
    On 10/01/2024 15:51, David Brown wrote:
    On 10/01/2024 15:31, bart wrote:

    It is almost entirely useless, except for the very, very few people
    who /need/ a C compiler that is very small.  In the days of 3.5"
    floppy disks for rescue disks, it was useful.  In the days of 16 GB
    USB flash devices costing pennies, it is irrelevant.

    16 GB can hold a great deal of useful DATA: images, audio and video
    for example. It also tends to be consumed sequentially.

    What the *beep* are you talking about?  Does your compiler consist of
    1 MB of program and 16 GB of video about how much better it is than
    everything else?

    The prime use of tcc, when it had a use, was for rescue disks and
    other situations when you needed to boot from small mediums and have a
    running system of some sort without installing on a hard disk.  That
    was originally a floppy, so tiny tools were vital.  Then boot CDs
    became popular - size was no longer an issue unless you wanted a mini
    CD.  By the time DVD's were common for the job, size of tools was
    irrelevant.

    I've just checked my main IT supplier - the /cheapest/ USB stick they
    have is 32 GB.


    I'm starting to wonder how dense you can be. But I know that's not the
    the case; just unreceptive to certain concepts or unwilling to consider
    them.

    You are claiming that there is no point in limiting the size of code,
    because after all you can buy 16GB or 32GB memory sticks.

    I am claiming that the size of a compiler is rarely of any relevance.
    The point of tcc being small was for situations where you might need a C compiler, but have very little space - primarily for *nix rescue disks
    or bootup systems. This is no longer a relevant use-case.

    There are almost no circumstances where you would need a C
    implementation, where you could use a C implementation that took 1 MB of
    space on a disk, and where you could not equally well use a C
    implementation that took 1 GB of space on the disk.

    Can you describe even a single situation where the 1 MB toolchain would
    work and the 1 GB would not? I don't care about which you /prefer/, or
    how long it would take to download or install, or how long it takes to
    run, or the flags it has, or how easy it is to use, or whether you think
    the extra space or features are unnecessary. Describe to me the
    circumstances for which a 1 MB tcc toolchain would work but a 1 GB gcc toolchain would not.

    Imagine someone wrote a program called "tcc" that accepted the same
    command line flags and options as the tcc compiler, then called gcc with appropriate flags and options so that gcc matched exactly in terms of
    the code it accepted and rejected, and how it treated the code. Under
    what circumstances would that "fake" tcc be unusable while "real" tcc
    works fine?


    So I'm not sure what your point is. Does the prevalence of very cheap
    storage make it OK to have code that is 10, 100 or 1000 times bigger
    than it need be?

    On what basis do you claim gcc (or some other tool) is orders of
    magnitude bigger than it needs to be?

    Let's take a real example. I have an installation of a complete
    gcc-based toolchain for 32-bit ARM, hosted on 64-bit Linux. I have many
    such installations, but we'll pick one - release gcc-arm-none-eabi-10-2020-q4-major. (You can download it yourself for
    Windows hosting if you like.) The download was about 150 Mb, unpacked
    it is 725 MB on my disk.

    Of those 725 Mb, 68 Mb is documentation - because for /real/ tools, documentation is important. And for /real/ tools, user convenience is
    vastly more important than disk space, so the documentation is in
    several different formats, letting users choose what they prefer.

    In the "bin" directory, there is about 60 MB of programs - there is not
    just a compiler, but there are many programs that are useful to at least
    some toolchain users. As well as the main C compiler, there is an
    assembler, linker, C++ compiler, stand-alone C preprocessor, debugger, profiler, code coverage tools, object code analysis and dump tools, and
    many others. No single user will use all of them, but each tool will be helpful to someone.

    Then there are some 150 MB of libraries and sub-programs for the
    compiler - the actual C compiler is about 25 MB. (C++ and LTO are other
    25 MB each.) That's vastly bigger than tcc - but gcc is a vastly more
    powerful compiler and does vastly more. Again, no one user uses all the features, but pretty much every feature is used by someone. Also in
    this directory is the "compiler support" libraries for the targets -
    software floating point, startup code, C++ runtime support, in multiple versions for some 20+ different 32-bit ARM architectures. That's
    another 50 MB.

    There's 20 MB of header files (by far the biggest bulk is from C++), and
    then about 430 MB of library files - again for countless versions and
    variants of ARM devices, for C and C++, for big full-featured libraries
    and small, space-saving libraries, for debug libraries, for different
    ABI versions, and so on.


    That breakdown should give you some idea of the space used - the space
    /needed/ - be real toolchains. You seem to imagine that the gcc
    developers are merely incompetent and write 1000 times too many lines of
    code.

    You fail to realise that you write your little tools for one person on
    one target, to work with source code written by one person for one kind
    of application, with no idea or concern about code optimisation, safety, quality control, testing, differing user experience and needs,
    debugging, user choices. Of course your little tools are tiny compared
    to real-world tools. (tcc /was/ written to be used by other people -
    but it was intentionally designed to be limited and inflexible so that
    it could be as small as possible.)

    Now, I'm sure a toolchain could be made with equal features to gcc and a quarter of the size. But doing so would be far more work - there is no
    point in finding some smart way to optimise the target libraries when
    disk space is free. As a user, I'd much rather the developers spent
    their time on something more useful than saving me a couple of pence
    worth of disk space.



    Apparently so, according to you. Because of course there are no
    consequences of that.

    No, there are no /significant/ consequences. So it doesn't matter.



    BTW these are some programs which are part of my gcc:

       nasm.exe   1.4MB
       ld.exe     1.5MB
       as.exe     1.6MB

    The first one, perhaps first two, will just fit onto one floppy, which I think is 1.44MiB. The last is just over.

    So three standalone programs that are still, in 2024, at floppy disk
    scale. Why is that, given that you can buy 32,000MB storages devices for 'pennies'?

    Do you imagine the developers took the size of a floppy into account
    when they made these programs? Is that what you are trying to suggest?
    These are small, in comparison to a C compiler, because they are simple,
    in comparison to a C compiler.


    Note that I did not write these programs. I don't have a monopoly on
    writing smallish, self-contained applications.

    Will you also dismiss those apps for being too small to be of
    consequence? Or will you admit that sometimes the scale of a task isn't
    great enough to warrant a huge executable?


    WTF are you on about? Sometimes you spout the most incredible rubbish.
    In what twisted fantasy world does "I don't care if a useful
    feature-filled program takes a lot of disk space" imply "Small programs
    are irrelevant" ?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Chris M. Thomasson on Thu Jan 11 11:42:27 2024
    On 11/01/2024 04:10, Chris M. Thomasson wrote:
    On 1/10/2024 2:10 AM, David Brown wrote:
    On 10/01/2024 08:40, Chris M. Thomasson wrote:
    On 1/9/2024 9:28 PM, Kaz Kylheku wrote:
    On 2024-01-10, tTh <tth@none.invalid> wrote:
    On 1/10/24 01:40, Bart wrote:
    An easy compiler is one where you just do:

         gcc prog

    and not, at the very least:

         gcc prog.c -prog.exe -std=c11 -pedantic-errors

    Meanwhile I routinely test C programs on half a dozen compilers. I >>>>>> can't
    be bothered with all this crap.

         So do as all of us do : put this crap in a Makefile or
         a shell script, and forget about it.

    Please don't get Bart started on makefiles!


    Don't get me started about freaking out for some minutes when I
    failed to use a god damn tab! My makefile would not work. God damn
    it! Ahhhh, that was around 20 years ago.

    The author of "make" described the distinction between tabs and spaces
    to be his worst mistake ever - but use of the tool took off too
    quickly for him to change it.

    Well, shit happens! lol. Jesting here. ;^)


    The difference between Bart and everyone else is that when other
    people make a tab mistake, they learn from it - and even 20 years on,
    you still remember.

    Oh my I sure do remember it, David! It managed to ingrain itself firmly
    into my mind. I was getting a bit pissed saying why the f**k wont this
    god damn makefile work!!!! GRRRRRR! I can remember it clearly. Fwiw, I
    just got dropped off at my house from a party, and decided to create a program that generated some makefiles for my AppCore project at the
    time, don't ask why. I might of been a bit boooozzyy. Yikes!


    Bart seems to forget things seconds after someone tells him how to use
    any program that he has not written himself.  (It takes him a little
    longer to forget how his own programs and languages work.)


    No shit? Bard helped me with some of my C code before. He is a nice guy.


    I'm sure Bart /is/ a nice guy. And he is a smart guy. But he has some deep-rooted and obsessive hatreds and prejudices that have blocked all
    sense of logic, rationality or factual considerations in regard to some
    topics. His fanaticism against C, gcc, development tools and *nix make
    him blind, incapable of understanding other posts, information and
    opinions, and unable to think straight or make a coherent argument. He
    sees everything on these topics as black or white, for him or against
    him, and warps everything other people say to fit that mould.

    As long as none of his triggers are activated, we get along fine. I'm
    sure if I met him in person, we could spent a pleasant time together
    discussing the pros and cons of different 80's and 90's home computers,
    why the modern Doctor Who will never live up to the originals, how
    pointless and overly commercial football has become, and lots of other
    topics. We could probably even chat about politics and religion without conflict. I'd /like/ to be able to talk to him in a comfortable and
    friendly manner about C, development tools, OS's, and the rest, but I
    fear it is not likely to happen.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Chris M. Thomasson on Thu Jan 11 11:55:59 2024
    On 11/01/2024 03:11, Chris M. Thomasson wrote:
    On 1/10/2024 7:10 PM, Chris M. Thomasson wrote:
    On 1/10/2024 2:10 AM, David Brown wrote:
    [...]
    Bart seems to forget things seconds after someone tells him how to
    use any program that he has not written himself.  (It takes him a
    little longer to forget how his own programs and languages work.)


    No shit? Bard helped me with some of my C code before. He is a nice guy.

    Oops! I meant Bart not Bard. Sorry everybody! ;^o Damn typos (tabs),
    lol! ;^o

    It makes a change from Brat, Part, and (for some reason) Bob, which I've
    been called in the past, because people can't get the head around 'Bart'.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Kaz Kylheku on Thu Jan 11 11:44:32 2024
    On 11/01/2024 02:46, Kaz Kylheku wrote:
    On 2024-01-10, bart <bc@freeuk.com> wrote:

    I once claimed that with the build schemes I prefer, you usually have to
    type only two things:

    mm prog # my language

    You mean: "cd /path/to/proj/project; mm prog", right?

    Well, it could be done as:

    mm <proglocation>

    Which in my typical projects, might mean typing:

    mm \cx\cc

    This creates \cx\cc.exe, a new version of my C compiler. You can argue
    whether path and file components of a filespec counts as one thing or two.

    Here, I should point out that what I am talking about with build systems
    is the means to create ONE new executable file (.exe or .dll, or .so
    and, um, just "." I guess on Linux!).

    While 'make' conflates several kinds of processes:

    * Building individual binaries
    * Building multiple binaries
    * Installing applications
    * Etc

    It is in building individual binaries many compilers or languages are
    weak on. Most open source projects I want to build, that are guarded by
    'make', have only a single output file.

    Getting back to the example above, creating a new production version of
    my C compiler, requires an additional step: replacing the production
    'mcc.exe' compiler with the new 'cc.exe'.

    It's done with a 2-line batch file.


    But 'make' was claimed to be superior because you only had to type one
    thing:

    make

    Never mind that 'makefile' might contains 100s or even 1000s of lines in

    There doesn't have to be a makefile. In a directory where there is
    nothing but prog.c, "make prog" will run "cc prog.c -o prog".

    OK, so just like:

    mcc prog

    At least someone recognises the utility of doing that rather than the
    gcc palaver. They don't even need the extension!

    Yet when I build that convenience into the compiler, people come down on
    me like a ton of bricks.

    Talk about hypocrisy.

    However, this 'make prog' is not quite what it seems:

    * If you need any extra options like '-lm', it won't put them in

    * If you decide to make it explicit, like 'make prog.c', it won't work.
    Because 'prog' has to be the name of the output, not the input.

    * If you have sources files prog.x and prog.y for languages X and Y, you
    can't control which one it will build

    So it's more of a parlour trick.

    It caused confusion, and made things go badly wrong, things that would
    have been picked up if 'make' needed the name of an input, as you surely
    wouldn't have used the same name for both.

    But in "mm prog1", "prog1" isn't the name of an input; it's an output.

    This seems common in Unix: let's call every input file 'makefile', and
    every output file 'a.out'!)

    The fixed name of the makefile is a great idea. If I could change
    anything, I would call it ".makefile".

    I think it's a terrible idea. It's like me deciding that when I type:

    mcc

    by itself, it will automatically compile xyzzy.c, every time. What it
    actually does is a bit saner, it shows some help text:

    MCC C Compiler 9-Jan-2024 23:33:19
    Usage:
    mcc prog[.c] # Compile prog.c to prog.exe
    mcc -help # Show options
    mcc -info # Further info

    Even this, gcc can't get right; typing just 'gcc' shows:

    gcc: fatal error: no input files
    compilation terminated.

    (It's actually worse than that: it shows 'gcc' in a light-grey colour,
    the same colour my console background, so it's invisible. After that, it changes the background to black for the error message itself, but
    doesn't change it back. Very amateurish.)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to bart on Thu Jan 11 13:24:24 2024
    On 10/01/2024 21:20, bart wrote:
    On 10/01/2024 19:24, bart wrote:
    On 10/01/2024 18:39, Keith Thompson wrote:
    bart <bc@freeuk.com> writes:
    On 10/01/2024 14:49, Scott Lurndal wrote:
    [...]
    Yet, the entire application can be built with
    $ make

    I bet you can't. There's something missing. Unless the implicit file
    that make uses happens to be in that '$' directory. Usually you have
    to navigate to the project first.

    That '$' is a shell prompt, not a directory.

    Yes, you have to "cd" (like "chdir" on Windows) to the project directory >>> before typing "make".  You also have to make sure the computer is
    powered on, login, launch a shell, and maybe one or two other things
    before you get to that point.

    You're missing the point. SL is making a big deal about the fact that
    you can type 'make' without providing any apparent input. But that
    input is provided when you use 'cd' to get the relevant folder.

    Or do you think that the 'make' command provided will build that
    specific project irrespective of the CWD?

    Let me put it another way; how does:

      $ make

    know it is to build that project, and not any other? The default input
    is presumably "./makefile", with the key bit being that ".".

    Yes, that's the usual way for non-trivial uses. (For simple cases, the build-it rules are good enough, given a target.) You can give make an
    explicit makefile ("make -f proj.make") or it will try to find the
    default one in the current directory. For gnu make (there are /many/
    other "make" implementations), it will try "GNUmakefile", "makefile",
    then "Makefile".


    So, when claiming that you only need to type one thing to start the
    process, it is disingenuous to leave out that part out.

    No. Your makefile (or cmake files, or bake files, or whatever build
    system you use) are part of your project - they are part of the source.


    After all, if you wanted to build project A, and, separately, project B,
    you can't do both of them like this:

       $ make
       $ make

    I think most people here assume the reader is familiar with the very
    basics of working a computer. But if you want it spelt out, you would generally do :

    $ cd ~/projects/project_a
    $ make
    $ cd ~/projects/project_b
    $ make

    Does that answer your question?

    If the two projects are closely connected, you might have them organised
    as sub-directories of "project". Then at the top-level, you could have
    a makefile with:


    all : project_a project_b
    .PHONY : project_a project_b

    project_a :
    $(MAKE) -C project_a

    project_b :
    $(MAKE) -C project_b


    Then you can build project_a by going to the project_a directory and
    typing "make", or going to the top-level project directory and typing
    "make project_a", or going to the top-level project directory and typing
    "make" to build project_a and project_b, together. (This is often
    particularly nice when using parallel make to make use of all the cores
    in your expensive computer.)


    We've covered this before. While I quite like sensible defaults, where C compilers tend to be sticklers for dotting all the Is, 'make' goes a
    little too far the other way.

    Before typing 'make', you'd better be sure you're in the right place!


    The same is true of getting undressed, and indeed most things in life.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to bart on Thu Jan 11 12:19:25 2024
    On 11/01/2024 11:44, bart wrote:
    On 11/01/2024 02:46, Kaz Kylheku wrote:
    On 2024-01-10, bart <bc@freeuk.com> wrote:

    I once claimed that with the build schemes I prefer, you usually have to >>> type only two things:

          mm prog                 # my language

    You mean: "cd /path/to/proj/project; mm prog", right?

    Well, it could be done as:

       mm <proglocation>

    Which in my typical projects, might mean typing:

       mm \cx\cc

    This creates \cx\cc.exe

    This behaviour is shared with the C compiler 'mcc'.

    However, it is different from other C compilers, where if you do:

    cc path/prog.c

    it produces an executable, say prog.exe, in the current directory, not
    at path/prog.exe. Same if creating an object file.

    What are the reasons for that?

    Because it sounds like it will cause all sorts of issues, for example:

    gcc -c /abc/a.c
    gcc -c /def/a.c

    These two distinct sources files.

    The two invocations will produce two different object files, both called
    a.o, and both stored in the current directory. The second will overwrite
    the first.

    Products like NASM work like mine.

    Don't tell me: you have to use 'make' to paper over all the deficiences
    in these world-class tools that appear to be written by 12-year-olds.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to David Brown on Thu Jan 11 13:45:56 2024
    On 11/01/2024 12:24, David Brown wrote:
    On 10/01/2024 21:20, bart wrote:

    Before typing 'make', you'd better be sure you're in the right place!


    The same is true of getting undressed, and indeed most things in life.



    You might remember a discussion last autumn about building Lua 5.4.

    There was a lot of confusion since the sources you got from googling
    'github lua', and also I think from the releases from that github site,
    and sources obtained via 'lua.org', were different.

    The latter had an extra directory level compared with github, and two
    makefiles rather than one:

    c:\xxx\lua-5.4.6>dir makefile*/s
    Directory of c:\xxx\lua-5.4.6
    02/05/2023 20:06 3,150 Makefile

    Directory of c:\xxx\lua-5.4.6\src
    03/02/2023 10:43 7,685 Makefile

    Github had only the latter level.

    Since both have the same name and both can be invoked with:

    make

    it meant no error was reported (like: 'no such makefile'), it just went
    wrong.

    Here, having to specify the name of a file, /and/ ensuring those two
    input files weren't identically named, could have saved a lot trouble by detecting the discrepancy sooner.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to bart on Thu Jan 11 14:55:17 2024
    On 11/01/2024 14:45, bart wrote:
    On 11/01/2024 12:24, David Brown wrote:
    On 10/01/2024 21:20, bart wrote:

    Before typing 'make', you'd better be sure you're in the right place!


    The same is true of getting undressed, and indeed most things in life.



    You might remember a discussion last autumn about building Lua 5.4.


    I vaguely remember the discussion, but not the details (sorry).

    There was a lot of confusion since the sources you got from googling
    'github lua', and also I think from the releases from that github site,
    and sources obtained via 'lua.org', were different.

    The latter had an extra directory level compared with github, and two makefiles rather than one:

      c:\xxx\lua-5.4.6>dir makefile*/s
      Directory of c:\xxx\lua-5.4.6
      02/05/2023  20:06             3,150 Makefile

      Directory of c:\xxx\lua-5.4.6\src
      03/02/2023  10:43             7,685 Makefile

    Github had only the latter level.

    Since both have the same name and both can be invoked with:

      make

    it meant no error was reported (like: 'no such makefile'), it just went wrong.

    Here, having to specify the name of a file, /and/ ensuring those two
    input files weren't identically named, could have saved a lot trouble by detecting the discrepancy sooner.


    I don't remember the details, so I am just guessing now - perhaps one
    was a developer version and the other a release version. Perhaps there
    was just a mistake.

    It is /very/ common to have makefiles in subdirectories - different
    parts of a project can be built with recursive make. (This can be done
    badly, as with many things, and can be the source of confusion,
    inefficiencies or problems if different sub-projects depend on each
    other.) But you should normally have a "parent" makefile or script in
    the outer directory, or at least clear build instructions.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From vallor@21:1/5 to 433-929-6894@kylheku.com on Thu Jan 11 14:58:47 2024
    On Wed, 10 Jan 2024 05:28:43 -0000 (UTC), Kaz Kylheku <433-929-6894@kylheku.com> wrote in <20240109212820.166@kylheku.com>:

    On 2024-01-10, tTh <tth@none.invalid> wrote:
    On 1/10/24 01:40, Bart wrote:
    An easy compiler is one where you just do:

       gcc prog

    and not, at the very least:

       gcc prog.c -prog.exe -std=c11 -pedantic-errors

    Meanwhile I routinely test C programs on half a dozen compilers. I
    can't be bothered with all this crap.

    So do as all of us do : put this crap in a Makefile or a shell
    script, and forget about it.

    Please don't get Bart started on makefiles!

    Speaking of which...let's not this time. :)

    Where would be a good place to discuss Makefiles and the build
    systems that use them; and perhaps the build systems that do
    the same job as "make"?

    comp.lang.misc?

    TIA

    --
    -v

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to bart on Thu Jan 11 16:13:54 2024
    On 2024-01-11, bart <bc@freeuk.com> wrote:
    On 11/01/2024 02:46, Kaz Kylheku wrote:
    The fixed name of the makefile is a great idea. If I could change
    anything, I would call it ".makefile".

    I think it's a terrible idea. It's like me deciding that when I type:

    mcc

    by itself, it will automatically compile xyzzy.c, every time. What it actually does is a bit saner, it shows some help text:

    No, that's like mcc, if not given options, finding a .mccproj
    file and building what is described there.

    I.e. "build the mcc project that's associated with this directory,
    via that directory's .mccproject property".

    You need a fixed name to attach a property to the directory.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to Kaz Kylheku on Thu Jan 11 17:00:35 2024
    Kaz Kylheku <433-929-6894@kylheku.com> writes:
    On 2024-01-11, bart <bc@freeuk.com> wrote:
    On 11/01/2024 02:46, Kaz Kylheku wrote:
    The fixed name of the makefile is a great idea. If I could change
    anything, I would call it ".makefile".

    I think it's a terrible idea. It's like me deciding that when I type:

    mcc

    by itself, it will automatically compile xyzzy.c, every time. What it
    actually does is a bit saner, it shows some help text:

    No, that's like mcc, if not given options, finding a .mccproj
    file and building what is described there.

    I.e. "build the mcc project that's associated with this directory,
    via that directory's .mccproject property".

    You need a fixed name to attach a property to the directory.

    That's fine, but don't make it a hidden file. A hidden file
    can be used to record tool preferences, but the file describing
    the project itself shouldn't be hidden.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Richard Harnden@21:1/5 to bart on Thu Jan 11 17:56:08 2024
    On 11/01/2024 01:14, bart wrote:
    On 10/01/2024 23:30, tTh wrote:
    On 1/10/24 19:57, bart wrote:

    The above tries to compile hello.c with gcc. The first attempt
    doesn't work. The second does, as no error is displayed. But what
    exactly has it compiled it to? As there is no file called 'hello.exe.

       o     ____    _____   _____   __  __
       o    |  _ \  |_   _| |  ___| |  \/  |
       o    | |_) |   | |   | |_    | |\/| |
       o    |  _ <    | |   |  _|   | |  | |
       o    |_| \_\   |_|   |_|     |_|  |_|
       o


    I see. It doesn't matter how complex, unfriendly, inconvenient or
    error-prone using a piece of software is, it's all fine so long as you
    tell the user:

      R T F M?

    That makes up for designing it properly?

    Instead of getting rid of unnecessary hoops you have to jump through,
    you'd just write a thicker instruction manual and sell more training
    courses. (And force people to use 'make' - another dozen hoops.)

    Plus of course you can command more pay for mastering that contrived complexity.

    What /I/ do is strive to get rid of those hoops.

    Here's some hoops, for you ...

    $ ls
    hello.c

    No Makefile, you'll notice.

    $ make hello
    cc hello.c -o hello

    $ ./hello
    Hello World!

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Kaz Kylheku on Thu Jan 11 18:49:45 2024
    On 11/01/2024 16:13, Kaz Kylheku wrote:
    On 2024-01-11, bart <bc@freeuk.com> wrote:
    On 11/01/2024 02:46, Kaz Kylheku wrote:
    The fixed name of the makefile is a great idea. If I could change
    anything, I would call it ".makefile".

    I think it's a terrible idea. It's like me deciding that when I type:

    mcc

    by itself, it will automatically compile xyzzy.c, every time. What it
    actually does is a bit saner, it shows some help text:

    No, that's like mcc, if not given options, finding a .mccproj
    file and building what is described there.

    I.e. "build the mcc project that's associated with this directory,
    via that directory's .mccproject property".

    You need a fixed name to attach a property to the directory.


    That would be more like a configuration file. It can control the
    behaviour of an interactive op, or run some sort of prelude script, or
    load some data, prior to starting a normal session.

    It wouldn't see such a file, act on it, then (perhaps silently) stop.

    If I wanted that behaviour, I would script it, using the same commands
    that I've been familiar with for decades from my OS's command prompt.

    The name of the script would usually be specific to the project, for
    example, 'makeccia' will run 'makeccia.bat'. The '-a' suffix refers to
    the floppy A: drive, so it is a very old example.

    The only time I reuse generic script names for a project are names like 'backup' and 'store'.

    I can see where you're coming from with 'make': if I load my IDE (which
    picks up a config file from this directory which describes the current
    local project), I can type standard commands like 'C' to compile, and
    'R' to run

    Within this app, you can be informal since it has an active environment
    which is clearly displayed and the files involved are shown. Well, IT IS
    AN APP and runs within an obvious context.

    'make' looks like it wants to be similarly informal, but there there is
    no strong environment, only the name of the current directory, which
    might contain /anything/. There is no real context.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Richard Harnden on Thu Jan 11 18:31:00 2024
    On 11/01/2024 17:56, Richard Harnden wrote:
    On 11/01/2024 01:14, bart wrote:
    On 10/01/2024 23:30, tTh wrote:
    On 1/10/24 19:57, bart wrote:

    The above tries to compile hello.c with gcc. The first attempt
    doesn't work. The second does, as no error is displayed. But what
    exactly has it compiled it to? As there is no file called 'hello.exe.

       o     ____    _____   _____   __  __
       o    |  _ \  |_   _| |  ___| |  \/  |
       o    | |_) |   | |   | |_    | |\/| |
       o    |  _ <    | |   |  _|   | |  | |
       o    |_| \_\   |_|   |_|     |_|  |_|
       o


    I see. It doesn't matter how complex, unfriendly, inconvenient or
    error-prone using a piece of software is, it's all fine so long as you
    tell the user:

       R T F M?

    That makes up for designing it properly?

    Instead of getting rid of unnecessary hoops you have to jump through,
    you'd just write a thicker instruction manual and sell more training
    courses. (And force people to use 'make' - another dozen hoops.)

    Plus of course you can command more pay for mastering that contrived
    complexity.

    What /I/ do is strive to get rid of those hoops.

    Here's some hoops, for you ...

    $ ls
    hello.c

    No Makefile, you'll notice.

    $ make hello
    cc     hello.c   -o hello

    $ ./hello
    Hello World!


    c:\xxx>dir/b
    hello.c

    c:\xxx>mcc hello
    Compiling hello.c to hello.exe

    c:\xxx>hello
    Hello, World! 11-Jan-2024 18:21:56

    See? It's built in to my compiler. No separate 'make' is needed. It
    doesn't have the restrictions I touched on in my earlier post on the
    subject. I can choose add the extension:

    c:\xxx>mcc hello.c
    Compiling hello.c to hello.exe

    It still works. It naturally extends to multiple source files. I don't
    need to understand make files.

    So my comments still stand. This 'make' example is just a misleading trick.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to bart on Thu Jan 11 22:02:44 2024
    On 2024-01-11, bart <bc@freeuk.com> wrote:
    On 11/01/2024 16:13, Kaz Kylheku wrote:
    On 2024-01-11, bart <bc@freeuk.com> wrote:
    On 11/01/2024 02:46, Kaz Kylheku wrote:
    The fixed name of the makefile is a great idea. If I could change
    anything, I would call it ".makefile".

    I think it's a terrible idea. It's like me deciding that when I type:

    mcc

    by itself, it will automatically compile xyzzy.c, every time. What it
    actually does is a bit saner, it shows some help text:

    No, that's like mcc, if not given options, finding a .mccproj
    file and building what is described there.

    I.e. "build the mcc project that's associated with this directory,
    via that directory's .mccproject property".

    You need a fixed name to attach a property to the directory.


    That would be more like a configuration file. It can control the
    behaviour of an interactive op, or run some sort of prelude script, or
    load some data, prior to starting a normal session.

    It wouldn't see such a file, act on it, then (perhaps silently) stop.

    If I wanted that behaviour, I would script it, using the same commands
    that I've been familiar with for decades from my OS's command prompt.

    When I do something in a Makefile, I'm also using the same commands
    I've been familiar with for decades.

    The name of the script would usually be specific to the project, for
    example, 'makeccia' will run 'makeccia.bat'. The '-a' suffix refers to
    the floppy A: drive, so it is a very old example.

    Anyone still working with .BAT files and railing against Unix
    is like a WWII soldier still in the trenches in 1975, refusing
    to surrender.

    I worked with .BAT files in the MS-DOS era. I left that stuff behind
    just as the DOS era was coming to an end, and I went off to university.
    Over the years, I had only rare, minor interactions with batch files on Windows.

    it's a very poor scripting language, that Microsoft replaced with the PowerShell. Each time I had to interact with it over the past 30
    years, I was reminded of how bad it is. It's like a freshman student's
    weekend project.

    The main weakness is that DOS and Windows programs receive the
    entire command line as a single string, which they must delimit
    themselves into arguments.

    No two programs agree on how that should be done, beyond the
    the trivial case when the command line has nothing but clumps of
    alphanumeric characters separated by spaces.

    By the way that @ thing you see in Batch files ("@echo off") is
    obviously cribbed from Makefiles! Make prints the commands that it
    executes, except ones that have a @ as the first character.

    Everything in DOS was cribbed from Unix, badly: piping | operator
    that doesn't actually run processes concurrently, < > redirection
    oeprators, device names mapped into the filesystem (but in the most
    stupid way imaginable, not like /dev). The parent directory being ..,
    but not actually due to there being a parent link, only faked out ...
    They couldn't make up their minds between / and \ so they stupidly
    supported *both* as separators, and put in a flag into the command
    interpreter to choose which one it would print.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Scott Lurndal on Thu Jan 11 21:18:42 2024
    On 2024-01-11, Scott Lurndal <scott@slp53.sl.home> wrote:
    That's fine, but don't make it a hidden file. A hidden file
    can be used to record tool preferences, but the file describing
    the project itself shouldn't be hidden.

    That ship mostly sailed with names like .svn/ and .git/.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to Kaz Kylheku on Thu Jan 11 23:03:30 2024
    Kaz Kylheku <433-929-6894@kylheku.com> writes:
    On 2024-01-11, Scott Lurndal <scott@slp53.sl.home> wrote:
    That's fine, but don't make it a hidden file. A hidden file
    can be used to record tool preferences, but the file describing
    the project itself shouldn't be hidden.

    That ship mostly sailed with names like .svn/ and .git/.

    That's metadata that will never be edited directly, but rather
    is managed by the appropriate tool. The input
    to Make will be edited by the end user unless a command line tool
    such as git or svn manage the contents completely from the command
    line.

    Even if it has a gui UI, there should be mechanisms to build and update a project without the gui.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Kaz Kylheku on Thu Jan 11 23:20:37 2024
    On 11/01/2024 22:02, Kaz Kylheku wrote:
    On 2024-01-11, bart <bc@freeuk.com> wrote:

    I worked with .BAT files in the MS-DOS era. I left that stuff behind
    just as the DOS era was coming to an end, and I went off to university.
    Over the years, I had only rare, minor interactions with batch files on Windows.

    it's a very poor scripting language,

    I used the word 'script' loosely.

    I make a distinction between command-languages, which generally have
    only a linear sequence of commands, no looping or conditional code; and
    proper scripting languages which are full languages.

    BAT files I consider command languages where you just write commands A,
    B, C ... one after the other.

    For building programs, I've never needed anything more sophisticated.
    This is the batch file for building my C compiler from source and
    replacing the current production compiler:

    mm -opt cc.m
    copy cc.exe \m\mcc.exe
    copy headers\windows.h \m

    This involves working with 62 source and support files. The assembler
    that mcc depends on is managed separately. The BAT file to update that
    is this, which involves 15 modules:

    mm -opt aa.m
    copy aa.exe \m\aa.exe

    You can see that there are very, very few demands in these 'scripts'. If
    I need real scripting, I have a perfectly good one of my own.

    Why, what hairy stuff are you doing that requires a language as complex
    as 'make' and a shell environment as capable as 'bash'? It sounds more
    like, you just use the capability because it's there. And then complain
    when Windows doesn't have that.


    that Microsoft replaced with the
    PowerShell. Each time I had to interact with it over the past 30
    years, I was reminded of how bad it is. It's like a freshman student's weekend project

    That's exactly how I view gcc's UI. Most Linux-derived utilities are as
    bad: you invoke them, and they just apparently hang. Then you realise
    they're waiting for input. Would it kill somebody to get them to display
    a prompt?

    The main weakness is that DOS and Windows programs receive the
    entire command line as a single string, which they must delimit
    themselves into arguments.

    Really? There's a function called __getmainargs() (you'll find it inside msvcrt.dll), that produces a list of separated parameters as would be
    supplied to a C program that uses 'main(n, args)'.

    Otherwise, calling GetCommandLine() and parsing a string is no big deal.
    That's exactly how languages like Python and even C (using 'fgets',
    forget 'scanf') process user input after the command line is out out of
    the way.

    No two programs agree on how that should be done, beyond the
    the trivial case when the command line has nothing but clumps of
    alphanumeric characters separated by spaces.

    So how does it work with, as I touched on, the 1000s of lines that might
    come after the command line, and or the millions of lines that might be
    read from a text file?

    Everything in DOS was cribbed from Unix, badly: piping | operator
    that doesn't actually run processes concurrently, < > redirection
    oeprators, device names mapped into the filesystem (but in the most
    stupid way imaginable, not like /dev). The parent directory being ..,
    but not actually due to there being a parent link, only faked out ...
    They couldn't make up their minds between / and \ so they stupidly
    supported *both* as separators, and put in a flag into the command interpreter to choose which one it would print.

    OK. I can't say I'm that interested in OSes or shell languages. The
    latter are just a way to copy files or launch programs. The former
    merely supplied a file system for the first decade or two I was coding.

    Of the first three OSes I was exposed to, two were from DEC, one from
    ICL. Then there was a period when I was working with bare boards, and
    then on small machines which ran a CP/M clone that my company wrote.

    MSDOS followed later; I didn't remember doing anything much with that
    either.

    The \ / think I agree is messy, using only / is simpler, and easier to
    locate if using lots of different keyboards. And using LF line
    delimiters rather than CRLF, made more sense as soon as people moved to
    VDUs. However, I support either.

    (Here, gcc gets badly confused between \ and /, but listing the things
    gcc gets wrong in its CLI would be a long article by itself.)

    Apart from that there's nothing I desperately need from Linux shell
    programs; even BAT is overkill most of the time.

    I'm be interested in what you think 'make' would do for me given that my
    needs are utterly simple. Partly that is because of my 'mm' compiler,
    but it would be little different if all my stuff was written in C.

    I just make the effort to keep things simple.

    Here's another little demo, which is getting my C compiler to work on
    Linux (although as a cross-compiler). I was going to write a script but
    the first part is only one line, which is to convert from my language to C:

    c:\cx>mc -c -linux cc
    M6 Compiling cc.m---------- to cc.c

    The next part has to be done under WSL:

    root@XXX:/mnt/c/cx# gcc cc.c -omcc -lm -ldl -fno-builtin

    I've got my compiler binary 'mcc', now I might as well try it on itself,
    but I can only produce an ASM file here:

    root@XXX:/mnt/c/cx# ./mcc -s cc # -s == -S
    Compiling cc.c to cc.asm

    To process further, I need to get back to Windows:

    c:\cx>aa cc
    Assembling cc.asm to cc.exe
    Error: Can't find external function: tcgetattr cc.asm

    There's a small problem since I used the cc.c produced for Linux
    (-linux), which has some OS-specific routines.

    But it doesn't matter (it still translated 30K lines of C to 100K lines
    of assembly for Win64 ABI, under Linux).

    The point of interest is, how would the famous 'make' have helped me
    here? I switched from Windows to Linux and back to Windows, and in each
    case there was only 1 or 2 lines to type.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Anthony Cuozzo@21:1/5 to All on Thu Jan 11 19:02:26 2024
    Why, what hairy stuff are you doing that requires a language as complex
    as 'make' and a shell environment as capable as 'bash'?

    Not OP, so please forgive me for jumping in, but I've found over the
    years that source distributions-- especially multi-platform source distributions --often require quite a bit more attention on the build side.

    Supporting a non-trivial program on e.g. three different CPU
    architectures with just as many (or more!) compilers across many
    different versions of several operating systems can quickly become
    impossible with one dedicated script per variation.

    Binary distributions are a different story, of course.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Scott Lurndal on Thu Jan 11 23:58:42 2024
    On 2024-01-11, Scott Lurndal <scott@slp53.sl.home> wrote:
    Kaz Kylheku <433-929-6894@kylheku.com> writes:
    On 2024-01-11, Scott Lurndal <scott@slp53.sl.home> wrote:
    That's fine, but don't make it a hidden file. A hidden file
    can be used to record tool preferences, but the file describing
    the project itself shouldn't be hidden.

    That ship mostly sailed with names like .svn/ and .git/.

    That's metadata that will never be edited directly, but rather
    is managed by the appropriate tool.

    Oh, it's touched all the time.

    $ vi .git/config

    Messing with hooks in .git/hooks.

    Commit message edited in .git/COMMIT_EDITMSG.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Kaz Kylheku on Fri Jan 12 09:08:40 2024
    On 11/01/2024 22:18, Kaz Kylheku wrote:
    On 2024-01-11, Scott Lurndal <scott@slp53.sl.home> wrote:
    That's fine, but don't make it a hidden file. A hidden file
    can be used to record tool preferences, but the file describing
    the project itself shouldn't be hidden.

    That ship mostly sailed with names like .svn/ and .git/.


    These are not part of the project - they are metadata for coordinating
    how the project is stored and how changes are tracked. I can take a
    project from a subversion and transfer it to a git server or zip it up
    and post it - the makefile (or other build system files and scripts)
    must be part of that, while .svn and .git directories most certainly are
    not part of that.

    You could argue that .gitignore is a useful file to track, and I would
    not mind if subversion had a similar file (or, ideally, also read and
    used .gitignore files). And it would arguably have been better if this
    file had been called "git.ignore" instead.

    But files that control actions and need to be tracked should not be
    hidden files. Hidden files are for user preferences, or local temporary
    files.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to bart on Fri Jan 12 09:52:10 2024
    On 11/01/2024 19:49, bart wrote:

    The name of the script would usually be specific to the project, for
    example, 'makeccia' will run 'makeccia.bat'. The '-a' suffix refers to
    the floppy A: drive, so it is a very old example.

    To be clear here - I don't think anyone would greatly object if "make"
    required an explicit makefile name, rather than using a default
    filename. It is more convenient to type "make" than "make -f
    project.mak", but some people prefer to do exactly that, rather than
    using a default makefile. It is particularly convenient if you have
    several small projects in the same directory, and don't want to combine
    a single makefile.

    And of course if "make" had originally required an explicit filename,
    there are plenty of *nix idioms and tricks to simplify things - adding a shebang to a makefile to make it executable, shell aliases, bash
    scripts, etc. People always manage to make things work in a way that is convenient for them.

    But since "make" uses "makefile" by default, that's what most people use.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to bart on Fri Jan 12 14:40:43 2024
    On 12/01/2024 00:20, bart wrote:
    On 11/01/2024 22:02, Kaz Kylheku wrote:
    On 2024-01-11, bart <bc@freeuk.com> wrote:

    I worked with .BAT files in the MS-DOS era.  I left that stuff behind
    just as the DOS era was coming to an end, and I went off to university.
    Over the years, I had only rare, minor interactions with batch files on
    Windows.

    it's a very poor scripting language,

    I used the word 'script' loosely.

    I make a distinction between command-languages, which generally have
    only a linear sequence of commands, no looping or conditional code; and proper scripting languages which are full languages.


    People can mean different things by "script" and "scripting language",
    so it is good that you are making this clear.

    BAT files I consider command languages where you just write commands A,
    B, C ... one after the other.

    For building programs, I've never needed anything more sophisticated.
    This is the batch file for building my C compiler from source and
    replacing the current production compiler:

        mm -opt cc.m
        copy cc.exe \m\mcc.exe
        copy headers\windows.h \m

    This involves working with 62 source and support files. The assembler
    that mcc depends on is managed separately. The BAT file to update that
    is this, which involves 15 modules:

        mm -opt aa.m
        copy aa.exe \m\aa.exe

    You can see that there are very, very few demands in these 'scripts'. If
     I need real scripting, I have a perfectly good one of my own.

    Why, what hairy stuff are you doing that requires a language as complex
    as 'make' and a shell environment as capable as 'bash'? It sounds more
    like, you just use the capability because it's there. And then complain
    when Windows doesn't have that.


    People use good tools when good tools are available - they don't go out
    of their way to use something inferior. Why is that a surprise? If you
    want to cut an orange, do you go out to the garage and find a rusty old
    knife that is nonetheless useable for cutting a bit of fruit - or do you
    use the shiny new titanium kitchen knife that is right in front of you?

    I used "make" on DOS and Windows for about 15 years before I started
    using Linux as a development system (rather than just for servers and
    for fun). Every Windows system I have ever owned personally or used at
    work has had "make" - because "make" has been the standard for quality development tools since early DOS days. The version I used first on
    Windows came with Turbo Pascal - Microsoft's tools came with their own
    slight variation of make called "nmake".

    If your build uses only a couple of commands, make doesn't add much
    compared to a script - but it doesn't cost much either. And you have
    the convenience that a lot of editors have shortcut keys for doing a
    "build", so something like ctrl-B will run "make" in the current
    directory without needing to set up any specific tool shortcuts. That's convenient.

    And unlike a language/compiler specific build system like you have
    within your "mm" or "mcc" tools (if I am not mistaken), "make" will work
    for anything you can run by commands. My makefiles might not just build executables from C files, but also build documentation (doxygen, LaTeX,
    pandoc, graphviz, etc.), run tests, run "clean", build release zip
    files, download to a target board via a debugger, and all sorts of other
    bits and pieces according to the needs of the project. One makefile
    beats a dozen scripts.


    that Microsoft replaced with the
    PowerShell. Each time I had to interact with it over the past 30
    years, I was reminded of how bad it is. It's like a freshman student's
    weekend project

    That's exactly how I view gcc's UI. Most Linux-derived utilities are as
    bad: you invoke them, and they just apparently hang. Then you realise
    they're waiting for input. Would it kill somebody to get them to display
    a prompt?

    Are you sure you are not exaggerating just a /tiny/ bit?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to David Brown on Fri Jan 12 16:12:13 2024
    On 12/01/2024 13:40, David Brown wrote:
    On 12/01/2024 00:20, bart wrote:

    Why, what hairy stuff are you doing that requires a language as
    complex as 'make' and a shell environment as capable as 'bash'? It
    sounds more like, you just use the capability because it's there. And
    then complain when Windows doesn't have that.


    People use good tools when good tools are available - they don't go out
    of their way to use something inferior.  Why is that a surprise?  If you want to cut an orange, do you go out to the garage and find a rusty old
    knife that is nonetheless useable for cutting a bit of fruit - or do you
    use the shiny new titanium kitchen knife that is right in front of you?

    I used "make" on DOS and Windows for about 15 years before I started
    using Linux as a development system (rather than just for servers and
    for fun).  Every Windows system I have ever owned personally or used at
    work has had "make" - because "make" has been the standard for quality development tools since early DOS days.  The version I used first on
    Windows came with Turbo Pascal - Microsoft's tools came with their own
    slight variation of make called "nmake".

    If your build uses only a couple of commands, make doesn't add much
    compared to a script - but it doesn't cost much either.  And you have
    the convenience that a lot of editors have shortcut keys for doing a
    "build", so something like ctrl-B will run "make" in the current
    directory without needing to set up any specific tool shortcuts.  That's convenient.

    And unlike a language/compiler specific build system like you have
    within your "mm" or "mcc" tools (if I am not mistaken), "make" will work
    for anything you can run by commands.  My makefiles might not just build executables from C files,

    If you isolate that part of it, then it's what I either build-in to the language + compiler (for M), or list in a simple text file (for C).

    In either case I will have a project file using a similar list for my
    basic IDE that I use for everyday development. But this part is not
    needed when somebody else needs to build my project.

    For a C project consisting of three files (one of Chris's), my IDE looks
    like this:

    https://github.com/sal55/langs/blob/master/ff.png

    It probably looked about the same in 1984. The project file for that is
    this:

    run cipher c.c output -e

    module cipher.c
    module sha2.c
    module hmac.c
    file sha2.h
    file hmac.h

    The 'run' lines show what happens when I type 'R' in the IDE.

    This project is small enough that it can be built by a third party with
    a one-line instruction. Otherwise it would need an @ file of a few lines listing the .c files.

    Because this project is tidily structured with paired .c/.h files, my
    older BCC compiler could figure out how to build it from just the lead
    module:

    c:\c>bcc -auto cipher
    1 Compiling cipher.c to cipher.asm (Pass 1)
    * 2 Compiling hmac.c to hmac.asm (Pass 2)
    * 3 Compiling sha2.c to sha2.asm (Pass 2)
    Assembling to cipher.exe



    but also build documentation (doxygen, LaTeX,
    pandoc, graphviz, etc.), run tests, run "clean", build release zip
    files, download to a target board via a debugger, and all sorts of other
    bits and pieces according to the needs of the project.  One makefile
    beats a dozen scripts.

    It looks like 'make' is competing with 'bash' then!

    At least with bash, what you type equates to what you might type
    interactively. I doubt there is a an interactive, REPL-style make (or
    maybe there is; I wouldn't be suprised).


    That's exactly how I view gcc's UI. Most Linux-derived utilities are
    as bad: you invoke them, and they just apparently hang. Then you
    realise they're waiting for input. Would it kill somebody to get them
    to display a prompt?

    Are you sure you are not exaggerating just a /tiny/ bit?

    I mainly remember the times when they do hang.

    But take the related trio gcc, as and ld.

    gcc and ld behave as expected with no input (with gcc it's a 'fatal
    error'; with ld it's a more restrained 'no input files').

    But with 'as', it just sits there. I wonder what it's waiting for; for
    me to type in ASM code live from the terminal? (If 'as' is designed for piped-in input, tdm/gcc doesn't appear to use that feature as I remember
    it generating discrete, temporary .s files.)

    My equivalent says this:

    c:\c>aa
    AA2.0 Assembler/Linker 10-Jan-2024
    Usage:
    aa filename[.asm] # Assemble filename.asm to
    filename.exe
    aa -help # Show other options

    If you're curious about what 'as' expects and speculatively try 'as
    --help', it displays 167 dense lines.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to David Brown on Fri Jan 12 16:01:09 2024
    David Brown <david.brown@hesbynett.no> writes:
    On 12/01/2024 00:20, bart wrote:

    That's exactly how I view gcc's UI. Most Linux-derived utilities are as
    bad: you invoke them, and they just apparently hang. Then you realise
    they're waiting for input. Would it kill somebody to get them to display
    a prompt?

    Are you sure you are not exaggerating just a /tiny/ bit?

    Clearly, Bart is trolling.

    And clearly, he doesn't understand the basic unix philosophy
    of combining commands in pipeline, where "displaying a prompt"
    would be silly.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to bart on Fri Jan 12 17:34:40 2024
    On 12/01/2024 17:12, bart wrote:
    On 12/01/2024 13:40, David Brown wrote:
    On 12/01/2024 00:20, bart wrote:

    Why, what hairy stuff are you doing that requires a language as
    complex as 'make' and a shell environment as capable as 'bash'? It
    sounds more like, you just use the capability because it's there. And
    then complain when Windows doesn't have that.


    People use good tools when good tools are available - they don't go
    out of their way to use something inferior.  Why is that a surprise?
    If you want to cut an orange, do you go out to the garage and find a
    rusty old knife that is nonetheless useable for cutting a bit of fruit
    - or do you use the shiny new titanium kitchen knife that is right in
    front of you?

    I used "make" on DOS and Windows for about 15 years before I started
    using Linux as a development system (rather than just for servers and
    for fun).  Every Windows system I have ever owned personally or used
    at work has had "make" - because "make" has been the standard for
    quality development tools since early DOS days.  The version I used
    first on Windows came with Turbo Pascal - Microsoft's tools came with
    their own slight variation of make called "nmake".

    If your build uses only a couple of commands, make doesn't add much
    compared to a script - but it doesn't cost much either.  And you have
    the convenience that a lot of editors have shortcut keys for doing a
    "build", so something like ctrl-B will run "make" in the current
    directory without needing to set up any specific tool shortcuts.
    That's convenient.

    And unlike a language/compiler specific build system like you have
    within your "mm" or "mcc" tools (if I am not mistaken), "make" will
    work for anything you can run by commands.  My makefiles might not
    just build executables from C files,

    If you isolate that part of it, then it's what I either build-in to the language + compiler (for M), or list in a simple text file (for C).

    In either case I will have a project file using a similar list for my
    basic IDE that I use for everyday development. But this part is not
    needed when somebody else needs to build my project.

    For a C project consisting of three files (one of Chris's), my IDE looks
    like this:

      https://github.com/sal55/langs/blob/master/ff.png

    It probably looked about the same in 1984. The project file for that is
    this:

      run cipher c.c output -e

      module cipher.c
      module sha2.c
      module hmac.c
      file sha2.h
      file hmac.h

    The 'run' lines show what happens when I type 'R' in the IDE.


    I don't really understand what you are trying to say here. Are you
    suggesting that your 40 year old DOS IDE is equivalent to modern IDE's ?
    Are you trying to say you can use your own tools for your own
    language, and rely on a simple script for C compilation, and can't
    handle anything else in a build process?


    but also build documentation (doxygen, LaTeX, pandoc, graphviz, etc.),
    run tests, run "clean", build release zip files, download to a target
    board via a debugger, and all sorts of other bits and pieces according
    to the needs of the project.  One makefile beats a dozen scripts.

    It looks like 'make' is competing with 'bash' then!


    I have no idea why you think that - except perhaps because you still
    have no concept of what "make" is and what it does, and think it is just
    a script with a complicated syntax.

    At least with bash, what you type equates to what you might type interactively. I doubt there is a an interactive, REPL-style make (or
    maybe there is; I wouldn't be suprised).


    That's exactly how I view gcc's UI. Most Linux-derived utilities are
    as bad: you invoke them, and they just apparently hang. Then you
    realise they're waiting for input. Would it kill somebody to get them
    to display a prompt?

    Are you sure you are not exaggerating just a /tiny/ bit?

    I mainly remember the times when they do hang.

    But take the related trio gcc, as and ld.

    gcc and ld behave as expected with no input (with gcc it's a 'fatal
    error'; with ld it's a more restrained 'no input files').

    But with 'as', it just sits there. I wonder what it's waiting for; for
    me to type in ASM code live from the terminal? (If 'as' is designed for piped-in input, tdm/gcc doesn't appear to use that feature as I remember
    it generating discrete, temporary .s files.)

    Yes, "as" can work with piped input. Yes, on real OS's this is a common
    way to use it. Windows has, surprising no one, only a half-arsed and inefficient pipe implementation, so it is more common to use temporary
    files for this sort of thing. (I am not familiar with TDM's builds and packaging of gcc and friends, but with mingw-64 builds it is certainly
    possible to use pipes. You simply pass the flag "-pipe" to gcc.)


    My equivalent says this:

      c:\c>aa
      AA2.0 Assembler/Linker 10-Jan-2024
      Usage:
              aa filename[.asm]           # Assemble filename.asm to
    filename.exe
              aa -help                    # Show other options

    If you're curious about what 'as' expects and speculatively try 'as
    --help', it displays 167 dense lines.


    Are you trying to convince people that your assembler is better than gas because yours has fewer features? Bizarre.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to bart on Fri Jan 12 16:50:10 2024
    bart <bc@freeuk.com> writes:
    On 12/01/2024 13:40, David Brown wrote:
    On 12/01/2024 00:20, bart wrote:

    But with 'as', it just sits there. I wonder what it's waiting for; for
    me to type in ASM code live from the terminal?

    It does that so you can pipe the assembler source code in to the
    assembler.

    $ cat file.s | as

    $ cat file.c | cpp | c0 | c1 | c2 | as > file.o

    or, if you want, you can type in the assembler source directly.

    Or you can save it in a file and supply the file argument to the command.

    None of which your stuff supports, which makes it useless to me.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Scott Lurndal on Fri Jan 12 16:28:15 2024
    On 12/01/2024 16:01, Scott Lurndal wrote:
    David Brown <david.brown@hesbynett.no> writes:
    On 12/01/2024 00:20, bart wrote:

    That's exactly how I view gcc's UI. Most Linux-derived utilities are as
    bad: you invoke them, and they just apparently hang. Then you realise
    they're waiting for input. Would it kill somebody to get them to display >>> a prompt?

    Are you sure you are not exaggerating just a /tiny/ bit?

    Clearly, Bart is trolling.

    And clearly, he doesn't understand the basic unix philosophy
    of combining commands in pipeline, where "displaying a prompt"
    would be silly.

    Yeah. Because it would be impossible too have two versions of a program
    (say 'as' that I used in my recent post), one for piping, one for
    interactive.

    One could even just be an alias of the other to avoid two copies. The
    program could detect what name it was invoked under, and operate in a
    different mode or with a different option.

    As demonstrated here under Windows: 'mm' is a compiler with an option to immediately run the program just compiled, without generating an EXE:

    c:\mx>mm -run hello
    Compiling hello.m to memory
    Hello World! 16:20:21

    However, if I make a copy called 'ms.exe' (I don't know how to do
    aliases on Windows):

    c:\mx>copy mm.exe ms.exe
    1 file(s) copied.

    Then it will automatically invoke that option, and also make it less
    verbose to give the illusion it's directly running from source, which it is:

    c:\mx>ms hello
    Hello World! 16:21:52

    Here is a more impressive example:

    c:\cx>\mx\ms cc hello
    Compiling hello.c to hello.exe

    Here, it's first building my C compiler from source, then using that to
    compile hello.c. (It adds 0.1 seconds to compile-time; it's still faster
    than compiling it with gcc!)

    Anyway, 'as' could be a lot better; just saying...

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to David Brown on Fri Jan 12 17:09:27 2024
    On 12/01/2024 16:34, David Brown wrote:
    On 12/01/2024 17:12, bart wrote:

    I don't really understand what you are trying to say here.  Are you suggesting that your 40 year old DOS IDE is equivalent to modern IDE's ?

    No. Only that the way I normally work hasn't changed a great deal.

    Are you trying to say you can use your own tools for your own
    language, and rely on a simple script for C compilation, and can't
    handle anything else in a build process?

    I'm separating the fundamental build-process for a program from what is
    needed for interactive development and testing.

    Makefiles you see supplied with open source projects don't generally
    make that distinction.

    But I can see you're struggling with the concept of simplicity.


    download to a
    target board via a debugger,

    (Hey, I used to do that! Not a makefile in sight either; how is that
    possible?

    I used to do that with no special tools, no external software and no
    external languages. I had to write assemblers for any new devices I has
    to use.)


    and all sorts of other bits and pieces
    according to the needs of the project.  One makefile beats a dozen
    scripts.

    It looks like 'make' is competing with 'bash' then!


    I have no idea why you think that - except perhaps because you still
    have no concept of what "make" is and what it does, and think it is just
    a script with a complicated syntax.

    So, what the hell is it then? What makes it so special compared with any
    other scripting language?

    All I can see is that it can create dependency graphs between files -
    which have to be determined from info that you provide in the file, it's
    not that clever - and can use that to avoid recompilation etc of a file
    unless its dependencies have changed.

    That is something I've never needed done automatically in my own work (I
    do it manually as I will know my projects intimately when I'm working
    with them).

    For production builds, it doesn't matter if everything is compiled.

    For end-user builds who just want a working binary, they have to build everything from source anyway.

    If you're curious about what 'as' expects and speculatively try 'as
    --help', it displays 167 dense lines.


    Are you trying to convince people that your assembler is better than gas because yours has fewer features?  Bizarre.

    It's simpler and gives a simple summary of how it works.

    Clearly your idea of 'better' is to be vastly more complicated.

    I guess an assembler which will only work for the processor you happen
    to be working with is no good at all. It has to also support dozens that
    are not relevant to the task in hand.

    BTW my assembler can directly produce EXE and DLL files; in that regard
    it IS better than 'as' and probably most others which like to off-load
    that critical bit.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to David Brown on Fri Jan 12 17:40:28 2024
    David Brown <david.brown@hesbynett.no> writes:
    On 12/01/2024 17:12, bart wrote:
    On 12/01/2024 13:40, David Brown wrote:
    On 12/01/2024 00:20, bart wrote:

    It looks like 'make' is competing with 'bash' then!


    I have no idea why you think that - except perhaps because you still
    have no concept of what "make" is and what it does, and think it is just
    a script with a complicated syntax.

    I can't tell if he's just trolling, or if he really believes what
    he writes.

    There's no way in bash to generate a dependency graph, which
    is the primary function of the make utility.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to bart on Fri Jan 12 17:16:14 2024
    bart <bc@freeuk.com> writes:
    On 12/01/2024 16:01, Scott Lurndal wrote:
    David Brown <david.brown@hesbynett.no> writes:
    On 12/01/2024 00:20, bart wrote:

    That's exactly how I view gcc's UI. Most Linux-derived utilities are as >>>> bad: you invoke them, and they just apparently hang. Then you realise
    they're waiting for input. Would it kill somebody to get them to display >>>> a prompt?

    Are you sure you are not exaggerating just a /tiny/ bit?

    Clearly, Bart is trolling.

    And clearly, he doesn't understand the basic unix philosophy
    of combining commands in pipeline, where "displaying a prompt"
    would be silly.

    Yeah. Because it would be impossible too have two versions of a program
    (say 'as' that I used in my recent post), one for piping, one for >interactive.

    Not impossible. Just unnecessary.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to Scott Lurndal on Fri Jan 12 17:43:50 2024
    scott@slp53.sl.home (Scott Lurndal) writes:
    bart <bc@freeuk.com> writes:
    On 12/01/2024 13:40, David Brown wrote:
    On 12/01/2024 00:20, bart wrote:

    But with 'as', it just sits there. I wonder what it's waiting for; for
    me to type in ASM code live from the terminal?

    It does that so you can pipe the assembler source code in to the
    assembler.

    $ cat file.s | as

    $ cat file.c | cpp | c0 | c1 | c2 | as > file.o

    or, if you want, you can type in the assembler source directly.

    Or you can save it in a file and supply the file argument to the command.

    None of which your stuff supports, which makes it useless to me.


    But, in any case, why wonder when you can just RTFM?

    $ man as
    ...
    You give as a command line that has zero or more input file names. The
    input files are read (from left file name to right). A command line
    argument (in any position) that has no special meaning is taken to be
    an input file name.

    If you give as no file names it attempts to read one input file from
    the as standard input, which is normally your terminal. You may have
    to type the EOF character (default control-D) to tell as there is
    no more program to assemble.

    Use -- if you need to explicitly name the standard input file in your
    command line.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Scott Lurndal on Fri Jan 12 17:59:30 2024
    On 12/01/2024 16:50, Scott Lurndal wrote:
    bart <bc@freeuk.com> writes:
    On 12/01/2024 13:40, David Brown wrote:
    On 12/01/2024 00:20, bart wrote:

    But with 'as', it just sits there. I wonder what it's waiting for; for
    me to type in ASM code live from the terminal?

    It does that so you can pipe the assembler source code in to the
    assembler.

    $ cat file.s | as

    $ cat file.c | cpp | c0 | c1 | c2 | as > file.o

    Using ">" on binary content? That seems off.

    But I've been playing with 'as', and it has issues. I'm not surprised
    you have to use it via pipes, because used conventionally, it stinks.

    If you do this:

    as hello.s

    What might someone expect the output to be? Probably not 'a.out', more
    likely hello.o. Why /isn't/ it just hello.o?

    Here a.out will be an object file; on Linux, it will sometimes it will
    be an executable. No possibility of confusion at all.

    What do you expect it to do here:

    as one.s two.s

    I expected two object files; I got one composite one. (Just as well if
    they would both be called a.out!)


    or, if you want, you can type in the assembler source directly.

    Or you can save it in a file and supply the file argument to the command.

    None of which your stuff supports, which makes it useless to me.

    I understand. Because writing:

    $ command inputfile

    is too complicated. Certainly it is if you have to do:

    $ command inputfile -o outputfile

    where outputfile is just putfile with the extension changed.

    I been running assemblers since the late 70s; I have never seen one
    behave as weirdly as 'as'.

    I thought /my/ 'aa' product was unconventional (in normally turning ASM
    sources directly to executables), but 'as' has it beat.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Janis Papanagnou@21:1/5 to bart on Fri Jan 12 19:02:38 2024
    On 12.01.2024 18:09, bart wrote:
    On 12/01/2024 16:34, David Brown wrote:

    It looks like 'make' is competing with 'bash' then!

    Why don't you just read about those two tools and learn, instead
    of repeatedly spouting such stupid statements of ignorance.


    I have no idea why you think that - except perhaps because you still
    have no concept of what "make" is and what it does, and think it is
    just a script with a complicated syntax.

    So, what the hell is it then? What makes it so special compared with any other scripting language?

    Meanwhile you seem to have read a bit about it, as you show below.

    It's a _simple_ tool - not complex, as you've previously posted -
    where you can define dependencies of entities, and define commands
    that create the targets if entities that are required by the target
    had changed. Its basic syntax and also its logic is simple, and it
    does the specific task very well, and it is not bound to a specific
    language, or to languages generally, but also to any generation
    processes.


    All I can see is that it can create dependency graphs between files -
    which have to be determined from info that you provide in the file, it's
    not that clever - and can use that to avoid recompilation etc of a file unless its dependencies have changed.

    Or rather, unless the entities (that are dependent on) have changed.

    And this is a crucial feature; for professional non-trivial projects.


    That is something I've never needed done automatically in my own work (I
    do it manually as I will know my projects intimately when I'm working
    with them).

    Yes, we know. You've repeatedly shown that you are actually doing
    small one-man-shows in projects that I can only call toy-projects.

    Professional projects have a different situation in many respects.
    (I don't go into detail here, since you're anyway only interested
    in your local comfort zone.)


    For production builds, it doesn't matter if everything is compiled.

    For a production build we extract a committed release into an own
    file system branch and build all, first for the tests, then to pack
    the source (optionally), and then the complete runtime environment.

    But we're doing professional software products. And the production
    build comes _at the end_.

    Before that we need efficient mechanisms to create consistent systems
    and configurations, and to avoid unnecessary compiles. And the 'make'
    process does exactly that.

    [...]

    Clearly your idea of 'better' is to be vastly more complicated.

    The Makefile mechanism is extremely important and yet very simple.

    Clearly your imputations are based on ignorance.

    Janis

    [...]

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Janis Papanagnou@21:1/5 to Scott Lurndal on Fri Jan 12 19:06:30 2024
    On 12.01.2024 18:40, Scott Lurndal wrote:
    David Brown <david.brown@hesbynett.no> writes:
    On 12/01/2024 17:12, bart wrote:
    On 12/01/2024 13:40, David Brown wrote:
    On 12/01/2024 00:20, bart wrote:

    It looks like 'make' is competing with 'bash' then!

    I have no idea why you think that - except perhaps because you still
    have no concept of what "make" is and what it does, and think it is just
    a script with a complicated syntax.

    I can't tell if he's just trolling, or if he really believes what
    he writes.

    My suspicion had been that he's maybe no person but an AI bot.
    It's just too pathological what he writes and how he behaves to be
    sure that he's a human poster. - On the other hand, this is Usenet,
    and everything is possible.

    Janis

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Janis Papanagnou@21:1/5 to bart on Fri Jan 12 19:10:56 2024
    On 12.01.2024 18:59, bart wrote:
    On 12/01/2024 16:50, Scott Lurndal wrote:

    $ cat file.c | cpp | c0 | c1 | c2 | as > file.o

    Using ">" on binary content?

    Of course.

    That seems off.

    Why?

    Janis

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to bart on Fri Jan 12 19:15:06 2024
    bart <bc@freeuk.com> writes:
    On 12/01/2024 16:50, Scott Lurndal wrote:
    bart <bc@freeuk.com> writes:
    On 12/01/2024 13:40, David Brown wrote:
    On 12/01/2024 00:20, bart wrote:

    But with 'as', it just sits there. I wonder what it's waiting for; for
    me to type in ASM code live from the terminal?

    It does that so you can pipe the assembler source code in to the
    assembler.

    $ cat file.s | as

    $ cat file.c | cpp | c0 | c1 | c2 | as > file.o

    Using ">" on binary content? That seems off.

    Unix files are untyped sequences of bytes.

    (e.g. the 'b' flag to the fopen stdio library routine is ignored on unix).

    That windows fucks around with line endings and uses CRLF instead of the
    more efficient LF is screwed up.



    If you do this:

    as hello.s

    What might someone expect the output to be?

    What the FM documents. RTFM.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Janis Papanagnou on Fri Jan 12 18:53:18 2024
    On 12/01/2024 18:10, Janis Papanagnou wrote:
    On 12.01.2024 18:59, bart wrote:
    On 12/01/2024 16:50, Scott Lurndal wrote:

    $ cat file.c | cpp | c0 | c1 | c2 | as > file.o

    Using ">" on binary content?

    Of course.

    That seems off.

    Why?

    Because when you see ">" on a command line, it means redirecting output
    that would normally be shown as text on a console or terminal.

    But you rarely see pure binary being displayed like that on a text display.

    Also, in the absence of ">", 'as' wouldn't write its output to the
    screen (only to some badly named file), so it is off from that
    perspective too.

    However I'm obviously just a bot, so what do I know.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to bart on Fri Jan 12 19:18:21 2024
    bart <bc@freeuk.com> writes:
    On 12/01/2024 18:10, Janis Papanagnou wrote:
    On 12.01.2024 18:59, bart wrote:
    On 12/01/2024 16:50, Scott Lurndal wrote:

    $ cat file.c | cpp | c0 | c1 | c2 | as > file.o

    Using ">" on binary content?

    Of course.

    That seems off.

    Why?

    Because when you see ">" on a command line, it means redirecting output
    that would normally be shown as text on a console or terminal.

    No, it doesn't mean that at all. It never has meant that.

    name' tells the shell to open 'name' on stdout before executing 'as'.

    the 'as' command takes text as input and produces binary output (ELF or *Cough*COFF).

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Scott Lurndal on Fri Jan 12 20:14:42 2024
    On 12/01/2024 19:15, Scott Lurndal wrote:
    bart <bc@freeuk.com> writes:
    On 12/01/2024 16:50, Scott Lurndal wrote:
    bart <bc@freeuk.com> writes:
    On 12/01/2024 13:40, David Brown wrote:
    On 12/01/2024 00:20, bart wrote:

    But with 'as', it just sits there. I wonder what it's waiting for; for >>>> me to type in ASM code live from the terminal?

    It does that so you can pipe the assembler source code in to the
    assembler.

    $ cat file.s | as

    $ cat file.c | cpp | c0 | c1 | c2 | as > file.o

    Using ">" on binary content? That seems off.

    Unix files are untyped sequences of bytes.

    (e.g. the 'b' flag to the fopen stdio library routine is ignored on unix).

    That windows fucks around with line endings and uses CRLF instead of the
    more efficient LF is screwed up.



    If you do this:

    as hello.s

    What might someone expect the output to be?

    What the FM documents. RTFM.

    I see. So forget just having intuitive behaviour. Or even behaviour that
    is compatible with related tools, so that:

    gcc -c file1.c produces file1.o
    gcc -c file1.c file2.c produces file1.o file2.o

    but:

    as file1.s produces a.out
    as file1.s file2.s produces a.out

    Oh, I see the problem: I haven't read the fucking manual. Except that if
    I do read it, the behaviour will still be around the bend.

    You guys all deserve medals for being so tolerant.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Scott Lurndal on Fri Jan 12 20:16:08 2024
    On 12/01/2024 19:18, Scott Lurndal wrote:
    bart <bc@freeuk.com> writes:
    On 12/01/2024 18:10, Janis Papanagnou wrote:
    On 12.01.2024 18:59, bart wrote:
    On 12/01/2024 16:50, Scott Lurndal wrote:

    $ cat file.c | cpp | c0 | c1 | c2 | as > file.o

    Using ">" on binary content?

    Of course.

    That seems off.

    Why?

    Because when you see ">" on a command line, it means redirecting output
    that would normally be shown as text on a console or terminal.

    No, it doesn't mean that at all. It never has meant that.

    name' tells the shell to open 'name' on stdout before executing 'as'.

    And without '> name', where does stuff sent to stdout end up?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Scott Lurndal on Fri Jan 12 20:21:29 2024
    On 2024-01-12, Scott Lurndal <scott@slp53.sl.home> wrote:
    David Brown <david.brown@hesbynett.no> writes:
    On 12/01/2024 00:20, bart wrote:

    That's exactly how I view gcc's UI. Most Linux-derived utilities are as
    bad: you invoke them, and they just apparently hang. Then you realise
    they're waiting for input. Would it kill somebody to get them to display >>> a prompt?

    Are you sure you are not exaggerating just a /tiny/ bit?

    Clearly, Bart is trolling.

    And clearly, he doesn't understand the basic unix philosophy
    of combining commands in pipeline, where "displaying a prompt"
    would be silly.

    C:\Users\kazk>findstr d
    ... "hangs" ...

    Even Microsoft somehow got the memo that at least text filters
    shouldn't spew chatter.

    I can't think of any Unix programs other than "ed" that take commands on standard input, but don't print a prompt by default.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Janis Papanagnou on Fri Jan 12 21:01:58 2024
    On 12/01/2024 18:02, Janis Papanagnou wrote:
    On 12.01.2024 18:09, bart wrote:
    On 12/01/2024 16:34, David Brown wrote:

    It looks like 'make' is competing with 'bash' then!

    Why don't you just read about those two tools and learn, instead
    of repeatedly spouting such stupid statements of ignorance.

    Because I've repeatedly said I don't need them. Why can't you accept that?

    How about YOU learn how to build software without those tools?

    It's a _simple_ tool - not complex, as you've previously posted -
    where you can define dependencies of entities, and define commands
    that create the targets if entities that are required by the target
    had changed. Its basic syntax and also its logic is simple,


    And this is a crucial feature; for professional non-trivial projects.

    Come on then, tell me how big your projects are. Are they bigger than
    Scott Lurndal's 10Mloc example? (Which seems to be mostly Python source
    code.)


    That is something I've never needed done automatically in my own work (I
    do it manually as I will know my projects intimately when I'm working
    with them).

    Yes, we know. You've repeatedly shown that you are actually doing
    small one-man-shows in projects that I can only call toy-projects.

    This is incredibly patronising.

    What is wrong with one-man projects?

    What is wrong with writing non-professional software? Is that the same
    as non-commercial?

    Where is the line between a toy project and a non-toy project? Is it
    related to how lines or how many modules an application might have, or
    the size of the final binaries?

    Is it to do with the number of end-users?

    Is lua.exe a toy application? (Since it only has 33 modules and is a
    300KB program.)


    Professional projects have a different situation in many respects.
    (I don't go into detail here, since you're anyway only interested
    in your local comfort zone.)

    No, don't. I assume you've got some hugely complicated app with a
    million moving parts. It's so big that nobody knows what's what. Your
    compilers are so slow that you HAVE to use dependencies to avoid
    spending 90% of the day twiddling your thumbs.

    That's a million miles from the stuff I do, yet you still insist /I/
    should be using all the same complicated tools you do.

    Actually I don't care what the hell you do. I care when it impacts me
    when I sometimes have to build something, WHICH IS RARELY MORE THAN
    DOZENS OF MODULES OR 100/200K LINES OF CODE, so obviousy trivial
    compared with your stuff, and yet I have to go through some torturous
    process to get it done.

    Let me tell about my own tools:

    * My main compiler (not for C), is a whole-program compiler (so
    interdepency graphs are pointless).

    * It can whizz through sourcecode at 500,000 lines per second at least

    * It can generate executable code at 5MB per second at least

    This means that for a project that is about 0.5Mloc, or produces a 5MB executable, recompiling everything might take ONE SECOND.

    (And that is with the cheapest PC in the shop and with a non-optimised compiler.)

    Presumably YOUR apps are massively bigger and more complex than that.
    But most stuff /I/ want write, or build of other people's, will be much smaller.

    This is why I consider make and friends pointless at this end of the scale.



    For production builds, it doesn't matter if everything is compiled.

    For a production build we extract a committed release into an own
    file system branch and build all, first for the tests, then to pack
    the source (optionally), and then the complete runtime environment.

    But we're doing professional software products. And the production
    build comes _at the end_.

    Before that we need efficient mechanisms to create consistent systems
    and configurations, and to avoid unnecessary compiles. And the 'make'
    process does exactly that.

    Well, /I/ don't need that. I used to write commercial software in the
    90s. I really never had some problems.

    Half the line count (then about 150Kloc in all) was in scripting code.
    That could be modified and tested /in the middle of a running application/.

    I could send (by email) a script module to a customer and they could run
    that without restarting their session.

    Others could use my scripting language to create their own OEM add-on
    products, without needing to rebuild the main application.

    Yes, there was an install and configuration process, done via a GUI.

    So, now tell me where the hell 'makefiles' would fit into that scenario.


    Just accept that some of this stuff is out of /your/ comfort zone.

    I write all my own tools, including a private C compiler. (I use it to
    help with translating bindings from C headers, or to build DLLs of
    libraries that exist as C source code, when the build info doesn't make
    it in impossible.)

    If I needed a tool like 'make', I would have created one.

    Instead I developed fast, whole-program compilers and scripting languages.

    Clearly your imputations are based on ignorance.

    Yeah. I could say the same thing. But usually I try and stay polite and
    argue only against ideas and not people.

    Have a good day.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to bart on Fri Jan 12 21:51:56 2024
    On 2024-01-12, bart <bc@freeuk.com> wrote:
    On 12/01/2024 18:02, Janis Papanagnou wrote:
    On 12.01.2024 18:09, bart wrote:
    On 12/01/2024 16:34, David Brown wrote:

    It looks like 'make' is competing with 'bash' then!

    Why don't you just read about those two tools and learn, instead
    of repeatedly spouting such stupid statements of ignorance.

    Because I've repeatedly said I don't need them. Why can't you accept that?

    You do need make, if you're on a Unix-like system and you want to make
    "hello" out of "hello.c" with a command that consists of only two words.

    This requirement was articulated by you.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to bart on Fri Jan 12 21:31:05 2024
    On 2024-01-12, bart <bc@freeuk.com> wrote:
    but also build documentation (doxygen, LaTeX,
    pandoc, graphviz, etc.), run tests, run "clean", build release zip
    files, download to a target board via a debugger, and all sorts of other
    bits and pieces according to the needs of the project.  One makefile
    beats a dozen scripts.

    It looks like 'make' is competing with 'bash' then!

    Make recipes are actually lines of shell code executed by the shell;
    make does not reimplement a redundant command interpretation.(*)

    You can use the SHELL variable to specify an alternative interpreter
    for the make recipe lines.

    ---

    * GNU Make contains logic for recognizing recipe lines that are
    trivial commands, and eliding the use of the shell for those. So that is
    to say that some simple command like "cc this.c -o this.o -c" is handled
    via fork and exec directly. It must be the case that if SHELL is used
    to specify an alternative interpreter, that logic is suppressed.

    That's exactly how I view gcc's UI. Most Linux-derived utilities are
    as bad: you invoke them, and they just apparently hang. Then you
    realise they're waiting for input. Would it kill somebody to get them
    to display a prompt?

    Are you sure you are not exaggerating just a /tiny/ bit?

    I mainly remember the times when they do hang.

    DOS/Windows stuff hangs also:

    C:\Users\kazk>findstr foo
    ... "hang" ...

    Even Microsoft clued in to the idea that a text filter shouldn't
    spew extraneous diagnostics by default.

    But with 'as', it just sits there. I wonder what it's waiting for; for
    me to type in ASM code live from the
  • From Scott Lurndal@21:1/5 to bart on Fri Jan 12 22:18:14 2024
    bart <bc@freeuk.com> writes:
    On 12/01/2024 19:18, Scott Lurndal wrote:
    bart <bc@freeuk.com> writes:
    On 12/01/2024 18:10, Janis Papanagnou wrote:
    On 12.01.2024 18:59, bart wrote:
    On 12/01/2024 16:50, Scott Lurndal wrote:

    $ cat file.c | cpp | c0 | c1 | c2 | as > file.o

    Using ">" on binary content?

    Of course.

    That seems off.

    Why?

    Because when you see ">" on a command line, it means redirecting output
    that would normally be shown as text on a console or terminal.

    No, it doesn't mean that at all. It never has meant that.

    name' tells the shell to open 'name' on stdout before executing 'as'.

    And without '> name', where does stuff sent to stdout end up?


    Wherever the shell points stdout to before executing the program.
    Might be a terminal, might be a pipe, might be a fifo, might
    be a file, might be a network stream (via netcat or via shell
    support for TCP connections), might be a printer.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to bart on Fri Jan 12 22:22:29 2024
    bart <bc@freeuk.com> writes:
    On 12/01/2024 18:02, Janis Papanagnou wrote:


    And this is a crucial feature; for professional non-trivial projects.

    Come on then, tell me how big your projects are. Are they bigger than
    Scott Lurndal's 10Mloc example? (Which seems to be mostly Python source >code.)

    The example shown had 8 million lines of C and C++ code. Less
    than 10% was python.

    Granted, a significant fraction of that is generated from yaml descriptions
    of memory mapped registers, yet it is still compiled by the C and C++ compilers.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to Keith Thompson on Fri Jan 12 22:16:25 2024
    Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
    bart <bc@freeuk.com> writes:
    On 12/01/2024 16:50, Scott Lurndal wrote:
    bart <bc@freeuk.com> writes:
    On 12/01/2024 13:40, David Brown wrote:
    On 12/01/2024 00:20, bart wrote:

    But with 'as' it just sits there. I wonder what it's waiting for; for
    me to type in ASM code live from the terminal?
    It does that so you can pipe the assembler source code in to the
    assembler.
    $ cat file.s | as
    $ cat file.c | cpp | c0 | c1 | c2 | as > file.o

    Yes, I used this as an example pipeline. I don't recall
    if the original as(1) wrote to stdout or always just to
    a.out.

    What might someone expect the output to be? Probably not 'a.out', more
    likely hello.o. Why /isn't/ it just hello.o?

    Partly historical inertia, and partly because "as" can't always know
    what the output file should be, for example if its input isn't a file.

    Yup. a.out is the historical inertia.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lew Pitcher@21:1/5 to Scott Lurndal on Fri Jan 12 23:04:56 2024
    On Fri, 12 Jan 2024 22:16:25 +0000, Scott Lurndal wrote:

    Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
    bart <bc@freeuk.com> writes:
    On 12/01/2024 16:50, Scott Lurndal wrote:
    bart <bc@freeuk.com> writes:
    On 12/01/2024 13:40, David Brown wrote:
    On 12/01/2024 00:20, bart wrote:

    But with 'as' it just sits there. I wonder what it's waiting for; for >>>>> me to type in ASM code live from the terminal?
    It does that so you can pipe the assembler source code in to the
    assembler.
    $ cat file.s | as
    $ cat file.c | cpp | c0 | c1 | c2 | as > file.o

    Yes, I used this as an example pipeline. I don't recall
    if the original as(1) wrote to stdout or always just to
    a.out.

    IIRC, Dennis Ritchie told a story about early Unix implemented on a system without memory protection mechanisms. The language tools of the time took
    up a lot of memory when compiling, and could trespass into other user's
    process memory space. It became customary, on this system, to holler
    "A dot OUT" whenever compiling, to warn the other programmers that the
    compiler was in use.

    I believe that the compiler suite /always/ wrote an a.out file. But,
    it wouldn't have been the assembler (as), but the linker (ld) that
    created it.

    What might someone expect the output to be? Probably not 'a.out', more
    likely hello.o. Why /isn't/ it just hello.o?

    Partly historical inertia, and partly because "as" can't always know
    what the output file should be, for example if its input isn't a file.

    Yup. a.out is the historical inertia.




    --
    Lew Pitcher
    "In Skills We Trust"

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From tTh@21:1/5 to bart on Sat Jan 13 00:16:23 2024
    On 1/12/24 18:59, bart wrote:

    $ cat file.c | cpp | c0 | c1 | c2 | as > file.o

    Using ">" on binary content? That seems off.

    Why ?

    --
    +---------------------------------------------------------------------+
    | https://tube.interhacker.space/a/tth/video-channels | +---------------------------------------------------------------------+

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lew Pitcher@21:1/5 to Lew Pitcher on Fri Jan 12 23:30:27 2024
    On Fri, 12 Jan 2024 23:04:56 +0000, Lew Pitcher wrote:
    [snip]

    I believe that the compiler suite /always/ wrote an a.out file. But,
    it wouldn't have been the assembler (as), but the linker (ld) that
    created it.

    Looking back into my manuals, it appears that the assembler defaulted
    to writing a.out if the assembly did not require any external references.

    To quote the "Unix Assembler Reference Manual" for Seventh Edition Unix
    "As ... produces an output file that contains relocation information
    and a complete symbol table; thus the output is acceptable to the
    UNIX link-editor ld, which may be used to combine the outputs of
    several assembler runs and to obtain object programs from libraries.
    The output format has been designed so that if a program contains
    no unresolved references to external symbols, it is executable
    without further processing."
    and
    "The output of the assembler is by default placed on the file a.out
    in the current directory; the "-o" flag causes the output to be
    placed on the named file."



    [snip]
    --
    Lew Pitcher
    "In Skills We Trust"

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Kaz Kylheku on Sat Jan 13 00:13:17 2024
    On 12/01/2024 21:51, Kaz Kylheku wrote:
    On 2024-01-12, bart <bc@freeuk.com> wrote:
    On 12/01/2024 18:02, Janis Papanagnou wrote:
    On 12.01.2024 18:09, bart wrote:
    On 12/01/2024 16:34, David Brown wrote:

    It looks like 'make' is competing with 'bash' then!

    Why don't you just read about those two tools and learn, instead
    of repeatedly spouting such stupid statements of ignorance.

    Because I've repeatedly said I don't need them. Why can't you accept that?

    You do need make, if you're on a Unix-like system and you want to make "hello" out of "hello.c" with a command that consists of only two words.

    To clarify:

    * You have a hypothetical situation where you want to turn hello.c into hello.exe (I'm going insist on that extension in this example).

    * The tool you have generates a.out given the two-word command 'cc hello.c'

    * Getting hello.exe requires the three-word command 'cc hello.c -ohello.exe'

    The solution apparently is not to make a three-line change to 'cc' to
    fix it, but to create a new language and implemention just to provide a
    wrapper around the unchanged behaviour of 'cc'.

    That sounds barmy.

    The astronauts who had to apply the conversion kit to the HST had no
    choice. Clearly they would rather have got the mirror in the first place.

    But your attitude seems to be that such a sticking-plaster fix is superior.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Scott Lurndal on Sat Jan 13 01:02:35 2024
    On 12/01/2024 22:22, Scott Lurndal wrote:
    bart <bc@freeuk.com> writes:
    On 12/01/2024 18:02, Janis Papanagnou wrote:


    And this is a crucial feature; for professional non-trivial projects.

    Come on then, tell me how big your projects are. Are they bigger than
    Scott Lurndal's 10Mloc example? (Which seems to be mostly Python source
    code.)

    The example shown had 8 million lines of C and C++ code. Less
    than 10% was python.

    Granted, a significant fraction of that is generated from yaml descriptions of memory mapped registers, yet it is still compiled by the C and C++ compilers.

    I misread 700Kloc of Python as 7Mloc (figures should be comma-separated).

    Malcolm's BBX project is about 400Kloc of code. But 340Kloc of it is in
    one header file that contains data.

    That accounts for the executable being about 2MB.

    A cold build with mcc takes 2.7 seconds. Subsequent full builds after a
    minor change take 1.4 seconds.

    If I wanted I can spend a few days removing the ASM intermediates, that
    would likely double the speed to 0.7 seconds, and probably 0.5 seconds
    if the compiler was optimised.

    Tcc is probably already at that, but has some issues with this code.

    If you remember, this is the project that needed CMake to produce the
    makefile (which didn't work). I build it with:

    c:\bbx\src\mcc @bbx

    with bbx containing:

    *.c freetype/*.c samplerate/*.c

    Fun fact 1: 'gcc @bbx' doesn't work. File-globbing (I assume just on
    this Windows version ) only works on the command line, not from inside a
    file like this.

    Fun fact 2: If I get rid of the file-globbing and list the files
    individually, gcc requires that \ is used as path separator, not /. I
    can also never remember which one it doesn't like. (My mcc accepts either.)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Keith Thompson on Sat Jan 13 01:12:17 2024
    On 13/01/2024 00:47, Keith Thompson wrote:
    bart <bc@freeuk.com> writes:

    It happens that once you have a working Makefile, it works equally
    well either to rebuild a project after a single change, or to build
    an entire project from scratch. Someone could probably create
    a simpler version of "make" that doesn't look at dependencies,
    and that always rebuilds everything. Such a tool would be worse
    than "make" for building projects during development, and not
    significantly better than "make" for building projects from scratch.

    And that's ignoring the "-j" option, which allows "make" to execute
    multiple steps in parallel. That works only because "make" knows
    about dependencies, and it can result in a full build from scratch
    finishing much more quickly. A simple script that just compiles
    each file isn't likely to do that. You can typically specify a
    maximum number of parallel jobs equal to the number of CPUs on your
    build system, e.g., `make -j $(nproc)`.

    That's a reasonable thing to do. But how does make do it? Can't a
    compiler apply the same approach if N files have been submitted?

    After all C allows independent compilation of modules. (Something my
    language doesn't have; there the granularity is an EXE file, not a module.)


    That sounds barmy.

    To you, I'm sure it does. It isn't.

    The astronauts who had to apply the conversion kit to the HST had no
    choice. Clearly they would rather have got the mirror in the first
    place.

    But your attitude seems to be that such a sticking-plaster fix is superior.

    I'd like to bring something to your attention. You often make
    statements about the attitudes of the people you're having these
    discussions with. Those statements are almost always wrong. I'd say
    more if I thought you'd be interested.


    My post was in response to "needing 'make' if you wanted a two-word
    command to build 'hello'".

    I didn't seriously think that was the reason make was invented.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Janis Papanagnou@21:1/5 to Keith Thompson on Sat Jan 13 04:36:18 2024
    On 12.01.2024 21:59, Keith Thompson wrote:

    If you run "command" at a shell prompt, its stdout goes to the terminal
    in which the shell is running. [...]

    Not quite. It goes to what stdout has actually been defined. E.g.

    exec > ouch
    ls -l

    So it depends on the context.

    Janis

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Janis Papanagnou@21:1/5 to bart on Sat Jan 13 05:01:45 2024
    On 12.01.2024 19:53, bart wrote:
    On 12/01/2024 18:10, Janis Papanagnou wrote:
    On 12.01.2024 18:59, bart wrote:
    On 12/01/2024 16:50, Scott Lurndal wrote:

    $ cat file.c | cpp | c0 | c1 | c2 | as > file.o

    Using ">" on binary content?

    Of course.

    That seems off.

    Why?

    Because when you see ">" on a command line, it means redirecting output
    that would normally be shown as text on a console or terminal.

    I propose that you try to give up what you think is "normally" and
    base your knowledge and opinions on facts. Honestly, it will make
    communication generally easier and not make you look like a moron.


    But you rarely see pure binary being displayed like that on a text display.

    Consider stdin and stdout just as input/output channels and don't
    make unnecessary further assumptions that will only mislead you.

    (Other aspects had been posted by others already, so I abstain.)

    [...]

    However I'm obviously just a bot, so what do I know.

    That was actually just a thought that popped up. But after thinking
    about it I got aware that AI bots (despite their inherent problems)
    are more fact-oriented and operate on a much larger knowledge base.

    Janis

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Janis Papanagnou@21:1/5 to bart on Sat Jan 13 05:12:24 2024
    On 12.01.2024 21:14, bart wrote:
    On 12/01/2024 19:15, Scott Lurndal wrote:
    [...]

    [...]

    You guys all deserve medals for being so tolerant.

    I think your cynicism is unjustified.

    When I read all your (often just annoying) posts in this thread and
    could observe then how calm and obliging the audience answered the
    posts for a long time - where e.g. my patience would had been at an
    end already - I said to myself; what a polite and tolerant audience
    this is!

    Yes, folks, you are tough guys. You have my due respect. Sincerely.

    Janis

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Janis Papanagnou@21:1/5 to bart on Sat Jan 13 05:15:02 2024
    On 12.01.2024 21:16, bart wrote:
    On 12/01/2024 19:18, Scott Lurndal wrote:

    name' tells the shell to open 'name' on stdout before executing 'as'.

    And without '> name', where does stuff sent to stdout end up?

    It depends on the output channel where stdout was directed to
    before the command is called. You know that the default is the
    terminal, but it depends. That's how it works.

    Janis

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Janis Papanagnou on Sat Jan 13 04:31:27 2024
    On 2024-01-13, Janis Papanagnou <janis_papanagnou+ng@hotmail.com> wrote:
    On 12.01.2024 19:53, bart wrote:
    On 12/01/2024 18:10, Janis Papanagnou wrote:
    On 12.01.2024 18:59, bart wrote:
    On 12/01/2024 16:50, Scott Lurndal wrote:

    $ cat file.c | cpp | c0 | c1 | c2 | as > file.o

    Using ">" on binary content?

    Of course.

    That seems off.

    Why?

    Because when you see ">" on a command line, it means redirecting output
    that would normally be shown as text on a console or terminal.

    I propose that you try to give up what you think is "normally" and
    base your knowledge and opinions on facts. Honestly, it will make communication generally easier and not make you look like a moron.

    He has a point. If a program produces binary data on standard output,
    then it will pretty much always have to be redirected.

    Usually for programs that produce binary deliverables, you want an
    option for standard output. Or perhaps the POSIX - convention: if the
    file name given is - (ASCII hyphen), use standard output rather
    than opening a file.

    In C, standard output (stdio) isn't even a binary stream. It is a text
    stream. On Unix that doesn't matter; there is no actual separate text
    mode; the "b" modifier of fopen is ignored.

    I don't think there is any way to write an ISO C strictly conforming
    program which switches stdio to binary mode so that it can reliably
    output binary data on standard output on any system. (I'm sure I looked
    into this in the past, so if there is a way, I forgot about it.)

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to bart on Sat Jan 13 04:46:16 2024
    On 2024-01-12, bart <bc@freeuk.com> wrote:
    I see. So forget just having intuitive behaviour. Or even behaviour that
    is compatible with related tools, so that:

    gcc -c file1.c produces file1.o
    gcc -c file1.c file2.c produces file1.o file2.o

    but:

    as file1.s produces a.out
    as file1.s file2.s produces a.out

    Few people invoke "as" directly. It is not intended for human use,
    regardless of how it is invoked.

    You can invoke gcc on .s files:

    gcc -c file1.s # file1.o pops out

    gcc is a compiler driver, not a compiler; it reacts to suffixes.

    If you change the suffix to .S, then gcc will run the C preprocessor on
    the file. This can be very useful.

    Besides being able to use include files, macros and conditionals,
    one obvious benefit of that is that you get standard comment syntax.

    Different targets of GNU as have different commenting conventions!!!
    And this is for compatibility reasons with other assemblers for those platforms.

    Using C preprocessing, it's possible to make one source file work with different implementations of as that use different assembly language
    syntax: like if it happens that there is a proprietary as that has
    different syntax from GNU as.

    You guys all deserve medals for being so tolerant.

    By now you've almost wasted more keystrokes complaining about how some
    command lines require several more tokens than they should, than you
    will ever save in your entire lifetime by actually using your preferred
    shorter command lines.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to bart on Sat Jan 13 04:17:06 2024
    On 2024-01-13, bart <bc@freeuk.com> wrote:
    On 13/01/2024 00:47, Keith Thompson wrote:
    bart <bc@freeuk.com> writes:

    It happens that once you have a working Makefile, it works equally
    well either to rebuild a project after a single change, or to build
    an entire project from scratch. Someone could probably create
    a simpler version of "make" that doesn't look at dependencies,
    and that always rebuilds everything. Such a tool would be worse
    than "make" for building projects during development, and not
    significantly better than "make" for building projects from scratch.

    And that's ignoring the "-j" option, which allows "make" to execute
    multiple steps in parallel. That works only because "make" knows
    about dependencies, and it can result in a full build from scratch
    finishing much more quickly. A simple script that just compiles
    each file isn't likely to do that. You can typically specify a
    maximum number of parallel jobs equal to the number of CPUs on your
    build system, e.g., `make -j $(nproc)`.

    That's a reasonable thing to do. But how does make do it? Can't a
    compiler apply the same approach if N files have been submitted?

    Yes. And in fact, languages with good module support like Modula-2
    don't need external make utilities.

    After all C allows independent compilation of modules. (Something my
    language doesn't have; there the granularity is an EXE file, not a module.)

    C has no specific syntax for expressing modules. It has translation
    units, with preprocessor header files used for interfacing.

    Dependencies are useful for incremental compilation (as Keith notes
    above). It's a given that when no part of a program is built, we
    are going to build all of its modules and so we don't need the
    dependencies.

    In the GCC world, there are compiler options used to tell the compiler,
    for each compiled translation unit, to emit the dependencies that it encounters. It emits them in the form of a makefile.

    For instance, if "foo.c" includes "foo.h" and "bar.h", then when gcc is compiling it (if given suitable options) will emit a small "foo.d" file
    (you can control the name) which looks something like:

    foo.o: foo.c foo.h bar.h

    These *.d files can be included into the makefile.

    Once these files exist, there is a dependency graph for the project,
    which is accurate, since it comes from the compiler.

    It's a hacky system, because, for instance, it will not pick up
    situations when a header file is deleted. Say bar.h is deleted and foo.c
    edited not to include it. The .d file is still the same and says that
    making foo.o requires bar.h. So make will refuse to build foo.o due to a missing prerequisite. But a correct .d file won't be emitted unless
    foo.o is compiled.

    In most other situations, it's fine.

    A C compiler could, instead of emitting makefile fragments, keep
    the dependency information in some repository which it itself
    understands, in order to recompile what is necessary.

    Only problem is that that compiler would be reimplementing most of make, probably badly, and every other similar compiler would have to do the
    same in order to have the same benefit.

    (You'd pretty much want some "libmake" library for this, so as not
    to reimplement it from scratch. Should every compiler implement its own
    job server/scheduler for handling parallel builds?)

    My post was in response to "needing 'make' if you wanted a two-word
    command to build 'hello'".

    In that specific environment, that's what we already have. That two-word command will do it with the least effort on your part.

    You need it if you want to expend no more effort than the two word
    command: not write a whole bunch of scripting or other code so
    that you then have some other two-word command.

    I didn't seriously think that was the reason make was invented.

    Yet, at some point make acquired suffix rules, which infer
    prerequisites from targets by pattern. Moreover, someone added
    a suffix rule that an X can be made from X.c. Not only that but
    from other suffixed files like X.f (Fortran) and whatnot.

    So the need was acknowledged; someone found that it would be useful
    to build foo from foo.c with a simple "make foo" command, which
    doesn't require a makefile to be present.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Janis Papanagnou@21:1/5 to Kaz Kylheku on Sat Jan 13 07:13:27 2024
    On 13.01.2024 05:31, Kaz Kylheku wrote:

    [...] If a program produces binary data on standard output,
    then it will pretty much always have to be redirected. [...]

    The problem with that view is; what is a binary?

    I have a *.c source code that contains literal UTF-8 characters.
    If I 'cat' that file to the terminal I either see these UTF-8
    characters or gibberish, depending on how I set up the terminal.

    Unix is (almost) transparent WRT "binary" representations.

    Similar with the file system. Are file names in other encodings
    than ASCII (or any of the 8-bit extensions) binary? (It only
    handles '\0' and '/' differently but the rest transparent.)

    Janis

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Janis Papanagnou@21:1/5 to bart on Sat Jan 13 06:54:38 2024
    On 12.01.2024 22:01, bart wrote:
    On 12/01/2024 18:02, Janis Papanagnou wrote:
    On 12.01.2024 18:09, bart wrote:
    On 12/01/2024 16:34, David Brown wrote:

    It looks like 'make' is competing with 'bash' then!

    Why don't you just read about those two tools and learn, instead
    of repeatedly spouting such stupid statements of ignorance.

    Because I've repeatedly said I don't need them. Why can't you accept that?

    Oh, I accept that. - But then I also expect that you don't spread
    uninformed nonsense.


    How about YOU learn how to build software without those tools?

    You're continuing to make silly and stupid statements. - I thought
    (given you know DEC and CP/M) you are not a child any more, yet
    behave so.

    What makes you think that I wouldn't be able to do trivialities?


    It's a _simple_ tool - not complex, as you've previously posted -
    where you can define dependencies of entities, and define commands
    that create the targets if entities that are required by the target
    had changed. Its basic syntax and also its logic is simple,


    And this is a crucial feature; for professional non-trivial projects.

    Come on then, tell me how big your projects are. Are they bigger than
    Scott Lurndal's 10Mloc example? (Which seems to be mostly Python source code.)

    Again playing childish? ("Mine is bigger that yours", sort of?)

    If you're interested what I actually do and have done, I can tell
    you. (Not that it would address or solve any inherent issue *you*
    obviously have.)

    The past decade (or so) "my" personal projects were only private
    hobbies, i.e. small toy-projects from a couple lines to a couple
    thousand lines. But I when I speak about "professional software
    engineering" I am rather speaking about the professional projects.

    Some outline; I was engaged in projects of various sizes. I don't
    recall the (not very significant) LOC numbers; these were anyway
    only in one case relevant, in a refactoring project of a large
    software component (used by at that time 1000+ software companies
    for their products, and at that time by nearly 20 million people
    in our country). The projects that I led myself or was member of
    ranged from a handful on-site persons to many hundreds persons
    spread across several sites and even different companies. And
    the development durations from very short ranges up to years. The
    areas for which the various software projects was developed were;
    for the big telecommunication companies (e.g. BT, Dt. Telekom),
    for the financial sector, for the state government). We used local
    tools for our site(s), and also collaborative tools. The source
    code or libraries were partly imported by collaborating companies,
    locally they were spread across various project component file
    systems. It had been tens thousands of files (I don't recall the
    exact number) and millions of lines of code (dito.). Everyone in
    the project was able to work on any of the sub-projects or parts,
    no specific knowledge (say, about compiler or library versions)
    was necessary by the individual member. Make was a standard tool
    almost everywhere. Other tools as well; configuration management,
    version control, test environments, project management tools, etc.

    These were all professional software projects, as opposed to my
    (or your) toy projects.



    That is something I've never needed done automatically in my own work (I >>> do it manually as I will know my projects intimately when I'm working
    with them).

    Yes, we know. You've repeatedly shown that you are actually doing
    small one-man-shows in projects that I can only call toy-projects.

    This is incredibly patronising.

    I was merely pointing out that you explained yourself (a couple
    of times) what sort of projects you are working on; I just named
    them toy-projects to make apparent to you where we need a more
    professional approach, and that you only address with your view
    your personal small isolated programming bubble. It's still quoted
    above with context, here again (for example):
    "I will know my projects intimately when I'm working with them"
    and you also mentioned I think more than once that you work alone.
    You might admit that this sort of manageable programming is very
    different from professional projects (as I depicted some above).


    What is wrong with one-man projects?

    There's nothing wrong with them. (I said above that privately I
    also do such "projects".) At some point of project complexity you
    are advised to handle it more professionally, though. And that is
    usually supported by sophisticated project tools and environments.


    What is wrong with writing non-professional software? Is that the same
    as non-commercial?

    (The question is IMO quite irrelevant, not worth discussing.)


    Where is the line between a toy project and a non-toy project? Is it
    related to how lines or how many modules an application might have, or
    the size of the final binaries?

    Is it to do with the number of end-users?

    There are a couple factors that you may also derive from above.
    (Beyond that it's not worth discussing where one ends or the other
    begins.)

    It's worth to understand, though, that 'make' is not a complex or
    unnecessary tool. If you understand its (simple) concept you can
    (but don't need to) also use it for your small projects. You only
    gain something, not lose anything; once you've overcome the barrier
    of acceptance for a probably unknown or unfamiliar tool it's really
    nice. (For example I maintain a dvds.csv file and generate a HTML
    page for it that I then upload; why not put the generation process
    commands and the simple dependencies in a Makefile and just call
    'make' and/or 'make install'? - I have tons of little toy-projects
    and instead of having everything in mind I have it either in a
    Makefile or in a small shell script that occasionally gets into a
    Makefile, so that I only need to do a 'make' in whatever context
    I actually am.)

    It should have meanwhile become obvious that no one forces you to
    use Makefiles. And that there's also nothing to say again one's
    toy-projects. - Only you cannot derive from such primitive cases
    about the sensibility of useful (and even necessary) tools and be
    constantly whining and complaining about them only because you
    don't see the gain you have with the tools. And that you don't
    know them and badmouth them doesn't make it easier to discuss;
    you should at least inform yourself if you feel the need to piss
    on well established tools.


    [...]


    Professional projects have a different situation in many respects.
    (I don't go into detail here, since you're anyway only interested
    in your local comfort zone.)

    No, don't. I assume you've got some hugely complicated app with a
    million moving parts.

    You again expose your habit to wrongly "assume" (= to make up)
    things just because you don't understand the topic lack the
    experience and avoid the facts.

    It's so big that nobody knows what's what. Your
    compilers are so slow that you HAVE to use dependencies to avoid
    spending 90% of the day twiddling your thumbs.

    Clueless as you are you are not in the position to be cynical.

    In the industry where I've done my professional projects we had
    no slow computers. But we had also no toy-projects. Yes, some of
    the (full!) compile runs lasted many hours; companies just cannot
    afford that waste of time if you compile everything only because
    you have no professional computer scientists that know how things
    have to be done more sophisticated to reduce time. If you manage
    the dependencies you can reduce the effort to compile a software
    system to minutes if not seconds. - Ignoring this simple fact is
    not only unprofessional, it's plain stupid.


    That's a million miles from the stuff I do, yet you still insist /I/
    should be using all the same complicated tools you do.

    Despite you have repeatedly been told by many posters you still
    repeat that nonsense. - No one said you should be using it. Are
    you so pathological that you don't get it?

    [...]

    Let me tell about my own tools:

    (You constantly do and no one cares.)

    [...]

    So, now tell me where the hell 'makefiles' would fit into that scenario.

    It's answered above by me as an experience report and suggestion
    to consider. It's (only) up to you to take action or ignore it.


    Just accept that some of this stuff is out of /your/ comfort zone.

    You actually know nothing about my "comfort zone"; as opposed to
    you I wasn't repeatedly complaining about this or that.

    Rather that's, as we observed here so often in your posts, a very
    typical childish sort of a defense kick.

    [...]

    If I needed a tool like 'make', I would have created one.

    It's very interesting that despite your very small and restricted
    experience you'd decide to follow the "not invented here" principle
    instead of using long established, refined, and well accepted tools
    that are already available (and even for free), and reliably work.

    [...]

    Clearly your imputations are based on ignorance.

    Yeah. I could say the same thing. But usually I try and stay polite and
    argue only against ideas and not people.

    Yet you don't.


    Have a good day.

    I wish you the same.

    Janis

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Kaz Kylheku on Sat Jan 13 12:03:59 2024
    On 13/01/2024 04:17, Kaz Kylheku wrote:
    On 2024-01-13, bart <bc@freeuk.com> wrote:
    On 13/01/2024 00:47, Keith Thompson wrote:
    bart <bc@freeuk.com> writes:

    It happens that once you have a working Makefile, it works equally
    well either to rebuild a project after a single change, or to build
    an entire project from scratch. Someone could probably create
    a simpler version of "make" that doesn't look at dependencies,
    and that always rebuilds everything. Such a tool would be worse
    than "make" for building projects during development, and not
    significantly better than "make" for building projects from scratch.

    And that's ignoring the "-j" option, which allows "make" to execute
    multiple steps in parallel. That works only because "make" knows
    about dependencies, and it can result in a full build from scratch
    finishing much more quickly. A simple script that just compiles
    each file isn't likely to do that. You can typically specify a
    maximum number of parallel jobs equal to the number of CPUs on your
    build system, e.g., `make -j $(nproc)`.

    That's a reasonable thing to do. But how does make do it? Can't a
    compiler apply the same approach if N files have been submitted?

    Yes. And in fact, languages with good module support like Modula-2
    don't need external make utilities.

    Finally somebody admitting that some languages may not need make as much.

    My language from 25 years ago didn't have compiler-supported modules but projects were structured in a certain way: all modules shared the same project-wide header file (which could group other headers).

    So a change in one module required compiling only that module. A change
    in a header usually required all modules to be recompiled. (Including
    the headers: in this scheme, they could contain their own code and
    functions which existed in an separate, outer scope.)

    If I'd used 'make', the dependency graph would only have told me what I
    already knew.

    My current language has language- and compiler-supported modules, but is designed for whole-program compilation and is very fast as previously
    stated.

    Modules can be grouped into sub-programs, and very large programs could
    turn those into dynamic libraries that are compiled separately, but I'm
    a long way from needing to do that.


    After all C allows independent compilation of modules. (Something my
    language doesn't have; there the granularity is an EXE file, not a module.)

    C has no specific syntax for expressing modules. It has translation
    units, with preprocessor header files used for interfacing.

    OK, independent compilation of translation units. It has header/include
    files which can recursively include other headers, and that can be
    shared in an ad hoc manner across multiple translation units.

    A large program needs selective and parallel compilation more,
    especially as people prefer slow compilers for C.

    Since if you have 50 modules (translation units) and each uses the same
    giant header files (GTK, windows etc), those headers will normally be
    processed 50 times.

    In my current language that would only be done once (and using a
    condensed form of those APIs which is a fraction the size of the C headers).

    A C compiler could, instead of emitting makefile fragments, keep
    the dependency information in some repository which it itself
    understands, in order to recompile what is necessary.

    Only problem is that that compiler would be reimplementing most of make, probably badly, and every other similar compiler would have to do the
    same in order to have the same benefit.

    We're now talking about the part of make which is about building what I
    call a 'program unit': a single EXE or DLL file in Windows terms. (In my
    stuff, that can include a single OBJ representing multiple modules.)

    I feel this stuff is ideally done in the compiler. But the nature of C
    makes that awkward to do. How does gcc, say, have any idea of the extent
    of a program? It might not use a shared header to link a distant module
    to the rest. And if you do:

    gcc hello.c hello.c

    it only has an indication that something is wrong when it tries to link
    and there are two 'main' functions being exported.

    This can be tackled by organising projects according to guidelines, but
    it is still largely C from 1972.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Janis Papanagnou on Sat Jan 13 14:08:16 2024
    On 13/01/2024 05:54, Janis Papanagnou wrote:
    On 12.01.2024 22:01, bart wrote:

    Come on then, tell me how big your projects are. Are they bigger than
    Scott Lurndal's 10Mloc example? (Which seems to be mostly Python source
    code.)

    Again playing childish? ("Mine is bigger that yours", sort of?)

    No. I know that my projects are very small compared to some in the
    industry. The sizes of OSes and all sorts of apps tell me that.

    I was interested in where you draw the line.

    (This is the same with every kind of product. Some companies produce a
    787 airliner with 5 million components, others make a plastic comb with
    just one.)

    Some 90% of the binaries in my Windows 11 OS are under 1MB. Each of
    those forms a 'program unit' which is what my comments about 'building'
    are about: how to compile the multiple source files into one binary.

    Beyond that you get the problems of creating large 'systems' which is
    what you seem to be into.

    For the first, I don't rate the use of 'make', not with the designs I've
    made in languages and compilers.

    For the other, the use of make seems archaic.

    If you're interested what I actually do and have done, I can tell
    you. (Not that it would address or solve any inherent issue *you*
    obviously have.)

    The past decade (or so) "my" personal projects were only private
    hobbies, i.e. small toy-projects from a couple lines to a couple
    thousand lines. But I when I speak about "professional software
    engineering" I am rather speaking about the professional projects.

    Some outline; I was engaged in projects of various sizes. I don't
    recall the (not very significant) LOC numbers; these were anyway
    only in one case relevant, in a refactoring project of a large
    software component (used by at that time 1000+ software companies
    for their products, and at that time by nearly 20 million people
    in our country). The projects that I led myself or was member of
    ranged from a handful on-site persons to many hundreds persons
    spread across several sites and even different companies. And
    the development durations from very short ranges up to years. The
    areas for which the various software projects was developed were;
    for the big telecommunication companies (e.g. BT, Dt. Telekom),
    for the financial sector, for the state government). We used local
    tools for our site(s), and also collaborative tools. The source
    code or libraries were partly imported by collaborating companies,
    locally they were spread across various project component file
    systems. It had been tens thousands of files (I don't recall the
    exact number) and millions of lines of code (dito.). Everyone in
    the project was able to work on any of the sub-projects or parts,
    no specific knowledge (say, about compiler or library versions)
    was necessary by the individual member. Make was a standard tool
    almost everywhere. Other tools as well; configuration management,
    version control, test environments, project management tools, etc.

    These were all professional software projects, as opposed to my
    (or your) toy projects.

    OK, thanks. So this is more like you working for a large organisation,
    having extensive premises, perhaps multiple sites, HR departments,
    finance, sales, managers, directors, receptionists ...

    While some people like me have worked for a small company of 10 or 20
    people. I was also self-employed for a decade. In both cases
    professional work was done with real, paying customers, just like a
    million such small businesses. We sold products in a half a dozen countries.

    You might appreciate then that in a small organisation things might be
    done differently, more simply, with fewer overheads and more informally.

    This can also mirror what happens with software projects. Most
    open-source software I've wanted to build has been on the scale of a
    small business or even a one-man operation.

    Yet next to their small premises is a giant office-block containing all
    the auto-config and makefile stuff that you are obliged to deal with if
    you want to use their product.

    What is wrong with one-man projects?

    There's nothing wrong with them. (I said above that privately I
    also do such "projects".) At some point of project complexity you
    are advised to handle it more professionally, though. And that is
    usually supported by sophisticated project tools and environments.

    My situation is different: I created my own languages, working tools and environment. I've always made it a part of development, to ensure a fast development cycle and to be productive.

    Complexity was kept under control. Projects were necessarily small
    (because it was just me, but also because of limited machine resources).

    It's worth to understand, though, that 'make' is not a complex or
    unnecessary tool. If you understand its (simple) concept you can
    (but don't need to) also use it for your small projects.

    If I was to use 'make', I would have to implement it myself. This I once started to do, until I started reading the manual, and it went on for
    ever. I then disliked it even more.

    You only
    gain something, not lose anything; once you've overcome the barrier
    of acceptance for a probably unknown or unfamiliar tool it's really
    nice. (For example I maintain a dvds.csv file and generate a HTML
    page for it that I then upload; why not put the generation process
    commands and the simple dependencies in a Makefile and just call
    'make' and/or 'make install'? - I have tons of little toy-projects
    and instead of having everything in mind I have it either in a
    Makefile or in a small shell script that occasionally gets into a
    Makefile, so that I only need to do a 'make' in whatever context
    I actually am.)

    It sounds like a classic 'hammer and nail' scenario. (If your only tool
    is a hammer, every task looks like a nail.)


    It should have meanwhile become obvious that no one forces you to
    use Makefiles.

    That is actually not true. Clearly, I don't use them for my own stuff.

    But sometimes I want to build what ought to be a straightforward
    program, but the necessary info is hidden inside a makefile.

    Or in the case of LIBJPEG, inside 15 makefiles, one for each compiler
    (for that famously portable language known as C).

    However, one was a generic makefile which I was able to use, in a rare
    case of make actual working.

    I was then able to capture the commands generated, and so determine what
    the relevant files were.

    This was somewhat complex as it first produced libraries in the form of
    .a files, which were then used with further .c for building multiple EXEs.

    All /I/ needed are the list of C files to submit to a compiler for each program. Here, 'make' was just one huge obstacle. Why don't the docs
    just list the relevant files (as well the makefiles)?

    And that there's also nothing to say again one's
    toy-projects.

    LIBJPEG is not up there were your huge professional product, but it is
    hardly a toy. It could easily have been one of the libraries you used.
    (It's just rather over the top for a JPEG encoder/decoder!)

    In the industry where I've done my professional projects we had
    no slow computers. But we had also no toy-projects. Yes, some of
    the (full!) compile runs lasted many hours

    So you had slow compilers. Either that or your product was huge.

    A fast compiler like tcc can generate 10MB of binaries per second per
    core (even on my slow PC).

    Running it for one hour would create a 36GB binary.

    OK, your compilers will do more analysis and will optimise, but it still
    sounds slow. No wonder you have to use make!

    I take it there was no one whose job it was to overview the efficiency
    of the overall process. But presumably changing languages and tools or
    even just overhauling the software to remove cruft was out of the question.

    However, unless the end product was one monolithic binary, you'd be able
    to parallelise some processes - or was that already done and it still
    took hours?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to Keith Thompson on Sat Jan 13 15:07:16 2024
    Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
    bart <bc@freeuk.com> writes:
    On 13/01/2024 00:47, Keith Thompson wrote:
    bart <bc@freeuk.com> writes:

    It happens that once you have a working Makefile, it works equally
    well either to rebuild a project after a single change, or to build
    an entire project from scratch. Someone could probably create
    a simpler version of "make" that doesn't look at dependencies,
    and that always rebuilds everything. Such a tool would be worse
    than "make" for building projects during development, and not
    significantly better than "make" for building projects from scratch.
    And that's ignoring the "-j" option, which allows "make" to execute
    multiple steps in parallel. That works only because "make" knows
    about dependencies, and it can result in a full build from scratch
    finishing much more quickly. A simple script that just compiles
    each file isn't likely to do that. You can typically specify a
    maximum number of parallel jobs equal to the number of CPUs on your
    build system, e.g., `make -j $(nproc)`.

    That's a reasonable thing to do. But how does make do it? Can't a
    compiler apply the same approach if N files have been submitted?

    I've never looked into it.

    I suppose it could be done, but IMHO compilers are complex enough
    without adding logic to perform parallel compilations, especially since >"make" already solves that problem.

    The most common example of submitting N files for compilation
    in a single compile command would be java.

    Even then, it wasn't sufficient, so ant was developed.

    https://ant.apache.org/

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to Scott Lurndal on Sat Jan 13 16:02:54 2024
    scott@slp53.sl.home (Scott Lurndal) writes:
    Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
    bart <bc@freeuk.com> writes:
    On 13/01/2024 00:47, Keith Thompson wrote:
    bart <bc@freeuk.com> writes:

    It happens that once you have a working Makefile, it works equally
    well either to rebuild a project after a single change, or to build
    an entire project from scratch. Someone could probably create
    a simpler version of "make" that doesn't look at dependencies,
    and that always rebuilds everything. Such a tool would be worse
    than "make" for building projects during development, and not
    significantly better than "make" for building projects from scratch.
    And that's ignoring the "-j" option, which allows "make" to execute
    multiple steps in parallel. That works only because "make" knows
    about dependencies, and it can result in a full build from scratch
    finishing much more quickly. A simple script that just compiles
    each file isn't likely to do that. You can typically specify a
    maximum number of parallel jobs equal to the number of CPUs on your
    build system, e.g., `make -j $(nproc)`.

    That's a reasonable thing to do. But how does make do it? Can't a
    compiler apply the same approach if N files have been submitted?

    I've never looked into it.

    And it's rather irrelevent, as make handles projects with multiple
    languages as well as projects which generate compilable collateral
    during the build process.

    The project that I posted about earlier has python elements, C
    elements, C++ elements and some custom shell scripts to generate
    header files used by the C and C++ elements. All of this
    is sequenced (dependency graph) by the make utility and to
    the extent possible, uses all the hardware resources available
    with parallel job submission (subject to dependency constraints).

    And the jobs need not be submitted locally, they can be easily
    farmed out to hundreds of hosts in a grid environment.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From tTh@21:1/5 to bart on Sun Jan 14 00:02:57 2024
    On 1/13/24 23:39, bart wrote:

    It would be ironic that, if I was to write an application in C, it would
    be 100% C with no other language involved. Not even any compiler options
    (not with my compiler anyway).

    Is your application can be compiled on Linux, OpenBSD, Solaris,
    HP-UX, FreeBSD, AIX, MacOSX and a few others commonly used
    operating system ?

    --
    +---------------------------------------------------------------------+
    | https://tube.interhacker.space/a/tth/video-channels | +---------------------------------------------------------------------+

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Keith Thompson on Sat Jan 13 22:39:10 2024
    On 13/01/2024 21:42, Keith Thompson wrote:
    bart <bc@freeuk.com> writes:
    On 13/01/2024 04:17, Kaz Kylheku wrote:
    On 2024-01-13, bart <bc@freeuk.com> wrote:
    [...]
    That's a reasonable thing to do. But how does make do it? Can't a
    compiler apply the same approach if N files have been submitted?
    Yes. And in fact, languages with good module support like Modula-2
    don't need external make utilities.

    Finally somebody admitting that some languages may not need make as much.

    Bart, seriously, what the hell are you talking about?

    It's true that some languages don't need "make" as much as C does.

    Nobody here has said otherwise, likely because other languages are
    largely off-topic here in comp.lang.c.

    Except 'make'? I get the impression that most programs written in C have
    a large component written in 'make' too. A component you can't always
    ignore since essential build info is encoded in it.

    In the case of the GMP project that came up, a language purportedly
    written in C, it seemed to depend on auto-config, make, bash, m4, awk, gcc-options and god knows what else.

    It would be ironic that, if I was to write an application in C, it would
    be 100% C with no other language involved. Not even any compiler options
    (not with my compiler anyway).

    I think I'd be the only one here doing that!

    (I wonder: are people here so addicted to 'make' that they don't
    actually know how to describe a project without it?)


    By all means, don't use "make". Nobody wants you to use it.

    Lots of people are saying I'm not being forced to, for example:

    JP:
    It should have meanwhile become obvious that no one forces you to
    use Makefiles.

    JP:
    - No one said you should be using it.

    But who then strongly hint that I should be using it:

    JP:
    Why don't you just read about those two tools and learn

    JP:
    It's very interesting that despite your very small and restricted
    experience you'd decide to follow the "not invented here" principle
    instead of using long established, refined, and well accepted tools
    that are already available (and even for free), and reliably work.

    (Well, I'm not a sheep. And when I started out, I /had/ to invent stuff
    because it wasn't otherwise available. Doing so I made a discovery: it
    worked great! )

    ------------------------
    Here are some additional comments for everybody (KT can stop reading).

    This is my simpified view of software development, split into four broad activities:

    N = 1 | N > 1
    -------------------------------
    | | |
    Developer | (A) | (C) |
    | | |
    -------------------------------
    | | |
    Final | (B) | (D) |
    | | |
    -------------------------------

    N
    This is the number of program units that are involved. A unit
    is a single binary file (eg. EXE or DLL on Windows).

    Developer

    This is the work done by a developer on a daily basis, eg.
    editing, compiling and testing the same project 100 times a day.

    Final

    This relates to a one-time build of the project, usually of
    some working version. It may be done remotely by someone
    else, working from source code.

    Here is how /I/ usually deal with those different scenarios:

    (A) I use two tools: a compiler, and a simple IDE. They both work from
    a simple list of source and support files.

    (B) This is the same as (A) but stripped down: only a compiler is
    used. There may be a minimal, flat, set of files. Multiple source
    files are sometimes converted to one monolithic file for less
    hassle.

    (C) and (D) I'm not going to go into these. The requirements are
    going to be too diverse. Anyone who's a programmer will know how to
    organise this stuff.

    So, as I understand what everybody else as been saying about how they
    work, it seems to looks like this:

    (A) The primary tool is make.

    (B) The primary tool is make.

    (C) The primary tool is make.

    (D) The primary tool is make.

    (Nobody here has mentioned IDEs, but that is a personal choice that
    shouldn't be foisted on anyone else. Everyone is big on make though.)

    When I want to build someone else's open source project, I would prefer
    to get (B) or (D). I nearly always get (A) or (C). And with 'make',
    which on Windows, often doesn't work.

    So, make is being used not just as a substitute build system when
    creating ONE executable file. But it is also used to do everything else.
    I see that as a mistake.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Janis Papanagnou on Sun Jan 14 01:13:53 2024
    On 13/01/2024 05:54, Janis Papanagnou wrote:

    It's very interesting that despite your very small and restricted
    experience

    I don't agree. My experience has spanned mainframes, minicomputers and generations of microprocessors.

    I designed and built 8-bit and 16-bit machines and video boards from components. I've programmed them from nothing, even bootstrapping my
    first HLL starting from binary machine code, via hex and assembly
    through to a compiler.

    Throughout the 80s I wrote endless drivers, graphics libraries, GUI
    libs, FP emulation, maths libs, took care of fonts, invented image file formats, and started writing GUI applications and scripting languages.

    Not one line was written in any other language.

    I believe it was quite remarkable to make money from commercial projects
    using a complete 'full stack' toolset written 100% by me and in 100% my languages.

    you'd decide to follow the "not invented here" principle
    instead of using long established, refined, and well accepted tools
    that are already available (and even for free), and reliably work.

    It wasn't really a choice at first. C compilers for microprocessors c.
    1982 were hopelessly slow, and that's if it they could somehow be got
    into our machines since every 8-bit computer used a different disk format.

    They were also expensive, and I wasn't even a programmer so no one was
    going to pay for it.

    Besides, I'd looked in The C Programming Language, and thought it was
    dreadful. I perservered with my own language which compiled in a second
    or two rather than minutes.

    I've never looked back.

    It's no secret here that I don't like C, but I sometimes need to use
    some libraries which come as C source. But for those (the ones where I
    can crack the makefile encryption!), I have my private C compiler to
    generate binaries. Isn't that something?

    Sometimes you need to go against the flow. Some innovative products have
    come about from people deciding to do their own thing. Mine however are personal tools.

    I already know you're going to reply to this, if at all, with a big
    <SNIP> and a comment 'Nobody Cares'.

    Jealous any? I wouldn't swap places with you or your apps that can take
    hours to build. IMV somebody is doing something wrong.


    (This is my current 2024 set of language tools:

    https://github.com/sal55/langs/blob/master/CompilerSuite.md

    There is no 'make'. It is utterly pointless here.)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Keith Thompson on Sun Jan 14 00:36:28 2024
    On 13/01/2024 23:26, Keith Thompson wrote:
    bart <bc@freeuk.com> writes:
    On 13/01/2024 21:42, Keith Thompson wrote:
    bart <bc@freeuk.com> writes:
    On 13/01/2024 04:17, Kaz Kylheku wrote:
    On 2024-01-13, bart <bc@freeuk.com> wrote:
    [...]
    That's a reasonable thing to do. But how does make do it? Can't a
    compiler apply the same approach if N files have been submitted?
    Yes. And in fact, languages with good module support like Modula-2
    don't need external make utilities.

    Finally somebody admitting that some languages may not need make as much. >>> Bart, seriously, what the hell are you talking about?
    It's true that some languages don't need "make" as much as C does.
    Nobody here has said otherwise, likely because other languages are
    largely off-topic here in comp.lang.c.

    Except 'make'? I get the impression that most programs written in C
    have a large component written in 'make' too. A component you can't
    always ignore since essential build info is encoded in it.

    Most? I don't know. Many? Sure.

    You wrote, "Finally somebody admitting that some languages may not need
    make as much.". Has anyone here claimed otherwise? If not, why do you
    find Kaz's statement so remarkable?

    People have suggested using make for everything, from hello.c up to JP's
    and SL's massive applications.

    They have suggested using it for any language and even stuff which is
    program code.

    I've certainly seen makefiles associated with most open source apps I've
    looked up.

    So, yes, it was refreshing that someone admitted that a language didn't
    need it. (I've tried saying so about mine, but people either don't
    believe me, ignore it, or dismiss it.)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Kaz Kylheku on Sun Jan 14 12:18:14 2024
    On 12/01/2024 21:31, Kaz Kylheku wrote:
    On 2024-01-12, bart <bc@freeuk.com> wrote:

    I mainly remember the times when they do hang.

    DOS/Windows stuff hangs also:

    C:\Users\kazk>findstr foo
    ... "hang" ...

    Even Microsoft clued in to the idea that a text filter shouldn't
    spew extraneous diagnostics by default.

    If you type 'findstr' with no arguments, it reports an error. Maybe
    'sort' was a better example to make your point.

    But with 'as', it just sits there. I wonder what it's waiting for; for
    me to type in ASM code live from the terminal? (If 'as' is designed for
    piped-in input, tdm/gcc doesn't appear to use that feature as I remember
    it generating discrete, temporary .s files.)

    gcc -pipe works in pipe mode.

    The "as" command is intended for compiler use; not only is it not
    an interactive assembler, it doesn't even have particularly good
    diagnostics for batch use. You have to know what you're doing.

    You're sort of making excuses for it. My 'aa' assembler was also
    designed mainly for machine-generated code, so it has very few frills.

    The syntax however is decent enough that I can use it for my inline
    assembler too.

    The way it works is also conventional: it takes .asm files as input and produces a file as output. It doesn't call every output 'a.out'!

    But that CLI is part of the lead module.

    By leaving that out, the remaining modules can statically compiled
    (embedded) into another application. That can invoke the assembler via
    this function:

    export func assembler(
    ichar outputfile,
    ref[]ichar asmfiles, dllfiles,
    int nasmfiles, ndllfiles, fobj, fdll, fcaption,
    ref[]ichar assemsources = nil,
    ichar entrypointname)int =

    which takes 10 arguments. I'd sort of wondered how some of that info is imparted to 'as' when the only input is a single string.

    (This manages N asm inputs and L libraries. Sources can come from files,
    or from in-memory strings, to avoid having to write out ASM files.
    Output will be a single EXE, DLL or OBJ file.)

    aa.exe is about 0.1MB; as.exe is 1.5MB.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to tTh on Sun Jan 14 14:33:04 2024
    On 13/01/2024 23:02, tTh wrote:
    On 1/13/24 23:39, bart wrote:

    It would be ironic that, if I was to write an application in C, it
    would be 100% C with no other language involved. Not even any compiler
    options (not with my compiler anyway).

       Is your application can be compiled on Linux, OpenBSD, Solaris,
       HP-UX, FreeBSD, AIX, MacOSX and a few others commonly used
       operating system ?

    I'm not suggesting that it will be 100% portable, just that it will
    involve no tools other than a C compiler.

    But if it is C code, and is either a pure library, or only does console
    or file i/o, then why not?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to bart on Sun Jan 14 16:20:21 2024
    bart <bc@freeuk.com> writes:
    On 13/01/2024 23:26, Keith Thompson wrote:
    bart <bc@freeuk.com> writes:
    On 13/01/2024 21:42, Keith Thompson wrote:
    bart <bc@freeuk.com> writes:
    On 13/01/2024 04:17, Kaz Kylheku wrote:
    On 2024-01-13, bart <bc@freeuk.com> wrote:
    [...]
    That's a reasonable thing to do. But how does make do it? Can't a >>>>>>> compiler apply the same approach if N files have been submitted?
    Yes. And in fact, languages with good module support like Modula-2 >>>>>> don't need external make utilities.

    Finally somebody admitting that some languages may not need make as much. >>>> Bart, seriously, what the hell are you talking about?
    It's true that some languages don't need "make" as much as C does.
    Nobody here has said otherwise, likely because other languages are
    largely off-topic here in comp.lang.c.

    Except 'make'? I get the impression that most programs written in C
    have a large component written in 'make' too. A component you can't
    always ignore since essential build info is encoded in it.

    Most? I don't know. Many? Sure.

    You wrote, "Finally somebody admitting that some languages may not need
    make as much.". Has anyone here claimed otherwise? If not, why do you
    find Kaz's statement so remarkable?

    People have suggested using make for everything, from hello.c up to JP's
    and SL's massive applications.

    You are again ascribing words to people. People have pointed out
    to you that make can be used for a number of purposes.

    They've not suggested that you -use- make yourself.


    They have suggested using it for any language and even stuff which is
    program code.

    No, they've _stated_ that it can be used for any language or stuff
    which is _not_ program code. That's just a plain and simple fact.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Tim Rentsch@21:1/5 to bart on Sun Jan 14 09:20:33 2024
    bart <bc@freeuk.com> writes:

    [...]

    While 'make' conflates several kinds of processes: [...]

    make doesn't conflate these different applications, any
    more than 'cat' "conflates" different kinds of files.
    They simply are general tools with a broad range of
    applicability. The idea that the applications listed
    are in some way inherently different shows only the
    limitations of your thinking, not any essential truth
    about make.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Tim Rentsch@21:1/5 to bart on Sun Jan 14 09:22:15 2024
    bart <bc@freeuk.com> writes:

    On 12/01/2024 19:15, Scott Lurndal wrote:
    [...]
    What the FM documents. RTFM.

    I see. So forget just having intuitive behaviour. [...]

    The problem is not what the behavior is. The problem is
    with your intuition about what the behavior should be.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Tim Rentsch@21:1/5 to Keith Thompson on Sun Jan 14 09:26:10 2024
    Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:

    Bart <bc@freeuk.cm> writes:
    [...]

    I've told you (multiple times, for *years*) how to invoke gcc in
    ISO C conforming mode *if that's what you want*.

    By my reckoning, for more than five years. Do you think it might
    be time to give up on the idea that he will ever hear you?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Tim Rentsch@21:1/5 to Keith Thompson on Sun Jan 14 09:54:57 2024
    Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:

    bart <bc@freeuk.com> writes:
    [...]

    You repeatedly react strongly to things nobody said. You invent
    strawman arguments.

    That's what bart does. He continually misrepresents other
    people's statements, to make them look stupid, so he can feel
    superior. Only insecure people feel a need to perpetually brag
    and to constantly run down everyone else's point of view. Given
    that he's been doing this for the better part of a decade it's
    unlikely he is going to change any time soon.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Tim Rentsch on Sun Jan 14 18:10:45 2024
    On 14/01/2024 17:22, Tim Rentsch wrote:
    bart <bc@freeuk.com> writes:

    On 12/01/2024 19:15, Scott Lurndal wrote:
    [...]
    What the FM documents. RTFM.

    I see. So forget just having intuitive behaviour. [...]

    The problem is not what the behavior is. The problem is
    with your intuition about what the behavior should be.

    I would love to know what behaviour of an assembler is intuitive to /you/.

    Or anybody.

    I would be surprised if that involved the brain-dead behaviour of either
    naming every output 'a.out', so overwriting the file created 5 seconds previously, or spewing reams of binary code to a text terminal sensitive
    to escape codes.

    I don't know how many assemblers you're written; I've done four or five standalone ones. All took files as input, and wrote correspondingly
    named files as output.

    (Except my first one, but that machine didn't a file system, so it can
    be excused.)

    The behaviour of 'as' is quite extraordinary. But it is typical of the
    regulars here to gang up on somebody who points out the bleeding
    obvious, and pretend that /they/ are mistaken, and that 'as' works
    perfectly.

    (Maybe you should apply to work for The Post Office and Fujitsu in the UK!)

    How about accepting some constructive criticism for a change, and
    ADMITTING that the behaviour is rubbish, but it has to be accepted
    because the way it works is hard-coded into too many tools to change it.

    Having a product called 'as2' is totally out of the question of course!

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Anthony Cuozzo@21:1/5 to bart on Sun Jan 14 13:44:27 2024
    On 1/14/24 13:17, bart wrote:
    On 14/01/2024 17:54, Tim Rentsch wrote:
    Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:

    bart <bc@freeuk.com> writes:
    [...]

    You repeatedly react strongly to things nobody said.  You invent
    strawman arguments.

    That's what bart does.  He continually misrepresents other
    people's statements, to make them look stupid, so he can feel
    superior.  Only insecure people feel a need to perpetually brag
    and to constantly run down everyone else's point of view.

    Yes, sorry. I forgot that was your job.



    I'm new here, so please forgive my ignorance. What's yours?

    I suppose what I'm asking is: What exactly is your goal here, Bart?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Tim Rentsch on Sun Jan 14 18:17:17 2024
    On 14/01/2024 17:54, Tim Rentsch wrote:
    Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:

    bart <bc@freeuk.com> writes:
    [...]

    You repeatedly react strongly to things nobody said. You invent
    strawman arguments.

    That's what bart does. He continually misrepresents other
    people's statements, to make them look stupid, so he can feel
    superior. Only insecure people feel a need to perpetually brag
    and to constantly run down everyone else's point of view.

    Yes, sorry. I forgot that was your job.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Jim Jackson@21:1/5 to Anthony Cuozzo on Sun Jan 14 19:16:45 2024
    On 2024-01-14, Anthony Cuozzo <anthony@cuozzo.us> wrote:
    On 1/14/24 13:17, bart wrote:
    On 14/01/2024 17:54, Tim Rentsch wrote:
    Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:

    bart <bc@freeuk.com> writes:
    [...]

    You repeatedly react strongly to things nobody said.?? You invent
    strawman arguments.

    That's what bart does.?? He continually misrepresents other
    people's statements, to make them look stupid, so he can feel
    superior.?? Only insecure people feel a need to perpetually brag
    and to constantly run down everyone else's point of view.

    Yes, sorry. I forgot that was your job.



    I'm new here, so please forgive my ignorance. What's yours?

    I suppose what I'm asking is: What exactly is your goal here, Bart?

    Good question. This thread has gone on. and on, and on, and ....
    Most sane people would have got bored and found something better to do.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Anthony Cuozzo on Sun Jan 14 19:57:52 2024
    On 14/01/2024 18:44, Anthony Cuozzo wrote:
    On 1/14/24 13:17, bart wrote:
    On 14/01/2024 17:54, Tim Rentsch wrote:
    Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:

    bart <bc@freeuk.com> writes:
    [...]

    You repeatedly react strongly to things nobody said.  You invent
    strawman arguments.

    That's what bart does.  He continually misrepresents other
    people's statements, to make them look stupid, so he can feel
    superior.  Only insecure people feel a need to perpetually brag
    and to constantly run down everyone else's point of view.

    Yes, sorry. I forgot that was your job.



    I'm new here, so please forgive my ignorance. What's yours?

    I suppose what I'm asking is: What exactly is your goal here, Bart?

    What is anyone's goal here?

    This newsgroup is just a bunch of old-timers who mainly discuss the
    finer points of the C standard, although the group has been more or less
    dead for the past couple of years.

    Since there are rarely any people who post about any practical problems,
    I think they mainly use stackoverflow and reddit for that. The regulars
    mainly just argue amongst themselves.

    I'm somewhat of an outsider and I like to point out things that I
    believe are wrong in things like the C language and the assorted
    collection of Unix-specific tools that apparently go with it.

    I'm an outsider because I don't routinely use C, or any of the tools,
    and because I don't have background in Unix-based development. I have a different perspective.

    So, what happens here is that I mention something that might be some
    bizarre quirk of C, or some weird, unfriendly way some tool works, or
    anything that goes against common-sense or intuition, or that I find has
    caused me grief.

    And then the regulars, instead of agreeing, go on the defensive. Some go
    on the attack, saying I'm the one at fault, I should RTFM, or do this or
    that, or that I'm ignorant, etc etc. (You've seen the thread.)

    So since I have nothing better to do**, I like to defend myself. And
    sometimes it is fascinating seeing people defend the indefendable.

    All people need to do is be honest.

    Does that answer your question?

    (** That's not quite true, this is taking me away from my current
    project. But when people openly insult me, I can't let it go. They need
    to stop replying.)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to bart on Mon Jan 15 00:34:31 2024
    On 2024-01-14, bart <bc@freeuk.com> wrote:
    On 12/01/2024 21:31, Kaz Kylheku wrote:
    On 2024-01-12, bart <bc@freeuk.com> wrote:

    I mainly remember the times when they do hang.

    DOS/Windows stuff hangs also:

    C:\Users\kazk>findstr foo
    ... "hang" ...

    Even Microsoft clued in to the idea that a text filter shouldn't
    spew extraneous diagnostics by default.

    If you type 'findstr' with no arguments, it reports an error. Maybe
    'sort' was a better example to make your point.

    So does grep with no arguments? I don't see where that is going.

    But with 'as', it just sits there. I wonder what it's waiting for; for
    me to type in ASM code live from the terminal? (If 'as' is designed for
    piped-in input, tdm/gcc doesn't appear to use that feature as I remember >>> it generating discrete, temporary .s files.)

    gcc -pipe works in pipe mode.

    The "as" command is intended for compiler use; not only is it not
    an interactive assembler, it doesn't even have particularly good
    diagnostics for batch use. You have to know what you're doing.

    You're sort of making excuses for it.

    I'm just stating what I have always believed the requirements to be.

    In Unixes and the GNU Project, there has not been a focus on assembly
    language as a primary development language, with a great developer
    experience.

    That's pretty much a fact.

    The amount of material written in .s or .S files is very small.

    My 'aa' assembler was also
    designed mainly for machine-generated code, so it has very few frills.

    The syntax however is decent enough that I can use it for my inline
    assembler too.

    GCC has great inline assembly.

    You can reference C expressions, which
    are evaluated to registers that the register allocator chooses, which
    you can reference in your inline code in a symbolic way.

    You can also indicate that your code template has certain effects,
    so that the compiler is informed.

    I had a nice experience many years ago when I wrapped the MIPS "load-linked/store-conditional" primitives as separate inline code
    macros.

    When I used these macros to write a synchronization primitive, GCC
    optimized away an unnecessary instruction or two, due to the way they integrated together.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to bart on Mon Jan 15 00:52:48 2024
    On 2024-01-14, bart <bc@freeuk.com> wrote:
    How about accepting some constructive criticism for a change, and
    ADMITTING that the behaviour is rubbish, but it has to be accepted
    because the way it works is hard-coded into too many tools to change it.

    I think a lot of aspects of "as" could easily be changed without
    breaking anything.

    But nobody is screaming for "as" to provide a great developer experience
    to someone writing large amounts of code in assembly language; it is not
    a pain point.

    The GNU implementation of the OS/360 JCL (job control language) is
    even worse than GNU As: it is so bad, that it doesn't actually exist.

    That's not going to change, until that lack causes hurt to the GNU
    project.

    The times when I have used assembly language in the context of gcc,
    I didn't run "as", because gcc recognizes .s and .S files.

    It exhibits the usual conventions: with -c, the .s file goes to .o,
    otherwise it's a complete program that goes to a.out.

    Typically, I don't have to do anything in a Makefile to add an assembly language source file. Just list the object file in the OBJS variable.

    E.g. having created a crc32-x86.S I would add it to the OBJS :=
    line in the Makefile:

    OBJS := ... crc32-x86.o

    and that's it. Make will deduce that there is a crc32-x86.S from
    which that can be built, and then the $(OBJS) are what the program
    is built from; the .o is pulled into it.

    Empty directory:

    $ ls -l
    total 0

    Empty assembler file:

    $ touch foo.S

    Make .o with no instructions in it:

    $ make foo.o
    cc -c -o foo.o foo.S
    $ ls -l
    total 4
    -rw-rw-r-- 1 kaz kaz 444 Jan 14 16:48 foo.o
    -rw-rw-r-- 1 kaz kaz 0 Jan 14 16:48 foo.S
    $ file foo.o
    foo.o: ELF 32-bit LSB relocatable, Intel 80386, version 1 (SYSV), not stripped

    This is just familiar commands I've been using for over 30 years; nothing weird. Just like you, I don't have to learn anything time-wasting and unproductive arcania.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Keith Thompson on Mon Jan 15 01:05:09 2024
    On 2024-01-14, Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:
    bart <bc@freeuk.com> writes:
    On 14/01/2024 17:22, Tim Rentsch wrote:
    bart <bc@freeuk.com> writes:
    On 12/01/2024 19:15, Scott Lurndal wrote:
    [...]
    What the FM documents. RTFM.

    I see. So forget just having intuitive behaviour. [...]
    The problem is not what the behavior is. The problem is
    with your intuition about what the behavior should be.

    I would love to know what behaviour of an assembler is intuitive to /you/. >>
    Or anybody.

    I would be surprised if that involved the brain-dead behaviour of
    either naming every output 'a.out', so overwriting the file created 5
    seconds previously, or spewing reams of binary code to a text terminal
    sensitive to escape codes.

    I already mentioned that GNU as doesn't write machine code to the
    terminal. (I discussed problems that could occor *if it did*.) Did you
    miss that? I know you're seeing at least *some* of my posts.

    Yes, it writes its output to "a.out" by default, for historical reasons.
    It also has an option to specify the name of the output file -- an
    option that is almost always used in practice. Invoking the "as"
    command directly is relatively rare.

    Invoking the as command directly is not just rare, it's a poor
    which invites causing problems for someone who tries to cross-compile
    your program.

    Your compiler knows where the assembler is; it was configured to know
    that.

    If your compiler is /path/to/arm-toolchain/bin/arm-linux-gnu-gcc,
    it's very unlikely that "as" is the right thing for calling the
    assembler.

    It might quite likely be /path/to/arm-toolchain/bin/arm-linux-gnu-as
    (i.e. use the same $(CROSS) prefix), but that is an educated guess.
    The assembler comes from binutils and might be at a different path,
    even if the basename is right.

    The best thing is to just invoke the compiler front end on the .s file.
    (If it is .S, you have to for the reason that it has to be preprocessed.
    I make assembly files .S because soone or later, someone will need preprocessing, and git has shit support for renaming.)

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Kaz Kylheku on Mon Jan 15 02:14:10 2024
    On 15/01/2024 00:34, Kaz Kylheku wrote:
    On 2024-01-14, bart <bc@freeuk.com> wrote:
    On 12/01/2024 21:31, Kaz Kylheku wrote:
    On 2024-01-12, bart <bc@freeuk.com> wrote:

    I mainly remember the times when they do hang.

    DOS/Windows stuff hangs also:

    C:\Users\kazk>findstr foo
    ... "hang" ...

    Even Microsoft clued in to the idea that a text filter shouldn't
    spew extraneous diagnostics by default.

    If you type 'findstr' with no arguments, it reports an error. Maybe
    'sort' was a better example to make your point.

    So does grep with no arguments? I don't see where that is going.

    This is about typing some command and it is apparently doing nothing, no indication whether it's busy, has crashed, is stuck in a loop, or is
    waiting for YOU to do something.

    I didn't know grep was on Windows, but if I type 'grep' with no params,
    it gives a usage message, so no complaints there.


    In Unixes and the GNU Project, there has not been a focus on assembly language as a primary development language, with a great developer experience.

    That's pretty much a fact.

    That is extraordinary. Wasn't C first implemented in assembly? It's
    always been a mainstay of computing as far as I can remember. Except no
    one now write whole apps in assembly. (I've done quite a few in the past.)

    Plenty though implement compilers that generate ASM source /in a file/
    and they expect to feed that to an assembler. Most generate object files
    from that.


    The amount of material written in .s or .S files is very small.

    My 'aa' assembler was also
    designed mainly for machine-generated code, so it has very few frills.

    The syntax however is decent enough that I can use it for my inline
    assembler too.

    GCC has great inline assembly.

    You can reference C expressions, which
    are evaluated to registers that the register allocator chooses, which
    you can reference in your inline code in a symbolic way.

    GCC inline assembly looks absolutely diabolic. I take it you've never
    seen it done properly?

    Actually I spent 5-10 minutes looking for examples, to try and figure
    out if asm instructions could in fact directly refer to symbols in the HLL.

    But most examples were one or two lines of weird syntax, following by
    some interfacing code. So I don't know.

    If /I/ had to write extensive programs in gcc inline assembly, then put
    a gun to my head now!

    Take this example in C:

    int a;

    void F(void) {
    int b=2, c=3;
    static int d=4;

    a = b + c * d;
    }

    I will now show it in my language but with that assignment replaced by
    inline assembly:

    int a

    proc F=
    int b:=2, c:=3
    static int d=4

    assem
    mov rax, [c] # (note my ints are 64 bits)
    imul2 rax, [d]
    add rax, [b]
    mov [a], rax
    end
    end

    My question is: what would the C version look like with that line in gcc
    inline assembly? (In both cases, 'a' should end up with the value 14.)

    You need to tell me, because I will otherwise not have a clue. From what
    I've seen of gcc inline asm:

    * Code has to be written within string literals, in dreadfil AT&T
    syntax. And apparently even with embedded \n line breaks. (Good
    grief - I think early 80s BASICs had more sophisticated facilities!)

    * You mostly use offsets to get at local variables

    * You apparently aren't allowed to use just any registers as you need
    to negotiate with gcc so as not to interfere with /its/ use of
    registers. So most examples I saw seemed to deal with this.

    I consider that when writing assembly, YOU are in charge not the
    compiler. As you can see from mine:

    * It is written just as it would be in an actual ASM file

    * You can refer to variables directly (the compiler will add what is
    needed to access locals or statics)

    If a function uses inline ASM, variables are kept in memory not
    registers. (I might allow that at some point.) Most such functions
    however contain only ASM.

    That still lets ASM use the facilities of the HLL such as functions, declarations, named constants, scopes etc.

    I suppose you're going to suggest that gcc's facilities are superior...

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to bart on Mon Jan 15 07:07:28 2024
    On 2024-01-15, bart <bc@freeuk.com> wrote:
    On 15/01/2024 00:34, Kaz Kylheku wrote:
    In Unixes and the GNU Project, there has not been a focus on assembly
    language as a primary development language, with a great developer
    experience.

    That's pretty much a fact.

    That is extraordinary. Wasn't C first implemented in assembly? It's

    No; C would have been implemented in NB (new B). It was B that was
    implemented in assembly. That's just bootstrapping, though.

    Thompson and Ritchie didn't have a nice assembler; IIRC, they started
    out by assembling code using macros in the TECO editor.

    Assembly language has never been emphasized in Unix, to my best
    knowledge. It's there.

    always been a mainstay of computing as far as I can remember. Except no
    one now write whole apps in assembly. (I've done quite a few in the past.)

    I did a bunch of assembly language programming, which was with
    "nice" assemblers. At university, I made a linked list library with
    numerous functions on a Sun 3 (68K) using Sun's "as". That was my
    first encounter with Unix's idea of assembly. I got it done, but it
    was pretty horrible, with next to no diagnostics when there was
    something wrong. It was obvious that the tool assumes correct input,
    coming from a compiler.

    My 'aa' assembler was also
    designed mainly for machine-generated code, so it has very few frills.

    The syntax however is decent enough that I can use it for my inline
    assembler too.

    GCC has great inline assembly.

    You can reference C expressions, which
    are evaluated to registers that the register allocator chooses, which
    you can reference in your inline code in a symbolic way.

    GCC inline assembly looks absolutely diabolic. I take it you've never
    seen it done properly?

    Actually I spent 5-10 minutes looking for examples, to try and figure
    out if asm instructions could in fact directly refer to symbols in the HLL.

    But most examples were one or two lines of weird syntax, following by
    some interfacing code. So I don't know.

    If /I/ had to write extensive programs in gcc inline assembly, then put
    a gun to my head now!

    Take this example in C:

    int a;

    void F(void) {
    int b=2, c=3;
    static int d=4;

    a = b + c * d;
    }

    I will now show it in my language but with that assignment replaced by
    inline assembly:

    int a

    proc F=
    int b:=2, c:=3
    static int d=4

    assem
    mov rax, [c] # (note my ints are 64 bits)
    imul2 rax, [d]
    add rax, [b]
    mov [a], rax
    end

    Problem is that the compiler's register allocator now has to be informed that the assembly language part is using rax and work around it.

    end

    My question is: what would the C version look like with that line in gcc inline assembly? (In both cases, 'a' should end up with the value 14.)

    Let's make it more interesting: what if b and c come from arguments,
    and the static int d actually has state that changes between
    invocations, so it can't be optimized away. Let's return the
    result, a:

    int F(int b, int c)
    {
    static int d=4;
    int a;

    d++;

    asm("imul %3, %2\n\t"
    "add %2, %1\n\t"
    "mov %1, %0\n\t"
    : "=r" (a)
    : "r" (b), "r" (c), "r" (d));

    return a;
    }

    It's pretty arcane in that the material is both in string literals,
    and not. The assembly language template is textual; the compiler knows
    nothing about its interior.

    I specified one output operand, and three input operands, requesting
    that they be in registers. I don't specify the register identities.
    They are referenced by number: %0, %1, %2, %3 in the order they
    appear. (A way to use named references exists.)

    We don't have to code the data transfers between the registers
    and the C operands they are connected to; that is done for us
    by the compiler.

    So, okay, unoptimized that looks like:

    $ gcc -c inline.c
    $ objdump -d inline.o

    inline.o: file format elf64-x86-64


    Disassembly of section .text:

    0000000000000000 <F>:
    0: 55 push %rbp
    1: 48 89 e5 mov %rsp,%rbp
    4: 89 7d ec mov %edi,-0x14(%rbp)
    7: 89 75 e8 mov %esi,-0x18(%rbp)
    a: 8b 05 00 00 00 00 mov 0x0(%rip),%eax # 10 <F+0x10>
    10: 83 c0 01 add $0x1,%eax
    13: 89 05 00 00 00 00 mov %eax,0x0(%rip) # 19 <F+0x19>
    19: 8b 0d 00 00 00 00 mov 0x0(%rip),%ecx # 1f <F+0x1f>
    1f: 8b 45 ec mov -0x14(%rbp),%eax
    22: 8b 55 e8 mov -0x18(%rbp),%edx
    25: 0f af d1 imul %ecx,%edx
    28: 01 d0 add %edx,%eax
    2a: 89 c0 mov %eax,%eax
    2c: 89 45 fc mov %eax,-0x4(%rbp)
    2f: 8b 45 fc mov -0x4(%rbp),%eax
    32: c9 leaveq
    33: c3 retq


    Optimized:

    $ gcc -O2 -c inline.c
    $ objdump -d inline.o

    inline.o: file format elf64-x86-64


    Disassembly of section .text:

    0000000000000000 <F>:
    0: 8b 05 00 00 00 00 mov 0x0(%rip),%eax # 6 <F+0x6>
    6: 83 c0 01 add $0x1,%eax
    9: 89 05 00 00 00 00 mov %eax,0x0(%rip) # f <F+0xf>
    f: 0f af f0 imul %eax,%esi
    12: 01 f7 add %esi,%edi
    14: 89 f8 mov %edi,%eax
    16: c3 retq


    The static variable is accessed relative to the instruction pointer.
    The offset is all zeros: that will be patched when this is linked.

    Note that between the unoptimized and optimized code, the register
    identities changed entirely.

    GCC's inline assembly feature is largely agnostic of the assembler
    back end. It interfaces with register allocation and such, but is
    otherwise generic. This allows it to have exactly the same grammar,
    no matter the architecture target.

    The syntax isn't particularly nice, but it has power.

    You need to tell me, because I will otherwise not have a clue. From what
    I've seen of gcc inline asm:

    * Code has to be written within string literals, in dreadfil AT&T
    syntax. And apparently even with embedded \n line breaks. (Good
    grief - I think early 80s BASICs had more sophisticated facilities!)

    The code template, after the registers like %0 and %1 are substituted
    into it, is just shoved into the assembly language output verbatim.

    The compiler doesn't analyze the interior.

    AT&T syntax is used if that's what the assembler requires.

    Not all GCC targets have assemblers in whose language the destination operand is on the right; it's that way for the x86 family though.

    * You mostly use offsets to get at local variables

    Nope; it's pretty much transparent.

    * You apparently aren't allowed to use just any registers as you need
    to negotiate with gcc so as not to interfere with /its/ use of
    registers. So most examples I saw seemed to deal with this.

    This is pretty awesome.

    I consider that when writing assembly, YOU are in charge not the
    compiler.

    If you're writing *inline* assembly in compiled code, if you let
    the compiler be in charge of some things, it's a lot better.

    If specific registers are required, that can be arranged. Sometimes that happens: some instruction sets have certain instructions that only work with certain registers, as you know.

    If you're not working with instructions like that, it's beneficial if
    the compiler allocates them for you. That nicely integrates into the
    register allocation.

    As you can see from mine:

    * It is written just as it would be in an actual ASM file

    It is nice for editing and all, and you were cranking out hundreds
    of lines of assembly, or even dozens, you'd want that.

    But mainly the code exists to do a job, not to be admired.

    * You can refer to variables directly (the compiler will add what is
    needed to access locals or statics)

    If a function uses inline ASM, variables are kept in memory not
    registers. (I might allow that at some point.) Most such functions
    however contain only ASM.

    As you can see, this is not a limitation in the GNU inline assembly.
    The optimized code did away with memory references, except for the
    static int d.

    GNU inline assembly is ugly, but it's very well designed semantically;
    it hits the target.

    When you have to do something in assembly language, it is fluid;
    you don't have to contend with an overly ridid instruction template that interferes with surrounding optimization.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Kaz Kylheku on Mon Jan 15 07:40:20 2024
    On 2024-01-15, Kaz Kylheku <433-929-6894@kylheku.com> wrote:
    On 2024-01-15, bart <bc@freeuk.com> wrote:
    On 15/01/2024 00:34, Kaz Kylheku wrote:
    In Unixes and the GNU Project, there has not been a focus on assembly
    language as a primary development language, with a great developer
    experience.

    That's pretty much a fact.

    That is extraordinary. Wasn't C first implemented in assembly? It's

    No; C would have been implemented in NB (new B). It was B that was implemented in assembly. That's just bootstrapping, though.

    Thompson and Ritchie didn't have a nice assembler; IIRC, they started
    out by assembling code using macros in the TECO editor.

    Assembly language has never been emphasized in Unix, to my best
    knowledge. It's there.

    always been a mainstay of computing as far as I can remember. Except no
    one now write whole apps in assembly. (I've done quite a few in the past.)

    I did a bunch of assembly language programming, which was with
    "nice" assemblers. At university, I made a linked list library with
    numerous functions on a Sun 3 (68K) using Sun's "as". That was my
    first encounter with Unix's idea of assembly. I got it done, but it
    was pretty horrible, with next to no diagnostics when there was
    something wrong. It was obvious that the tool assumes correct input,
    coming from a compiler.

    My 'aa' assembler was also
    designed mainly for machine-generated code, so it has very few frills. >>>>
    The syntax however is decent enough that I can use it for my inline
    assembler too.

    GCC has great inline assembly.

    You can reference C expressions, which
    are evaluated to registers that the register allocator chooses, which
    you can reference in your inline code in a symbolic way.

    GCC inline assembly looks absolutely diabolic. I take it you've never
    seen it done properly?

    Actually I spent 5-10 minutes looking for examples, to try and figure
    out if asm instructions could in fact directly refer to symbols in the HLL. >>
    But most examples were one or two lines of weird syntax, following by
    some interfacing code. So I don't know.

    If /I/ had to write extensive programs in gcc inline assembly, then put
    a gun to my head now!

    Take this example in C:

    int a;

    void F(void) {
    int b=2, c=3;
    static int d=4;

    a = b + c * d;
    }

    I will now show it in my language but with that assignment replaced by
    inline assembly:

    int a

    proc F=
    int b:=2, c:=3
    static int d=4

    assem
    mov rax, [c] # (note my ints are 64 bits)
    imul2 rax, [d]
    add rax, [b]
    mov [a], rax
    end

    Problem is that the compiler's register allocator now has to be informed that the assembly language part is using rax and work around it.

    end

    My question is: what would the C version look like with that line in gcc
    inline assembly? (In both cases, 'a' should end up with the value 14.)

    Let's make it more interesting: what if b and c come from arguments,
    and the static int d actually has state that changes between
    invocations, so it can't be optimized away. Let's return the
    result, a:

    int F(int b, int c)
    {
    static int d=4;
    int a;

    d++;

    asm("imul %3, %2\n\t"
    "add %2, %1\n\t"
    "mov %1, %0\n\t"
    : "=r" (a)
    : "r" (b), "r" (c), "r" (d));

    return a;
    }

    We can also turn this multiply and add into a stand-alone primitive
    that we can put behind a macro:

    #define mul_add(x, y, z) \
    ({ int _res; \
    asm("imul %3, %2\n\t" \
    "add %2, %1\n\t" \
    "mov %1, %0\n\t" \
    : "=r" (_res) \
    : "r" (x), "r" (y), "r" (z)); \
    _res; })

    Which we then freely use like this:

    int F(int b, int c)
    {
    static int d=4;
    int a;

    d++;

    a = mul_add(b, c, d);

    return a;
    }


    Complex example:

    int G(int a, int b, int c, int d, int e, int f, int g, int h, int i)
    {
    return mul_add(mul_add(a, b, c),
    mul_add(d, mul_add(e, f, g), h),
    i);
    }

    gcc -O2 code:

    0000000000000020 <G>:
    20: 0f af f2 imul %edx,%esi
    23: 01 f7 add %esi,%edi
    25: 89 ff mov %edi,%edi
    27: 8b 44 24 08 mov 0x8(%rsp),%eax
    2b: 8b 54 24 10 mov 0x10(%rsp),%edx
    2f: 44 0f af c8 imul %eax,%r9d
    33: 45 01 c8 add %r9d,%r8d
    36: 44 89 c0 mov %r8d,%eax
    39: 0f af c2 imul %edx,%eax
    3c: 01 c1 add %eax,%ecx
    3e: 89 c9 mov %ecx,%ecx
    40: 8b 44 24 18 mov 0x18(%rsp),%eax
    44: 0f af c8 imul %eax,%ecx
    47: 01 cf add %ecx,%edi
    49: 89 f8 mov %edi,%eax
    4b: c3 retq

    GCC inline assembly is good if you have certain instructions that the compiler doesn't use, and you'd like to use them as first class primitives (meaning that they are not disadvantaged compared to primitives the compiler knows about).

    We would likely obtain better code if if we unbundled the multiplication
    and addition by writing separate mul and add primitives.

    Because the code above has to follow the rigid template where imul
    is immediately followed by the related add, after which there is
    a mandatory mov.

    We really want:

    #define mul_add(x, y, z) add(x, mul(y, z))

    where we separately write the add and mul as inline assembly fragments.

    A mul_add primitive might make sense if the processor had such a thing
    in one instruction.

    With GCC inline assembly, you want to only put the essentials into it:
    only do what is necessary and only bundle together what must be
    bundled.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to bart on Mon Jan 15 12:57:15 2024
    On 12/01/2024 22:01, bart wrote:
    On 12/01/2024 18:02, Janis Papanagnou wrote:
    On 12.01.2024 18:09, bart wrote:
    On 12/01/2024 16:34, David Brown wrote:

    It looks like 'make' is competing with 'bash' then!

    Why don't you just read about those two tools and learn, instead
    of repeatedly spouting such stupid statements of ignorance.

    Because I've repeatedly said I don't need them. Why can't you accept that?


    You've told us. We believe you. You live in a small, limited little
    world where you can get by with the tools you make yourself and don't
    have to interact with other developers or other software. By
    definition, your tools do all you need - if you want a new feature, you
    add it to the tool. And they don't do more than you personally need.

    If you are happy like that, great. (You are apparently not particularly
    happy about all of this, based on your posts, which is a shame.)

    But /please/ stop complaining when other people do things differently.
    /You/ are the odd one out here. You alone. Your methods might be
    better for /you/, for your own very limited and specific needs - they
    are not suitable for the rest of the world.

    No one is forcing you to use make, or C, or Linux, or anything else that triggers you. If you /want/ to use these things, and want to ask for
    help or advice, that's fine - as long as you do so in good faith and try
    to learn from the answers. But all you seem to want to do is fight and
    argue, making a fool of yourself in your wilful ignorance. That's not particularly good for anyone.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to bart on Mon Jan 15 13:10:13 2024
    On 13/01/2024 23:39, bart wrote:
    On 13/01/2024 21:42, Keith Thompson wrote:
    bart <bc@freeuk.com> writes:
    On 13/01/2024 04:17, Kaz Kylheku wrote:
    On 2024-01-13, bart <bc@freeuk.com> wrote:
    [...]
    It's true that some languages don't need "make" as much as C does.

    Nobody here has said otherwise, likely because other languages are
    largely off-topic here in comp.lang.c.

    Except 'make'? I get the impression that most programs written in C have
    a large component written in 'make' too. A component you can't always
    ignore since essential build info is encoded in it.

    Can you remember a single thread in this newsgroup, over the last decade
    or so, that was about "make" and was not dominated by /you/ ranting
    about how you hate make?

    Other people might mention "make" in passing, just as they might mention
    gcc or clang or msvc, as a tool that is often used in connection with C programming. There's no more than that - once your rants are filtered out.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Gabriel Rolland on Mon Jan 15 11:39:32 2024
    On 15/01/2024 08:51, Gabriel Rolland wrote:
    bart <bc@freeuk.com> writes:

    If I recall the ongoing thread, there was two "indefendable" statements:

    OK, whatever the actual spelling of 'indefendable' might be...

    a) make is useless and cryptic

    My 'C and Make' thread showed a clear example of it being used
    gratuitously. I contend that that happens a lot.

    b) gcc's outputing of binaries to a.out by default is useless and
    cryptic

    Since *a* has been explained already. I'll just give my two cents on
    *b*.
    When I'm learning to program, I use to have a lot of source files in the
    same repository. I don't want't the binaries, I just want to play with
    the source and sometimes, compile them and see if they compile correctly
    and the behavior is correct. Outputting the binary to a.out by default instead of "hello.o" is sort of useful here. For two reasons :
    1. I don't have the overhaul of remembering how did I call that source
    file in that particular moment when I wrote it. I know I have to call
    ./a.out

    Hang on: are you generating 'a.out' the object file, or 'a.out' the
    executable file? (Because ./a.out will execute the file.)

    Here is where Unix/Linux's treatment of file extensions does my head in. 'a.out' is used there for both kinds of file. To find out what it
    actually is, you have to look inside the file, which defeats the purpose
    of having a file extension at all.

    and that's it.
    2. It doesn't crowds my directory with lots of useless binaries.

    The problems of always having the same a.exe/a.out output (here it is
    the executable file - see, I have to keep disambiguating!) are multiple:

    * If you working with several small one-file programs c, d, and e say,
    you want them compiled as c.exe, d.exe and e.exe. Having them all be
    a.exe is not going to work; which of c, d, e does it correspond to?
    Suppose you want to run c, d, e one after the other?

    * You might be testing (as I do), multiple compilers on the same c.c.
    The first produces c.exe; you test it. Compile with the second to make a
    new c.exe; you test that. Compile with gcc to make a new ... a.exe. Now
    you have to remember it's a different executable.

    (The number of times I've forgotten that and run c.exe instead, and
    thought gcc's code wasn't quite as fast as I'd expected...).

    * You compile a big program one.c which takes a long time. You then
    compiled another program two.c, which you now realise has overwritten
    the a.exe that represented one.c.

    The answer isn't to use '-o c.exe' either. In both cases gcc is out of
    kilter with the other compilers; it will eiher produce the wrong EXE, or
    you need extra options that the others don't.


    OK, I replied at more length than I intended. I really feel these small
    matters in such tools are important. Others gloss over them:

    * "You never run gcc directly; use make!"

    * "Write a wrapper script or program around gcc"

    * "You spent more time complaining about it then you'd have spent just
    typing '-oprog.exe!"

    And so on. The trouble is if you post, on a forum, some C code with a
    brief note of how it should be built, you can't assume somebody has the
    same wrapper, and you don't want to also post a make script, but you
    have to add these stupid extra options each time.

    I could suggest for example that, if 'gcc prog.c' always generated
    'prog', that you instead used:

    gcc prog.c -oa.out

    if you don't want the proliferation of binaries. But I guess you
    wouldn't want that extra hassle.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to bart on Mon Jan 15 12:45:34 2024
    On 12/01/2024 18:09, bart wrote:
    On 12/01/2024 16:34, David Brown wrote:
    On 12/01/2024 17:12, bart wrote:

    I don't really understand what you are trying to say here.  Are you
    suggesting that your 40 year old DOS IDE is equivalent to modern IDE's ?

    No. Only that the way I normally work hasn't changed a great deal.


    OK. Some of us have learned to take advantage of new technology, new possibilities, new software, new ideas, new tools. We use old tools in
    new ways, new tools in old ways - whatever gives a more pleasant and
    productive environment, with the best quality results.

    Are you trying to say you can use your own tools for your own
    language, and rely on a simple script for C compilation, and can't
    handle anything else in a build process?

    I'm separating the fundamental build-process for a program from what is needed for interactive development and testing.


    Fair enough. These are different tasks. For source code that must be distributed in buildable form (and the huge majority of source code
    written is /not/ distributed), you need a build tool geared at working
    on a wide variety of systems, supporting different compilers and OS's,
    and ensuring that the correct flags and details are in place. Makefiles
    can be a good way to hold this, but for support of a bigger variety of
    systems, autoconfig has been a very successful (if heavy-handed)
    solution that works across many dozens of systems. A more modern tool
    that is simpler in use and works better for common modern systems
    (including Windows) is CMake.

    For development purposes, your tools are aimed at efficient development
    and getting the most help possible from the tools in identifying and
    fixing errors. So there you choose your compiler specifically as
    something with a lot of warnings and other static checking, and you use
    a build system that minimising build time and helps keep track of the
    options you need. Make is the obvious choice that works well for many
    uses - beyond that, you want integrated build and test systems,
    continuous build systems, and that kind of thing.

    So while "make" is neither necessary nor sufficient for all situations,
    it provides a very versatile tool that can cover a great deal of
    situations. If you want one tool that is good enough for a wide range
    of uses, "make" is the one to use. It will be overkill for some cases,
    and for other cases it might need a makefile generator like CMake or
    Meson (or an IDE), but overall it works great for many things. That's
    why it is so popular, and the standard build tool for such a high
    proportion of projects.

    Makefiles you see supplied with open source projects don't generally
    make that distinction.

    Why should they? An optimal deployment build makefile is likely to be different from an optimal development build makefile. But should the
    open source developers make that effort? For a big project that is used
    across a wide range of systems, targeting automatic builds by
    non-developers, the answer is probably yes. For a smaller project,
    where people interested in the source code are expected to be competent
    C or C++ developers, why should they go out of their way to spoonfeed
    newbies or people who have allergies to standard tools? If the project
    targets Windows specifically, perhaps they will provide CMake files or
    even MSVC project files. If the main development platform, and the main target, is *nix, then why should you complain that their build system
    targets that? If you don't like it, ask for your money back.


    But I can see you're struggling with the concept of simplicity.


    Simple solutions are fine for simple problems. A hammer is a simple
    tool, but it's not much use for screws. I like a toolbox - I have
    several simple tools, like hammers and screwdrivers. And I have a few
    complex tools, like an electric drill with interchangeable bits, and a
    few very niche tools for odd uses (like these special bits for removing
    broken screws).

    You live in your own little world, where everything is nailed together,
    and a hammer is all you need. That's great for /you/, but stop getting
    your knickers in a twist just because the rest of the world is different.


    download to a target board via a debugger,

    (Hey, I used to do that! Not a makefile in sight either; how is that possible?


    Grow up.

    I used to do that with no special tools, no external software and no
    external languages. I had to write assemblers for any new devices I has
    to use.)


    Yes, I've heard it before. If you wanted a keyboard, you had to carve
    it out of a rock with your teeth.

    When I learned assembly, I assembled code to hex by hand. On paper. I
    don't consider that particularly relevant to my work today.


    and all sorts of other bits and pieces according to the needs of the
    project.  One makefile beats a dozen scripts.

    It looks like 'make' is competing with 'bash' then!


    I have no idea why you think that - except perhaps because you still
    have no concept of what "make" is and what it does, and think it is
    just a script with a complicated syntax.

    So, what the hell is it then? What makes it so special compared with any other scripting language?

    You've answered part of that yourself below!


    All I can see is that it can create dependency graphs between files -
    which have to be determined from info that you provide in the file, it's
    not that clever - and can use that to avoid recompilation etc of a file unless its dependencies have changed.


    Make coordinates builds. As you say, it creates DAG graphs for the
    tasks needed for the build, based on rules that the user can control.
    This means that the user does not have to provide details for everything
    - make works out a lot from those rules.

    It does, however, need to be told which targets you want to build, and
    what their dependencies are. This can be done manually, or using make's features, or in combination with other tools such as gcc's support for generating dependency files, or by using makefile generators (like
    CMake). Simple makefiles for small projects often list dependencies
    manually, more complex makefiles for bigger projects have more scalable
    and easy to use solutions. For most of my projects, make (with gcc)
    figure out all the required C and C++ files, and all the right
    dependencies, automatically. Once it is set up, I don't change the
    makefile even when adding or removing source files.

    Oh, and make does all this with support for parallel builds - keeping
    track of multiple jobs, including tools that themselves use multiple cpu
    cores at a time.

    That is something I've never needed done automatically in my own work (I
    do it manually as I will know my projects intimately when I'm working
    with them).

    Some developers work on lots of projects. Some projects have lots of developers working on them. Again, your methods are suited only to your
    own little world, not to other people.

    If you're curious about what 'as' expects and speculatively try 'as
    --help', it displays 167 dense lines.


    Are you trying to convince people that your assembler is better than
    gas because yours has fewer features?  Bizarre.

    It's simpler and gives a simple summary of how it works.

    Don't you document your tools?


    Clearly your idea of 'better' is to be vastly more complicated.

    My idea of "better" varies by context - it isn't as overly simplified as
    you seem to think. In your little world, "better" means "does what Bart
    wants at the moment - no one else matters". In the real world, tools
    are written for other people - "better" means it does what /they/ want,
    in the full knowledge that people want different things.


    I guess an assembler which will only work for the processor you happen
    to be working with is no good at all. It has to also support dozens that
    are not relevant to the task in hand.

    Yes.

    Because that means I get well-tested tools used by huge numbers of
    people, with lots of support, lots of features, and lots of versatility.
    And I get them for free, or for low cost - vastly cheaper than if they
    were written specifically for /me/. It doesn't matter if it also
    supports features I don't need, languages I don't need, targets I don't
    need. It matters that they support the features I need, the languages I
    need, the targets I need. And it is a huge bonus that when I want to
    use a new feature, or language, or target, it's already there.

    I've had my share of target-specific tools. I've dealt with different compilers for each target, each with their own idiosyncrasies,
    limitations, extra features, etc. I've written more than my fair share
    of Keil-C and Imagecraft-C and other code that has to be fine-tuned for
    a specific compiler and target. I've used a dozen assemblers, all with
    their own way of handling directives and other bits and pieces. With
    gcc and binutils, I have /one/ toolchain base that I have used for
    perhaps 8 different targets - it was an enormous step forward. I don't
    care if it also supports another dozen targets - maybe I'll use some of
    them in the future too.

    (Oh, and I used "make" for all of this - pretty much all the assemblers
    and compilers.)



    BTW my assembler can directly produce EXE and DLL files; in that regard
    it IS better than 'as' and probably most others which like to off-load
    that critical bit.


    Again, what you see as an advantage is only relevant in your limited
    little world. (And I can appreciate that it /is/ an advantage for your
    usage.) I sometimes need specific features in my linker setup file -
    things that cannot be done in a simple assembler. Not that I
    particularly need to care about the linker or assembler, since it is all handled by the gcc driver controlled by make.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Gabriel Rolland on Mon Jan 15 13:48:39 2024
    On 15/01/2024 09:51, Gabriel Rolland wrote:
    bart <bc@freeuk.com> writes:


    [snip]
    I'm new here, so please forgive my ignorance. What's yours?
    I suppose what I'm asking is: What exactly is your goal here, Bart?

    What is anyone's goal here?

    This newsgroup is just a bunch of old-timers who mainly discuss the
    finer points of the C standard, although the group has been more or
    less dead for the past couple of years.

    Hello there, everyone, this is actually my first post on Usenet at
    all. I started configuring and reading it like two days ago. It is not
    really a test since I'd like to reply to some content here.

    Welcome to the group - and as a new C programmer!

    We don't /just/ discuss the finer points of the C standards, but that is definitely something we do cover, and you'll be hard pushed to find a discussion forum with more standards experts than you find here. We are
    happy to discuss all kinds of aspects of C programming, but if you need platform-specific help or help with particular libraries, it's not often
    a good place - but we might be able to suggest alternatives.

    It's worth starting a new thread if you have something specific - a lot
    of people will already have marked this thread as pointless and ignored!

    This group
    seems rather lively, I guess it is partly thanks to you, Bart.

    Oh, Bart does not lack in enthusiasm and energy for his postings :-) He
    is, however, often confusingly inaccurate about C. Ask some questions,
    and you'll quickly see which regulars give answers that work for your
    needs (some are better at getting the exact details right, others are
    better at explaining in layman's terms).


    Since there are rarely any people who post about any practical
    problems, I think they mainly use stackoverflow and reddit for
    that. The regulars mainly just argue amongst themselves.

    I'm kind of young and getting into programming, in C mainly, because I
    like backend stuff in general. I dislike going to stackoverflow or
    reddit. I reckon these are useful platforms but not open to free
    discussion like it is the case here in Usenet. So this newsgroup is not
    dead and I find it still useful.

    I'm somewhat of an outsider and I like to point out things that I
    believe are wrong in things like the C language and the assorted
    collection of Unix-specific tools that apparently go with it.

    I'm an outsider because I don't routinely use C, or any of the tools,
    and because I don't have background in Unix-based development. I have
    a different perspective.

    This may get on the nerve of some people. But it is interesting. Like
    when you made a point about the redirection (>) of binary output to
    stdout, the people that corrected your assumption of this operator
    teached me a lot about how the redirection of stdin works.

    They say the best way to get help on the internet is not by asking a
    question, but by posting a wrong answer and waiting to be corrected.
    Bart has tried this a lot, but he is not good at listening to the
    corrections.

    Unsurprisingly for a newsgroup titled "comp.lang.c", most people here
    use C a lot, or have used it a lot in the past, and most people here are interested in the language. Bart is an outsider in that sense. Most of
    us also use one or more different languages for various purposes. Many
    of us use *nix systems (typically, but not always, Linux) rather than
    Windows or Macs - unless you are doing Windows or Mac specific
    development, *nix is generally considered more developer friendly. But
    it's far from an exclusive rule.


    So, what happens here is that I mention something that might be some
    bizarre quirk of C, or some weird, unfriendly way some tool works, or
    anything that goes against common-sense or intuition, or that I find
    has caused me grief.

    And then the regulars, instead of agreeing, go on the defensive. Some
    go on the attack, saying I'm the one at fault, I should RTFM, or do
    this or that, or that I'm ignorant, etc etc. (You've seen the thread.)

    So since I have nothing better to do**, I like to defend myself. And
    sometimes it is fascinating seeing people defend the indefendable.

    It is fascinating to see how Bart defends himself against imaginary
    attacks, tilting at windmills. When Bart finds something he doesn't
    like about C (and he finds a /lot/ that he doesn't like), he is unable
    to distinguish between replies that say "I don't like it either, but I
    can live with it", or "It is this way because of ...", or "Some people
    /do/ like it", or "You have misunderstood - the feature works like
    this...". In Bart's mind, any reply gets read as "We all love
    everything about C and its a perfect language". This often leads to unproductive threads, such as this one.


    If I recall the ongoing thread, there was two "indefendable" statements:
    a) make is useless and cryptic
    b) gcc's outputing of binaries to a.out by default is useless and
    cryptic

    Since *a* has been explained already. I'll just give my two cents on
    *b*.
    When I'm learning to program, I use to have a lot of source files in the
    same repository. I don't want't the binaries, I just want to play with
    the source and sometimes, compile them and see if they compile correctly
    and the behavior is correct. Outputting the binary to a.out by default instead of "hello.o" is sort of useful here. For two reasons :
    1. I don't have the overhaul of remembering how did I call that source
    file in that particular moment when I wrote it. I know I have to call
    ./a.out and that's it.
    2. It doesn't crowds my directory with lots of useless binaries.


    A tool that you might find useful when playing around is
    <https://godbolt.org>. It is an online compiler. In particular, it
    makes it extremely easy to test bits of code with a variety of
    compilers, and any command line options you like, and look at the
    generated assembly. (You can also run the program, but I personally
    find looking at the assembly more useful.)

    All people need to do is be honest.

    I agree.

    I think most of us do.


    Does that answer your question?

    (** That's not quite true, this is taking me away from my current
    project. But when people openly insult me, I can't let it go. They
    need to stop replying.)

    You are then on a long quest. Good luck.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to bart on Mon Jan 15 13:19:00 2024
    On 14/01/2024 01:36, bart wrote:
    On 13/01/2024 23:26, Keith Thompson wrote:
    bart <bc@freeuk.com> writes:
    On 13/01/2024 21:42, Keith Thompson wrote:
    bart <bc@freeuk.com> writes:
    On 13/01/2024 04:17, Kaz Kylheku wrote:
    On 2024-01-13, bart <bc@freeuk.com> wrote:
    [...]
    That's a reasonable thing to do. But how does make do it? Can't a >>>>>>> compiler apply the same approach if N files have been submitted?
    Yes. And in fact, languages with good module support like Modula-2 >>>>>> don't need external make utilities.

    Finally somebody admitting that some languages may not need make as
    much.
    Bart, seriously, what the hell are you talking about?
    It's true that some languages don't need "make" as much as C does.
    Nobody here has said otherwise, likely because other languages are
    largely off-topic here in comp.lang.c.

    Except 'make'? I get the impression that most programs written in C
    have a large component written in 'make' too. A component you can't
    always ignore since essential build info is encoded in it.

    Most?  I don't know.  Many?  Sure.

    You wrote, "Finally somebody admitting that some languages may not need
    make as much.".  Has anyone here claimed otherwise?  If not, why do you
    find Kaz's statement so remarkable?

    People have suggested using make for everything, from hello.c up to JP's
    and SL's massive applications.

    Do you think that any one person here speaks for everyone? /I/ have
    written that I find "make" to be a useful tool for all kinds of things,
    not just C projects. (I never suggested I use it for "everything",
    however.) But that's /me/, and the way /I/ like to work - it's a
    convenient tool, and it is in no way C-specific. For some other things, however, I don't find it useful so I don't use it. I rarely need it in combination with Python projects (although I have used it for some
    aspects of some Python projects). I didn't use it with Delphi.
    Sometimes I do, technically, use "make", but it is hidden behind an IDE.

    Writing that someone is "admitting" that there are languages that may
    not need make suggests you think there is some grand conspiracy here -
    that the entire newsgroup is in denial and hiding things. That is
    complete nonsense - pure paranoia. No one has even hinted at anything
    of the sort.

    It would be helpful if you did not make all these crazy exaggerations
    and extrapolations. Maybe you'd find that, if you simply calmed down,
    the world is /not/ all against you and everything you say.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to bart on Mon Jan 15 13:57:32 2024
    On 15/01/2024 12:39, bart wrote:
    On 15/01/2024 08:51, Gabriel Rolland wrote:
    bart <bc@freeuk.com> writes:

    If I recall the ongoing thread, there was two "indefendable" statements:

    OK, whatever the actual spelling of 'indefendable' might be...

    a) make is useless and cryptic

    My 'C and Make' thread showed a clear example of it being used
    gratuitously. I contend that that happens a lot.

    b) gcc's outputing of binaries to a.out by default is useless and
    cryptic

    Since *a* has been explained already. I'll just give my two cents on
    *b*.
    When I'm learning to program, I use to have a lot of source files in the
    same repository. I don't want't the binaries, I just want to play with
    the source and sometimes, compile them and see if they compile correctly
    and the behavior is correct. Outputting the binary to a.out by default
    instead of "hello.o" is sort of useful here. For two reasons :
    1. I don't have the overhaul of remembering how did I call that source
    file in that particular moment when I wrote it. I know I have to call
    ./a.out

    Hang on: are you generating 'a.out' the object file, or 'a.out' the executable file? (Because ./a.out will execute the file.)

    Here is where Unix/Linux's treatment of file extensions does my head in. 'a.out' is used there for both kinds of file. To find out what it
    actually is, you have to look inside the file, which defeats the purpose
    of having a file extension at all.

    *nix does not make heavy use of file extensions (as you know). You can
    use the "file" command to see what kind of file you have (as you also
    know, as you've been told before). Sometimes file extensions are
    helpful, such as for distinguishing different source code languages.

    If you ask gcc to compile but not link a file, such as "gcc -c hello.c",
    the default output will be an object file named "hello.o".

    If you ask gcc to compile and link, such as "gcc hello.c", the default
    output will be an executable called "a.out". (It is in "elf" format -
    long ago, the default executable format was called "a.out".)


    and that's it.
    2. It doesn't crowds my directory with lots of useless binaries.

    The problems of always having the same a.exe/a.out output (here it is
    the executable file - see, I have to keep disambiguating!) are multiple:

    * If you working with several small one-file programs c, d, and e say,
    you want them compiled as c.exe, d.exe and e.exe. Having them all be
    a.exe is not going to work; which of c, d, e does it correspond to?
    Suppose you want to run c, d, e one after the other?

    If that's a problem, write "gcc -o d.exe d.c" to get an executable named "d.exe" (even on Linux). Or write "make d", to compile and link "d.c"
    into an executable called "d".


    * You might be testing (as I do), multiple compilers on the same c.c.
    The first produces c.exe; you test it. Compile with the second to make a
    new c.exe; you test that. Compile with gcc to make a new ... a.exe. Now
    you have to remember it's a different executable.

    (The number of times I've forgotten that and run c.exe instead, and
    thought gcc's code wasn't quite as fast as I'd expected...).


    I think I see a common factor running through all these problems you are having...

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Chris M. Thomasson on Mon Jan 15 14:20:08 2024
    On 15/01/2024 07:37, Chris M. Thomasson wrote:
    On 1/14/2024 9:47 PM, Keith Thompson wrote:
    "Chris M. Thomasson" <chris.m.thomasson.1@gmail.com> writes:
    On 1/14/2024 2:58 PM, Keith Thompson wrote:
    [...]
    I already mentioned that GNU as doesn't write machine code to the
    terminal.  (I discussed problems that could occor *if it did*.)  Did >>>> you
    miss that?  I know you're seeing at least *some* of my posts.
    Yes, it writes its output to "a.out" by default, for historical
    reasons.
    It also has an option to specify the name of the output file -- an
    option that is almost always used in practice.  Invoking the "as"
    command directly is relatively rare.

    Rare until you have to use it.

    And still rare after you have to use it.  Are you using the word "rare"
    in some non-standard sense?

                                    Fwiw, keep in mind that this was well
    before C++11. Take the sensitive sync algorithms (compiler
    reordering's, ect...) out of the realm of C/C++ and code them up using
    assembly language. The declarations can be in C with CDECL ABI.

    How is any of that relevant?  Are you saying that invoking the "as"
    command directly *isn't* relatively rare?

    I am saying that I had to create my own sync primitives in pure assembly language back them. So, I would use as to assemble them. No problem.
    Nothing strange, just that I had to do it. Whether or not that is rare,
    well, that's another story? The commands were in a makefile anyway. Is
    that rare?


    It is a /long/ time since I have found any point in writing pure
    assembly files. I do sometimes need to write assembly code - but
    writing it as gcc inline assembly makes it far easier because the
    compiler handles all the mess of register allocation and otherwise
    getting optimal integration with C (or C++).

    It also seems a bit odd to use non-portable assembly to implement sync primitives equivalent to those later standardised in C11/C++11. gcc has
    had builtin functions for atomic accesses for decades (C11/C++ merely
    changed the spellings and standardised them), and higher level features
    like mutexes are provided by the OS.

    Still, you use the tools you like, as you like to use them.

    Even if you needed to invoke "as" directly (and not, for example, via
    "gcc"), it's still trivial to tell it the names of the input and output
    files.  I just typed "as hello.s -o hello.o" on my system, and it worked
    just fine.  And "gcc -c hello.s" did essentially the same thing.

    Well, I used as directly. Just like with MASM. That is rare? ;^)

    Yes.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Kaz Kylheku on Mon Jan 15 14:15:14 2024
    On 15/01/2024 07:07, Kaz Kylheku wrote:
    On 2024-01-15, bart <bc@freeuk.com> wrote:


    GCC has great inline assembly.

    My question is: what would the C version look like with that line in gcc
    inline assembly? (In both cases, 'a' should end up with the value 14.)

    Let's make it more interesting: what if b and c come from arguments,
    and the static int d actually has state that changes between
    invocations, so it can't be optimized away. Let's return the
    result, a:

    int F(int b, int c)
    {
    static int d=4;
    int a;

    d++;

    asm("imul %3, %2\n\t"
    "add %2, %1\n\t"
    "mov %1, %0\n\t"
    : "=r" (a)
    : "r" (b), "r" (c), "r" (d));

    return a;
    }

    It's pretty arcane in that the material is both in string literals,
    and not. The assembly language template is textual; the compiler knows nothing about its interior.

    I specified one output operand, and three input operands, requesting
    that they be in registers. I don't specify the register identities.
    They are referenced by number: %0, %1, %2, %3 in the order they
    appear. (A way to use named references exists.)

    OK. I think you've just confirmed my worst fears about gcc inline assembly.

    But you've also made it clear that this isn't really assembly at all. Is
    it even for x64? I can't tell! The use of 'mov' rather than 'ldr' or
    'str' suggests it is x86 or x64 rather than ARM. But are those 32-bit
    mov's or 64-bit?

    So it's some sort of hideous hybrid that gcc has come up with, that is
    neither assembly nor C.

    It figures that most examples I could find on-line were only a handful
    of lines, often just one line, of actual instructions rather than
    directives. My inline assembler doesn't have directives.

    It would be far, far simpler to write assembly in its own file (and keep
    well away from AT&T syntax), than to try and inline it. Which defeats
    the purpose of it.


    As for your revised example, I would write it like this (I'm using my
    language, but no reason why that assem...end block can't be written the
    same way within in C, using assem {...}, and without those ghastly strings):

    func F(int b, c)int =
    static int d=4
    ++d

    assem
    mov rax, [c]
    imul2 rax, [d]
    add rax, [b]
    end
    end

    Exactly the same as before, except I don't need to explicitly write to
    'a'. If a return value is expected in this context, then an assem block
    is expected to set up that value in the right place.

    Or I could even assign an assem block to variable:

    a := assem mov rbx, [c]; imul2 rbx, [d]; add rbx, [b]; end

    (I didn't know I could do this, but it seems to work.)


    25: 0f af d1 imul %ecx,%edx
    28: 01 d0 add %edx,%eax
    2a: 89 c0 mov %eax,%eax

    -----

    f: 0f af f0 imul %eax,%esi
    12: 01 f7 add %esi,%edi
    14: 89 f8 mov %edi,%eax

    The static variable is accessed relative to the instruction pointer.
    The offset is all zeros: that will be patched when this is linked.

    That is a back-end detail; it's not usually specified in the instructions.


    Note that between the unoptimized and optimized code, the register
    identities changed entirely.

    GCC's inline assembly feature is largely agnostic of the assembler
    back end. It interfaces with register allocation and such, but is
    otherwise generic. This allows it to have exactly the same grammar,
    no matter the architecture target.

    The syntax isn't particularly nice, but it has power.

    So it's some kind of HLA.

    You need to tell me, because I will otherwise not have a clue. From what
    I've seen of gcc inline asm:

    * Code has to be written within string literals, in dreadfil AT&T
    syntax. And apparently even with embedded \n line breaks. (Good
    grief - I think early 80s BASICs had more sophisticated facilities!)

    The code template, after the registers like %0 and %1 are substituted
    into it, is just shoved into the assembly language output verbatim.

    That's how I crudely did it at one time (though still using real
    syntax). But it doesn't work when the output is binary code and not
    assembly text.


    The compiler doesn't analyze the interior.

    AT&T syntax is used if that's what the assembler requires.

    Not all GCC targets have assemblers in whose language the destination operand is on the right; it's that way for the x86 family though.

    * You mostly use offsets to get at local variables

    Nope; it's pretty much transparent.

    So can specify the variables direcly? As in, for example:

    asm inc u32 [d] # one-liner form of my syntax

    I consider that when writing assembly, YOU are in charge not the
    compiler.

    If you're writing *inline* assembly in compiled code, if you let
    the compiler be in charge of some things, it's a lot better.

    Then you'd write in the HLL. Having inline ASM already takes care of 80%
    of the pain of writing pure assembly. In gcc inline, it /adds/ pain!

    But I stated my compiler does take care of some details like adding the frame-pointer into the address mode. That's means you don't need to keep
    track of whether a variable is local, static or global.

    Below is an example of a function that mostly HLL with some inline ASM.
    This one does the job of LIBFFI (another needlessly hard to build
    library), for Win64 ABI.

    -------------

    export function os_calldllfunction(
    ref proc fnaddr,
    int retcode, nargs,
    ref[]u64 args,
    ref[]byte argcodes)u64 =

    u64 a
    r64 x
    int nextra := 0, pushedbytes

    !Stack is 16-byte aligned at this point

    if nargs<4 then
    nextra := 4-nargs !need at least 4 slots for shadow space
    elsif nargs.odd then !need one more for a 16-byte-aligned stack
    nextra := 1
    fi

    pushedbytes:=(nextra+nargs)*8

    to nextra do
    asm push 0
    od

    for i := nargs downto 1 do
    a := args[i] !get generic 64-bit value to push
    asm push u64 [a]
    od

    ! Blindly load first 4 args to both int/float regs, whether used or not,
    ! and assuming calling a variadic function whether it is or not

    assem
    mov D10, [Dstack]
    movq XMM0, [Dstack]
    mov D11, [Dstack+8]
    movq XMM1, [Dstack+8]
    mov D12, [Dstack+16]
    movq XMM2, [Dstack+16]
    mov D13, [Dstack+24]
    movq XMM3, [Dstack+24]
    end

    if retcode='I' then
    a := (ref func:int64(fnaddr))^()
    asm add Dstack,[pushedbytes]
    return a

    else
    x := (ref func:r64(fnaddr))^()
    asm add Dstack,[pushedbytes]
    return u64@(x) !(type-punning cast)

    fi
    end

    (Uses alternate register naming.)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to bart on Mon Jan 15 14:35:46 2024
    bart <bc@freeuk.com> writes:
    On 15/01/2024 07:07, Kaz Kylheku wrote:
    On 2024-01-15, bart <bc@freeuk.com> wrote:


    asm("imul %3, %2\n\t"
    "add %2, %1\n\t"
    "mov %1, %0\n\t"
    : "=r" (a)
    : "r" (b), "r" (c), "r" (d));

    return a;
    }

    But you've also made it clear that this isn't really assembly at all. Is
    it even for x64? I can't tell! The use of 'mov' rather than 'ldr' or
    'str' suggests it is x86 or x64 rather than ARM. But are those 32-bit
    mov's or 64-bit?

    1) it's not an assembler, it's a way to "patch" the assembler code
    generated by the compiler.


    So it's some sort of hideous hybrid that gcc has come up with, that is >neither assembly nor C.

    Your baseless opinion noted. Feel free to submit a patch to
    the GCC team to implement your preferred syntax for inline assembler.

    Do ensure that it works for all 100 of the GCC target architectures.


    It would be far, far simpler to write assembly in its own file (and keep
    well away from AT&T syntax),

    No, it would not be.

    And the AT&T syntax is _far_ superior to the intel syntax
    with all the ugly syntactic sugar.

    val = tp->mem_read(psource, len);
    rax = get_reg_value(regs, REG_RAX, QUAD);

    __asm__ __volatile__ (
    "testl $1, %1\n\t" // 1 byte operand?
    "jne 1f\n\t" // Yes, go to it

    "testl $8, %1\n\t" // Was it eight bytes?
    "jne 2f\n\t" // Yup. Done.

    "testl $4, %1\n\t" // Was it 4 bytes?
    "je 3f\n\t" // no, Try 2 bytes

    "movsx %%ebx, %%rbx\n\t" // Sign extend 32-bits to 64
    "cdqe\n\t" // Sign extend EAX to RAX
    "jmp 2f\n" // Done here

    "3:\tmovsx %%bx, %%rbx\n\t" // Sign extend 16-bits to 64
    "movsx %%ax, %%rax\n\t" // Sign extend AX TO RAX
    "jmp 2f\n" // Done here

    "1:\tmovsx %%bl, %%rbx\n" // Sign extend BL to RBX
    "movsx %%al, %%rax\n" // Sign extend AL to RAX
    "2:\tsub %%rbx, %%rax\n" // Subtract from comparison value
    "pushfq\n\t" // Save the flags from the sub
    "popq %0\n\t"
    :"=r"(flags_word)
    :"r"(len), "b"(val), "a"(rax));

    guest_rflags &= ~MATH_FLAGS;
    guest_rflags |= (flags_word & MATH_FLAGS);
    source += inc;
    count--;

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to Gabriel Rolland on Mon Jan 15 14:56:54 2024
    Gabriel Rolland <gabrielrolland@gmail.com> writes:
    bart <bc@freeuk.com> writes:




    This may get on the nerve of some people. But it is interesting. Like
    when you made a point about the redirection (>) of binary output to
    stdout, the people that corrected your assumption of this operator
    teached me a lot about how the redirection of stdin works.

    Sometimes it can help to start from the beginning:

    http://squoze.net/UNIX/v7/files/doc/06_shell.pdf

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Kaz Kylheku on Mon Jan 15 16:39:51 2024
    On 15/01/2024 08:07, Kaz Kylheku wrote:
    On 2024-01-15, bart <bc@freeuk.com> wrote:
    On 15/01/2024 00:34, Kaz Kylheku wrote:


    I will now show it in my language but with that assignment replaced by
    inline assembly:

    int a

    proc F=
    int b:=2, c:=3
    static int d=4

    assem
    mov rax, [c] # (note my ints are 64 bits)
    imul2 rax, [d]
    add rax, [b]
    mov [a], rax
    end

    Problem is that the compiler's register allocator now has to be informed that the assembly language part is using rax and work around it.

    That is not the only problem, but it is certainly a limitation.


    end

    My question is: what would the C version look like with that line in gcc
    inline assembly? (In both cases, 'a' should end up with the value 14.)

    Let's make it more interesting: what if b and c come from arguments,
    and the static int d actually has state that changes between
    invocations, so it can't be optimized away.

    That makes sense.

    Let's return the
    result, a:

    int F(int b, int c)
    {
    static int d=4;
    int a;

    d++;

    asm("imul %3, %2\n\t"
    "add %2, %1\n\t"
    "mov %1, %0\n\t"
    : "=r" (a)
    : "r" (b), "r" (c), "r" (d));

    return a;
    }

    It's pretty arcane in that the material is both in string literals,
    and not. The assembly language template is textual; the compiler knows nothing about its interior.

    I specified one output operand, and three input operands, requesting
    that they be in registers. I don't specify the register identities.

    Yes. You could also use "g", which allows registers, or memory
    addresses, or immediate results, which could be appropriate for some
    operands on some processors (like the x86). Re-arranging slightly
    (unless that is considered "cheating"), so that the C code is "c *= d; c
    += b;", then only "c" needs to be in a register. And gcc can do the
    move for you, so you don't need the third instruction. (On a processor
    with three argument instructions, common for many RISC cpus, your "add"
    would go directly to "a". But you'd also want to keep all operands as "r".)

    They are referenced by number: %0, %1, %2, %3 in the order they
    appear. (A way to use named references exists.)

    Yes. I like to use it, if I have more than two operands. I often use
    it even if I have just one or two operands.

    GNU inline assembly is ugly, but it's very well designed semantically;
    it hits the target.


    I don't think gcc inline assembly is particularly pretty, or easy to use
    - but I have not seen any alternatives that come close to the power and
    /are/ pretty or easy to use. Inline assembly is something to be used
    rarely, when you really need it - and rarely used things are allowed to
    be difficult or ugly, and to require the user to look up reference
    manuals. If a language or compiler's inline assembly is easy to use,
    it's a sign that the tool is so weak that people will need to resort to assembly on a regular basis.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Scott Lurndal on Mon Jan 15 15:44:50 2024
    On 15/01/2024 14:35, Scott Lurndal wrote:
    bart <bc@freeuk.com> writes:

    But you've also made it clear that this isn't really assembly at all. Is
    it even for x64? I can't tell! The use of 'mov' rather than 'ldr' or
    'str' suggests it is x86 or x64 rather than ARM. But are those 32-bit
    mov's or 64-bit?

    1) it's not an assembler, it's a way to "patch" the assembler code
    generated by the compiler.


    So it's some sort of hideous hybrid that gcc has come up with, that is
    neither assembly nor C.

    Your baseless opinion noted. Feel free to submit a patch to
    the GCC team to implement your preferred syntax for inline assembler.

    Do ensure that it works for all 100 of the GCC target architectures.

    Which all share the same instruction set? And all have an 'ebx' register:

    "movsx %%ebx, %%rbx\n\t"
    ?



    It would be far, far simpler to write assembly in its own file (and keep
    well away from AT&T syntax),

    No, it would not be.

    And the AT&T syntax is _far_ superior to the intel syntax
    with all the ugly syntactic sugar.

    val = tp->mem_read(psource, len);
    rax = get_reg_value(regs, REG_RAX, QUAD);

    __asm__ __volatile__ (
    "testl $1, %1\n\t" // 1 byte operand?
    "jne 1f\n\t" // Yes, go to it

    "testl $8, %1\n\t" // Was it eight bytes?
    "jne 2f\n\t" // Yup. Done.

    "testl $4, %1\n\t" // Was it 4 bytes?
    "je 3f\n\t" // no, Try 2 bytes

    "movsx %%ebx, %%rbx\n\t" // Sign extend 32-bits to 64
    "cdqe\n\t" // Sign extend EAX to RAX
    "jmp 2f\n" // Done here

    "3:\tmovsx %%bx, %%rbx\n\t" // Sign extend 16-bits to 64
    "movsx %%ax, %%rax\n\t" // Sign extend AX TO RAX
    "jmp 2f\n" // Done here

    "1:\tmovsx %%bl, %%rbx\n" // Sign extend BL to RBX
    "movsx %%al, %%rax\n" // Sign extend AL to RAX
    "2:\tsub %%rbx, %%rax\n" // Subtract from comparison value
    "pushfq\n\t" // Save the flags from the sub
    "popq %0\n\t"
    :"=r"(flags_word)
    :"r"(len), "b"(val), "a"(rax));

    You're having a laugh, surely? AT&T is bad enough even without the
    travesty of it displayed here:

    "jmp 2f\n"
    "3:\tmovsx %%bx, %%rbx\n\t"

    What's with the strings, newline and tab escapes? What's that 'f' for?
    Instead of %rbx you now have to type %%rbx; better not leave out an % by mistake. And you have to pay close attention here:

    "testl $1, %1\n\t" // 1 byte operand?

    so as not to mix up $1 and %1. It almost makes makefiles look readable!

    I generally write:

    "\tmovsx %%bx, %%rbx\n\t"

    as:

    movsx rbx, bx

    I assume "2f" and "2:" are labels? I don't understand why you couldn't
    have made it a /little/ more readable by writing:

    "1:\tmovsx %%bl, %%rbx\n" // Sign extend BL to RBX
    "movsx %%al, %%rax\n" // Sign extend AL to RAX

    for example as:

    "1: movsx %%bl, %%rbx" nl // ...
    " movsx %%al, %%rax" nl // ...

    BTW those comments are superfluous, unless you are acknowledging that
    the syntax is so unreadable, that you have to write a version in English.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to bart on Mon Jan 15 16:23:24 2024
    On 15/01/2024 03:14, bart wrote:
    On 15/01/2024 00:34, Kaz Kylheku wrote:
    On 2024-01-14, bart <bc@freeuk.com> wrote:
    On 12/01/2024 21:31, Kaz Kylheku wrote:
    On 2024-01-12, bart <bc@freeuk.com> wrote:



    GCC has great inline assembly.

    You can reference C expressions, which
    are evaluated to registers that the register allocator chooses, which
    you can reference in your inline code in a symbolic way.

    GCC inline assembly looks absolutely diabolic.

    You forgot the "IMHO" disclaimer. /You/ think it looks diabolic, and
    you probably think that means everyone else thinks so too, and that
    anyone who says they can use gcc inline assembler successfully is lying
    or in denial.

    I've used inline assembly with a fair number of compilers over the
    years. There is no doubt that gcc's system can look scary. And there
    is no doubt that there are simpler systems around.

    However, I have never seen an inline assembly syntax that integrates
    with an optimising compiler in an efficient way, and is not
    approximately as complicated as gcc's.

    Simpler inline assembly invariably places a lot more limits on the
    integration. For example, it can require "assembly only" functions,
    which cannot be inlined into other code. Or it forces specific uses of registers, and perhaps forces local data to be put on the stack instead
    of being in registers.

    Several of the optimising compilers I have used have supported gcc's
    syntax for inline assembly - including gcc, icc, clang and CodeWarrior
    (which is a commercial compiler with no connection whatsoever with gcc
    or open source - it just copies several of gcc's extensions because they
    are a de-facto standard). The only compiler I used with an inline
    assembly of comparable power and integration to gcc's, but a different
    syntax, was Diab Data. It's syntax was nicer in some ways, less nice in others, but I am confident that you would, if you were being honest,
    find it equally diabolical.

    (Note that gcc also supports what it calls "Basic assembly syntax". But
    for most purposes, "Extended assembly syntax" is used because it is much
    more powerful.)


    I take it you've never
    seen it done properly?

    Actually I spent 5-10 minutes looking for examples, to try and figure
    out if asm instructions could in fact directly refer to symbols in the HLL.


    You pass the relevant data into and out of the inline assembly. If you
    think you need access to other symbols in the assembly, you are (almost certainly) doing things wrong. You are trying to do the compiler's job
    behind its back, and that is not a good idea.

    But most examples were one or two lines of weird syntax, following by
    some interfacing code. So I don't know.


    Exactly. You don't know.

    If /I/ had to write extensive programs in gcc inline assembly, then put
    a gun to my head now!

    If you are trying to write extensive programs in assembly, you are
    already getting it wrong. Inline assembly is for things that cannot be expressed in high level languages, or the very rare occasions where you
    know a way to do something in assembly that is very much more efficient
    than the compiler can generate, and the code is speed critical, and
    there are no built-ins for the task, and no target intrinsics provided
    by the processor manufacturer.


    Take this example in C:

       int a;

       void F(void) {
            int b=2, c=3;
            static int d=4;

            a = b + c * d;
       }

    I will now show it in my language but with that assignment replaced by
    inline assembly:

        int a

        proc F=
            int b:=2, c:=3
            static int d=4

            assem
                mov   rax, [c]               # (note my ints are 64 bits)
                imul2 rax, [d]
                add   rax, [b]
                mov   [a], rax
            end
        end

    My question is: what would the C version look like with that line in gcc inline assembly? (In both cases, 'a' should end up with the value 14.)

    void F(void) {
    int b = 2:
    int c = 3;
    static int d = 4;

    asm ("imul2 %[c], %[d]\n\t"
    "add %[c], %[b]"
    : [c] "+g" (c) : [b] "g" (b), [d] "g" (d));
    a = c;
    }

    The generated result (from <https://godbolt.org>) is :

    F():
    mov eax, 3
    imul2 eax, 4
    add eax, 2
    mov DWORD PTR a[rip], eax
    ret

    As you can see, I only need two manual assembly instructions - thinks
    like moving data around are best handled in C, typically automatically
    done by the compiler to fit the input and output operands. And the
    compiler generates code that works with the optimiser.

    With your simplistic inline assembly, you need to write everything
    manually. And the compiler must assume to use rax and other scratch
    registers, meaning it can't use them for local data that lives around
    the assembly - even if your code does not use them. It has to put the
    register data on the stack, and then your inline assembly has to move it
    off the stack - a pointless waste of time compared to gcc that knows
    about the data and the registers. Your assembly changes the value of
    "a", but the compiler doesn't know that - so it is forced to make sure
    all memory data is written out before the assembly, and re-read after
    the assembly. What a painful waste of target efficiency!

    Yes, your assembly syntax is simple and clear. No, it is not better for real-world use-cases where inline assembly is relevant.

    Let's look at an actual example from my own code, in an older project.
    I wanted an endian swap function on an ARM microcontroller, and for
    reasons that escape me for now, I did not want to use gcc's
    __builtin_bswap32, or an intrinsic from a header, or just plain C code
    (which modern gcc could optimise to a single "rev" instruction). The
    code was probably originally written for quite an old version of the
    compiler. So I wrote the function:

    static inline uint32_t swapEndian32(uint32_t x) {
    uint32_t y;
    asm ("rev %[y], %[x]" : [y] "=r" (y) : [x] "r" (x) : );
    return y;
    }

    This is, IMHO, quite clear once you know that gcc assembly consists of
    the assembly template, the outputs, then the inputs. And it generates
    the code optimally - when used in an expression, there will be no extra
    moves, or data put on the stack, or wasted registers. The compiler can
    move the code back and forth while optimising, eliminate calls when the
    result is used, and generally do its job just as well with this function
    as any other inline function or built in operator.


    You need to tell me, because I will otherwise not have a clue.

    It's clear that you haven't a clue. So how can you justify ranting and
    raving against something you don't understand?

    From what
    I've seen of gcc inline asm:

     * Code has to be written within string literals,

    Yes, obviously. Assembly is not C, so writing assembly mixed in your C requires it to be in a format that is acceptable in C syntax (or at
    least close enough to C syntax to be a non-invasive extension). String literals are also quite amenable to generation by macros, for those that
    want to write something complicated.

    in dreadfil AT&T
       syntax.

    "Dreadful" is, again, /your/ opinion - not shared by everyone. (I
    personally don't care either way.) It only applies to x86, not any
    other targets, and is easily changed by the "-masm=intel" flag.

    And apparently even with embedded \n line breaks. (Good
       grief - I think early 80s BASICs had more sophisticated facilities!)

    That is an inevitability for string literals. And it doesn't matter
    much in practice, since most inline assembly (IME) consists of a single statement - gcc handles any moves that might be needed.

    Remember, the compiler passes the assembly on to the assembler - this is
    /not/ a C compiler with a built-in assembler. And that's a good thing.
    Have you any idea how many assembly instructions there are for all the
    targets supported by gcc? And you'd need to update gcc every time there
    was a new instruction, rather than just updating the assembler (which is
    a lot simpler).

    Of course it would be /possible/ to extend gcc with a built-in
    assembler. But what would that give you? Lots of duplicate work to
    support C, C++, Fortran, Ada, and other languages? The assembler
    already handles assembly - why make an HLL do it? It's a lot better to
    put the effort into reducing the number of times you actually need to
    write inline assembly, by improving the optimiser and builtin functions.


     * You mostly use offsets to get at local variables

    You never do that. You are imagining things. Or you are looking at
    some very odd inline assembly examples.


     * You apparently aren't allowed to use just any registers as you need
       to negotiate with gcc so as not to interfere with /its/ use of
       registers. So most examples I saw seemed to deal with this.

    Or, as sane people would say, you don't need to mess around trying to
    figure out what different registers are used for different purposes, or
    where your input data is, or where your output data should go - gcc will
    handle it all for you.


    I consider that when writing assembly, YOU are in charge not the
    compiler. As you can see from mine:

     * It is written just as it would be in an actual ASM file

    Yes - and that's why it is so limited, and requires so much more
    assembly. I prefer to let the compiler do what the compiler is good at.


     * You can refer to variables directly (the compiler will add what is
       needed to access locals or statics)

    I can refer to all the variables I want to - and coordinate with the
    compiler so that it knows what I am doing. Cooperation works far better
    than some arrogant pompous fool claiming they know better, and ruining
    the optimiser's work. Mind you, you wrote your compiler, so I suppose
    you /do/ know better than your compiler.


    If a function uses inline ASM, variables are kept in memory not
    registers.

    What a terrible pessimation.

    (I might allow that at some point.) Most such functions
    however contain only ASM.


    What a terrible limitation.

    That still lets ASM use the facilities of the HLL such as functions, declarations, named constants, scopes etc.

    I suppose you're going to suggest that gcc's facilities are superior...


    There really isn't the slightest doubts there.

    I'll happily agree that your inline assembly is simpler. But in every
    other respect, it's not close to gcc's.

    But perhaps you don't care about efficient code generation (and to be
    fair, that is certainly not always important), and perhaps since your
    compiler doesn't do much optimising then there is little to be lost by
    failing to work along with the optimiser. And perhaps you have to write
    big sections of assembly because you can't write them in C and get fast results.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Kaz Kylheku on Mon Jan 15 17:04:23 2024
    On 15/01/2024 08:40, Kaz Kylheku wrote:
    On 2024-01-15, Kaz Kylheku <433-929-6894@kylheku.com> wrote:
    On 2024-01-15, bart <bc@freeuk.com> wrote:
    On 15/01/2024 00:34, Kaz Kylheku wrote:
    In Unixes and the GNU Project, there has not been a focus on assembly
    language as a primary development language, with a great developer
    experience.

    That's pretty much a fact.

    That is extraordinary. Wasn't C first implemented in assembly? It's

    No; C would have been implemented in NB (new B). It was B that was
    implemented in assembly. That's just bootstrapping, though.

    Thompson and Ritchie didn't have a nice assembler; IIRC, they started
    out by assembling code using macros in the TECO editor.

    Assembly language has never been emphasized in Unix, to my best
    knowledge. It's there.

    always been a mainstay of computing as far as I can remember. Except no
    one now write whole apps in assembly. (I've done quite a few in the past.) >>
    I did a bunch of assembly language programming, which was with
    "nice" assemblers. At university, I made a linked list library with
    numerous functions on a Sun 3 (68K) using Sun's "as". That was my
    first encounter with Unix's idea of assembly. I got it done, but it
    was pretty horrible, with next to no diagnostics when there was
    something wrong. It was obvious that the tool assumes correct input,
    coming from a compiler.

    My 'aa' assembler was also
    designed mainly for machine-generated code, so it has very few frills. >>>>>
    The syntax however is decent enough that I can use it for my inline
    assembler too.

    GCC has great inline assembly.

    You can reference C expressions, which
    are evaluated to registers that the register allocator chooses, which
    you can reference in your inline code in a symbolic way.

    GCC inline assembly looks absolutely diabolic. I take it you've never
    seen it done properly?

    Actually I spent 5-10 minutes looking for examples, to try and figure
    out if asm instructions could in fact directly refer to symbols in the HLL. >>>
    But most examples were one or two lines of weird syntax, following by
    some interfacing code. So I don't know.

    If /I/ had to write extensive programs in gcc inline assembly, then put
    a gun to my head now!

    Take this example in C:

    int a;

    void F(void) {
    int b=2, c=3;
    static int d=4;

    a = b + c * d;
    }

    I will now show it in my language but with that assignment replaced by
    inline assembly:

    int a

    proc F=
    int b:=2, c:=3
    static int d=4

    assem
    mov rax, [c] # (note my ints are 64 bits)
    imul2 rax, [d]
    add rax, [b]
    mov [a], rax
    end

    Problem is that the compiler's register allocator now has to be informed that
    the assembly language part is using rax and work around it.

    end

    My question is: what would the C version look like with that line in gcc >>> inline assembly? (In both cases, 'a' should end up with the value 14.)

    Let's make it more interesting: what if b and c come from arguments,
    and the static int d actually has state that changes between
    invocations, so it can't be optimized away. Let's return the
    result, a:

    int F(int b, int c)
    {
    static int d=4;
    int a;

    d++;

    asm("imul %3, %2\n\t"
    "add %2, %1\n\t"
    "mov %1, %0\n\t"
    : "=r" (a)
    : "r" (b), "r" (c), "r" (d));

    return a;
    }

    We can also turn this multiply and add into a stand-alone primitive
    that we can put behind a macro:

    #define mul_add(x, y, z) \
    ({ int _res; \
    asm("imul %3, %2\n\t" \
    "add %2, %1\n\t" \
    "mov %1, %0\n\t" \
    : "=r" (_res) \
    : "r" (x), "r" (y), "r" (z)); \
    _res; })

    static inline int mul_add(int x, int y, int z) {
    asm("imul %[z], %[y]\n\t"
    "add %[z], %[x]"
    : [z] "+r" (z) : [y] "g" (y), [x] "g" (x));
    return z;
    }


    Which we then freely use like this:

    int F(int b, int c)
    {
    static int d=4;
    int a;

    d++;

    a = mul_add(b, c, d);

    return a;
    }


    Yes.


    Complex example:

    int G(int a, int b, int c, int d, int e, int f, int g, int h, int i)
    {
    return mul_add(mul_add(a, b, c),
    mul_add(d, mul_add(e, f, g), h),
    i);
    }

    gcc -O2 code:

    0000000000000020 <G>:
    20: 0f af f2 imul %edx,%esi
    23: 01 f7 add %esi,%edi
    25: 89 ff mov %edi,%edi
    27: 8b 44 24 08 mov 0x8(%rsp),%eax
    2b: 8b 54 24 10 mov 0x10(%rsp),%edx
    2f: 44 0f af c8 imul %eax,%r9d
    33: 45 01 c8 add %r9d,%r8d
    36: 44 89 c0 mov %r8d,%eax
    39: 0f af c2 imul %edx,%eax
    3c: 01 c1 add %eax,%ecx
    3e: 89 c9 mov %ecx,%ecx
    40: 8b 44 24 18 mov 0x18(%rsp),%eax
    44: 0f af c8 imul %eax,%ecx
    47: 01 cf add %ecx,%edi
    49: 89 f8 mov %edi,%eax
    4b: c3 retq




    GCC inline assembly is good if you have certain instructions that the compiler
    doesn't use, and you'd like to use them as first class primitives (meaning that
    they are not disadvantaged compared to primitives the compiler knows about).

    We would likely obtain better code if if we unbundled the multiplication
    and addition by writing separate mul and add primitives.

    Because the code above has to follow the rigid template where imul
    is immediately followed by the related add, after which there is
    a mandatory mov.

    We really want:

    #define mul_add(x, y, z) add(x, mul(y, z))

    where we separately write the add and mul as inline assembly fragments.

    static inline int mul(int x, int y) {
    asm("imul %[x], %[y]"
    : [x] "+r" (x) : [y] "g" (y));
    return x;
    }

    static inline int add(int x, int y) {
    asm("add %[x], %[y]"
    : [x] "+r" (x) : [y] "g" (y));
    return x;
    }

    static inline int mul_add(int x, int y, int z) {
    return add(x, mul(y, z));
    }

    Now compiling G gives (with Intel syntax - because some people cry
    louder than others!) :


    G:
    mov eax, edi
    imul r9d, DWORD PTR [rsp+8]
    imul esi, edx
    add r8d, r9d
    add eax, esi
    imul r8d, DWORD PTR [rsp+16]
    add ecx, r8d
    imul ecx, DWORD PTR [rsp+24]
    add eax, ecx
    ret

    Notice how gcc can now re-arrange the adds and multiplies for greater efficiency, and skip redundant moves and unnecessary register loads.

    This is almost as optimal as if we define:

    static inline int mul_add(int x, int y, int z) {
    return x + y * z;
    }

    The only difference is that the for the inline assembly, the compiler
    can't take advantage of what it knows about being able to ignore the
    upper half of the 64-bit registers when doing this 32-bit arithmetic.


    A mul_add primitive might make sense if the processor had such a thing
    in one instruction.

    With GCC inline assembly, you want to only put the essentials into it:
    only do what is necessary and only bundle together what must be
    bundled.


    Yes. For inline assembly, simple is not really important, but small is beautiful! Say exactly what you mean, no more and no less, and let the compiler do what it does best.


    And move to C++20. Then you can write (for the mythical madd instruction) :

    constexpr int mul_add(int x, int y, int z)
    __attribute__((const))
    {
      if (std::is_constant_evaluated()) {
    return x + y * z;
    } else {
    asm("madd %[x], %[y], %[z]"
    : [x] "+r" (x) : [y] "g" (y), [z] "g" (z));
       return x;
    }
    }


    Now the compiler can evaluate at compile time if the parameters are
    known, or using the super-efficient madd instruction at runtime if
    necessary.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From James Kuyper@21:1/5 to bart on Mon Jan 15 11:22:26 2024
    On 13/01/2024 23:39, bart wrote:
    On 13/01/2024 21:42, Keith Thompson wrote:
    bart <bc@freeuk.com> writes:
    On 13/01/2024 04:17, Kaz Kylheku wrote:
    On 2024-01-13, bart <bc@freeuk.com> wrote:
    [...]
    It's true that some languages don't need "make" as much as C does.

    Nobody here has said otherwise, likely because other languages are
    largely off-topic here in comp.lang.c.

    Except 'make'? I get the impression that most programs written in C have
    a large component written in 'make' too. ...

    That's not been my experience. The make file is generally only a small
    fraction of the total size of the source code files. This is
    particularly true if it was written by someone familiar with and making effective use of make's defaults, which are generally well-chosen (you
    would not agree, of course - but those defaults were chosen for a target audience with very different preferences from yours).

    ... A component you can't always
    ignore since essential build info is encoded in it.

    Well, yes - but that build info needs to be stored somewhere. It could
    be in the documentation, but especially for projects far more
    complicated than yours, equivalent documentation would be large,
    complex, and easily misunderstood. Putting that information in a make
    file is far more reliable, when targeting platforms that support make.
    It's also a lot easier to customize the make file than to figure out how
    to make corresponding modifications to the documentation.

    Part of the information could be put in a build script. In fact, that's
    what makefile controls: for each rule, there's a build script. By
    default, the "sh" shell is used to interpret those scripts, but you can
    change it to use your preferred scripting language instead.
    However, all of those parts of a makefile that aren't build scripts
    encode additional useful information that ordinary script languages
    aren't designed to handle.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to David Brown on Mon Jan 15 17:29:38 2024
    On 15/01/2024 17:04, David Brown wrote:
    On 15/01/2024 08:40, Kaz Kylheku wrote:

    A mul_add primitive might make sense if the processor had such a thing
    in one instruction.

    With GCC inline assembly, you want to only put the essentials into it:
    only do what is necessary and only bundle together what must be
    bundled.


    Yes.  For inline assembly, simple is not really important, but small is beautiful!  Say exactly what you mean, no more and no less, and let the compiler do what it does best.


    And move to C++20.  Then you can write (for the mythical madd
    instruction) :

    constexpr int mul_add(int x, int y, int z)
    __attribute__((const))
    {
      if (std::is_constant_evaluated()) {
        return x + y * z;
      } else {
        asm("madd %[x], %[y], %[z]"
            : [x] "+r" (x) : [y] "g" (y), [z] "g" (z));
        return x;
      }
    }


    Now the compiler can evaluate at compile time if the parameters are
    known, or using the super-efficient madd instruction at runtime if
    necessary.


    Apologies for replying to my own post, but of course this can be done in
    C with gcc extensions (and when we have inline assembly, we are already
    relying on gcc extensions):

    static inline int mul_add(int x, int y, int z)
    __attribute__((const))
    {
      if (__builtin_constant_p(x + y * z)) {
    return x + y * z;
    } else {
    asm("madd %[x], %[y], %[z]"
    : [x] "+r" (x) : [y] "g" (y), [z] "g" (z));
       return x;
    }
    }

    This is, in fact, even better than the C++20 version since it will
    optimise to compile-time computation if the result can be calculated at
    compile time, even if all the parameters are not "constant expressions".
    This code will be optimised to "return 0;" :-)

    int F4(int y, int z) {
    return mul_add(y * z, -y, z);
    }

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to bart on Mon Jan 15 17:35:29 2024
    bart <bc@freeuk.com> writes:
    On 15/01/2024 14:35, Scott Lurndal wrote:
    bart <bc@freeuk.com> writes:

    But you've also made it clear that this isn't really assembly at all. Is >>> it even for x64? I can't tell! The use of 'mov' rather than 'ldr' or
    'str' suggests it is x86 or x64 rather than ARM. But are those 32-bit
    mov's or 64-bit?

    1) it's not an assembler, it's a way to "patch" the assembler code
    generated by the compiler.


    So it's some sort of hideous hybrid that gcc has come up with, that is
    neither assembly nor C.

    Your baseless opinion noted. Feel free to submit a patch to
    the GCC team to implement your preferred syntax for inline assembler.

    Do ensure that it works for all 100 of the GCC target architectures.

    Which all share the same instruction set?

    No, of course not. The compiler syntax must be usable for all supported architectures. All the compiler does is pass it through to the
    assembler output after any parameter substitution. The compiler doesn't
    even look at the opcode.


    You're having a laugh, surely?

    No. I'm serious. [DWORD] is useless cruft.

    AT&T is bad enough even without the
    travesty of it displayed here:

    "jmp 2f\n"
    "3:\tmovsx %%bx, %%rbx\n\t"

    If you don't understand the standard C escapes, you really
    should go back to read the standard carefluly.



    What's with the strings, newline and tab escapes? What's that 'f' for?

    RTFM for the architecture dependent assembler that the compiler
    driver will end up calling to build the output object file.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to David Brown on Mon Jan 15 17:30:32 2024
    David Brown <david.brown@hesbynett.no> writes:
    On 15/01/2024 03:14, bart wrote:

    <snip>

    The only compiler I used with an inline
    assembly of comparable power and integration to gcc's, but a different >syntax, was Diab Data.

    Now there's a name I haven't heard in decades. What ever happened
    to them? We worked with them back in the early 90's using their
    88100 compiler. It was impressive, particularly compared to the
    PCC port that Motorola provided. Greenhills was ok, but diab
    produced better code. gcc was still pretty primitive in those days.

    A good Norweigian company.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to Gabriel Rolland on Mon Jan 15 17:41:23 2024
    Gabriel Rolland <gabrielrolland@gmail.com> writes:
    bart <bc@freeuk.com> writes:


    Here is where Unix/Linux's treatment of file extensions does my head
    in. 'a.out' is used there for both kinds of file. To find out what it
    actually is, you have to look inside the file, which defeats the
    purpose of having a file extension at all.

    The executable. Unix treatment of file extension is non existent,

    That is an understatement. A unix filename is a sequence of bytes
    with no semantics associated with them, other than by application
    convention.

    The largest security hole in Windows was its reliance on extension
    to identify file type regardless of content.

    This is a supposition on your part. If gcc/cc was working as you said,
    like outputting of hello.c hello.exe by default. In that case I would
    maybe have considered something like aliasing gcc thus :

    alias cc='gcc -o current.exe'

    or
    function cc
    {
    gcc -o "$1".exe "$1".c
    }

    as was posted upthread.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Scott Lurndal on Mon Jan 15 18:55:27 2024
    On 15/01/2024 17:35, Scott Lurndal wrote:
    bart <bc@freeuk.com> writes:

    You're having a laugh, surely?

    No. I'm serious. [DWORD] is useless cruft.

    I don't use [DWORD], whatever that means.

    Meanwhile %% in front of every register name, f after a label, and ""
    and \n and \t on every line is useful cruft!

    AT&T is bad enough even without the
    travesty of it displayed here:

    "jmp 2f\n"
    "3:\tmovsx %%bx, %%rbx\n\t"

    If you don't understand the standard C escapes, you really
    should go back to read the standard carefluly.

    I understand C escape codes. I am asking WHAT THE FUCK ARE THEY DOING IN
    EVERY LINE OF AN ASSEMBLY PROGRAM?

    You're doing this on purpose aren't you?




    What's with the strings, newline and tab escapes? What's that 'f' for?

    RTFM for the architecture dependent assembler that the compiler
    driver will end up calling to build the output object file.

    So you don't actually know.

    Just admit that my approach to inline assembler is better and give it up.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to bart on Mon Jan 15 19:12:08 2024
    bart <bc@freeuk.com> writes:
    On 15/01/2024 16:40, Gabriel Rolland wrote:
    bart <bc@freeuk.com> writes:

    This has been answered better than I could by David Brown.

    I'm not reading DB's comments today.

    Your loss.


    The executable. Unix treatment of file extension is non existent,
    true. So I don't bother with file extensions much. But I have yet to go
    on comp.os.windows to explain how confusing file extensions are.

    This is not a Linux newsgroup. It's a language group.

    But I expect you do use file extensions.

    Sure, for convenience, not necessity. Some very useful tools,
    such as make, have default rules for common file name suffixes,
    but there's nothing that prevents one from supplying their own
    rules.

    Otherwise any file in one of
    your folders be absolutely anything: text, source in any language,
    document, binary data, object file, executable ...

    It's a directory, not a folder.

    And yes, any file in a directory can be pretty much anything the
    user wants it to be. c_code_main is a perfectly legal name for
    a c source file while b235 is a perfectly legal name for the
    executable file generated by a C compiler from the source file
    c_code_main.


    I'm imagine if 'abc' was a C source file, you'd have to put the object
    file 'abc' in a separate folder, and the executable 'abc' in a third
    one? Otherwise they will clash.

    $ cat /tmp/z
    #include <stdio.h>

    int
    main(int argc, const char **argv, const char **envp, const char **auxv)
    {
    printf("Hello World\n");

    return 0;
    }
    $ cat /tmp/z | cc -x c -o a -
    $ ./a
    Hello World
    $

    Programs that deal with them will of course check that they are what
    they say they are.

    And how, exactly, will they do that for a text file?

    But because of this stupid quirk in gcc that could have been fixed in
    three lines of code, millions of people have to add extra instructions:

    gcc myprog.c -o myprog

    You keep beat a dead horse. Pointless and a waste of time.


    You don't get this in other languages.

    That's demonstrably false.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Gabriel Rolland on Mon Jan 15 18:41:54 2024
    On 15/01/2024 16:40, Gabriel Rolland wrote:
    bart <bc@freeuk.com> writes:

    This has been answered better than I could by David Brown.

    I'm not reading DB's comments today.

    The executable. Unix treatment of file extension is non existent,
    true. So I don't bother with file extensions much. But I have yet to go
    on comp.os.windows to explain how confusing file extensions are.

    This is not a Linux newsgroup. It's a language group.

    But I expect you do use file extensions. Otherwise any file in one of
    your folders be absolutely anything: text, source in any language,
    document, binary data, object file, executable ...

    I'm imagine if 'abc' was a C source file, you'd have to put the object
    file 'abc' in a separate folder, and the executable 'abc' in a third
    one? Otherwise they will clash.

    So extensions serve useful purposes for both machine, and human users.

    Programs that deal with them will of course check that they are what
    they say they are.


    This is a supposition on your part. If gcc/cc was working as you said,
    like outputting of hello.c hello.exe by default. In that case I would
    maybe have considered something like aliasing gcc thus :

    alias cc='gcc -o current.exe'

    So I wouldn't have that extra hassle.
    But that would belong to my
    '.profile' opinions and everybody can have his own '.profile' opinion
    about how gcc should work. I respect that.

    Suppose you write an application as myprog.c. You distribute it for
    people to build. The instructions could have been as simple as:

    gcc myprog.c

    and it will create myprog which can be invoked as ./myprog, or as
    myprog.exe which on Windows is invoked as just myprog.

    But because of this stupid quirk in gcc that could have been fixed in
    three lines of code, millions of people have to add extra instructions:

    gcc myprog.c -o myprog

    You don't get this in other languages.

    It is just Wrong.

    Hardcoding the name of an output file is something a beginner might do,
    or in the early stages of an an application (I do that myself).

    But that program version is never going to see the light of day. So how
    the hell did that idiotic behaviour of gcc (and the worse one of as)
    ever escape into the wild? QoI was lacking.

    Meanwhile everybody is defending it, and there are even people like you
    saying that is an advantage!

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Scott Lurndal on Mon Jan 15 19:32:49 2024
    On 15/01/2024 19:12, Scott Lurndal wrote:
    bart <bc@freeuk.com> writes:
    On 15/01/2024 16:40, Gabriel Rolland wrote:
    bart <bc@freeuk.com> writes:

    This has been answered better than I could by David Brown.

    I'm not reading DB's comments today.

    Your loss.


    The executable. Unix treatment of file extension is non existent,
    true. So I don't bother with file extensions much. But I have yet to go
    on comp.os.windows to explain how confusing file extensions are.

    This is not a Linux newsgroup. It's a language group.

    But I expect you do use file extensions.

    Sure, for convenience, not necessity. Some very useful tools,
    such as make, have default rules for common file name suffixes,
    but there's nothing that prevents one from supplying their own
    rules.

    Otherwise any file in one of
    your folders be absolutely anything: text, source in any language,
    document, binary data, object file, executable ...

    It's a directory, not a folder.

    And yes, any file in a directory can be pretty much anything the
    user wants it to be. c_code_main is a perfectly legal name for
    a c source file

    True:

    c:\c>copy hello.c c_code_main
    1 file(s) copied.

    c:\c>mcc c_code_main.
    Compiling c_code_main. to c_code_main.exe

    Although some compilers appear to have a problem with it:

    c:\c>gcc c_code_main.
    C:\tdm\bin\ld.exe:c_code_main.: file format not recognized; treating
    as linker script
    C:\tdm\bin\ld.exe:c_code_main.:2: syntax error
    collect2.exe: error: ld returned 1 exit status

    They still need that scrap of info that was imparted by the extension,
    either as .c or as -xc

    while b235 is a perfectly legal name for the
    executable file generated by a C compiler from the source file
    c_code_main.

    It might well be. On Linux of course they tend not to have extensions,
    for a very simple reason: it would get rapidly tedious to have to type,
    many 1000s of times:

    ./b235.elf

    or whatever extension is chosen.



    I'm imagine if 'abc' was a C source file, you'd have to put the object
    file 'abc' in a separate folder, and the executable 'abc' in a third
    one? Otherwise they will clash.

    $ cat /tmp/z
    #include <stdio.h>

    int
    main(int argc, const char **argv, const char **envp, const char **auxv)
    {
    printf("Hello World\n");

    return 0;
    }
    $ cat /tmp/z | cc -x c -o a -
    $ ./a
    Hello World
    $

    Not sure what you're demonstrating here. Except how you're evading the
    issue by avoiding intermediate files altogether.

    Brilliant. Not sure how it plays with makefiles though which seem to
    deal extensively with .o files.


    Programs that deal with them will of course check that they are what
    they say they are.

    And how, exactly, will they do that for a text file?

    How does Linux deal with that same problem?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to bart on Mon Jan 15 20:12:22 2024
    bart <bc@freeuk.com> writes:
    On 15/01/2024 19:12, Scott Lurndal wrote:


    Programs that deal with them will of course check that they are what
    they say they are.

    And how, exactly, will they do that for a text file?

    How does Linux deal with that same problem?

    Linux doesn't.

    The gnu C compiler will produce errors if the input isn't valid C,
    I suppose you could call that checking that they are what
    they say they are.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to bart on Mon Jan 15 19:19:21 2024
    bart <bc@freeuk.com> writes:
    On 15/01/2024 17:35, Scott Lurndal wrote:
    bart <bc@freeuk.com> writes:

    You're having a laugh, surely?

    No. I'm serious. [DWORD] is useless cruft.

    I don't use [DWORD], whatever that means.

    Meanwhile %% in front of every register name, f after a label, and ""
    and \n and \t on every line is useful cruft!

    AT&T is bad enough even without the
    travesty of it displayed here:

    "jmp 2f\n"
    "3:\tmovsx %%bx, %%rbx\n\t"

    If you don't understand the standard C escapes, you really
    should go back to read the standard carefluly.

    I understand C escape codes. I am asking WHAT THE FUCK ARE THEY DOING IN >EVERY LINE OF AN ASSEMBLY PROGRAM?

    Even you included them in the example you posted (and elided from this reply).

    I include the C escape codes (they're not necessary for proper compilation) to make
    the intermediate assembler output more readable, in the unlikely event
    that one might need to look at it for some reason. Force of long-standing habit.

    You can certainly leave them out if you don't want them. They're not required. But you'd know that if you had bothered to read the
    documentation.



    What's with the strings, newline and tab escapes? What's that 'f' for?

    Assemblers for fifty years have used numeric temporary labels. So that
    the target can be before or after the branch instruction, one may suffix
    the branch label with 'f' or 'b' on a branch-class instruction. The
    GNU gas assembler is hardly the first assembler to support such syntax.

    But, I suspect you already know this, and may even have been able to
    figure it out from context. But that wouldn't fit your trolling
    narrative.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Scott Lurndal on Mon Jan 15 21:25:53 2024
    On 15/01/2024 18:30, Scott Lurndal wrote:
    David Brown <david.brown@hesbynett.no> writes:
    On 15/01/2024 03:14, bart wrote:

    <snip>

    The only compiler I used with an inline
    assembly of comparable power and integration to gcc's, but a different
    syntax, was Diab Data.

    Now there's a name I haven't heard in decades. What ever happened
    to them? We worked with them back in the early 90's using their
    88100 compiler. It was impressive, particularly compared to the
    PCC port that Motorola provided. Greenhills was ok, but diab
    produced better code. gcc was still pretty primitive in those days.

    A good Norweigian company.

    A good /Swedish/ company, not Norwegian!

    The compiler was bought by Wind River, and I don't think it is very
    relevant now.

    I only ever got to play with the compiler for a short while, back in the
    late 1990's, for the m68k. Our company couldn't afford it at the time
    as it was /not/ cheap. But it had powerful optimisation for its day.

    I've never been much fond of Greenhills, though I have not had much use
    of it (some customers have).

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to bart on Mon Jan 15 21:16:39 2024
    On 15/01/2024 19:41, bart wrote:
    On 15/01/2024 16:40, Gabriel Rolland wrote:
    bart <bc@freeuk.com> writes:

    This has been answered better than I could by David Brown.

    I'm not reading DB's comments today.

    Maybe you will look later - or maybe others will find them interesting.
    I am clear in my opinions in my posts, but I have also written a great
    deal that is primarily to help you. If you don't pay attention to that,
    it's not going to help your reputation of being wilfully ignorant and disregarding help and advice.


    The executable. Unix treatment of file extension is non existent,
    true. So I don't bother with file extensions much. But I have yet to go
    on comp.os.windows to explain how confusing file extensions are.

    This is not a Linux newsgroup. It's a language group.

    So why are you concerned here with Linux's use, or lack thereof, of file extensions?


    But I expect you do use file extensions. Otherwise any file in one of
    your folders be absolutely anything: text, source in any language,
    document, binary data, object file, executable ...

    I'm imagine if 'abc' was a C source file, you'd have to put the object
    file 'abc' in a separate folder, and the executable 'abc' in a third
    one? Otherwise they will clash.

    So extensions serve useful purposes for both machine, and human users.


    Sure, file extensions can be very useful. They can also be deceiving,
    which is why it is a huge security hole in Windows (though I'd hesitate
    to agree with Scott that it is the /biggest/ hole in Windows) to treat
    them as so important. It tells me something when a file has .txt, or
    .c, or .md at the end. It also gives programs useful information if
    they have to deal with multiple file types that cannot be identified
    clearly. But it is not something a system should rely on for something important, and programs should allow file extensions to be overridden.

    Thus gcc will assume "file.c" is a C file, while "file.f" is a Fortran
    file. But you can override that if you want. And file extension is
    irrelevant to *nix OS's for identifying if a file should be executed or
    not. Everyone wins, no one loses - unlike the Windows user who
    double-clicks on a malware file called "Readme.txt.exe" and the OS has "helpfully" hidden the ".exe" bit.

    Suppose you write an application as myprog.c. You distribute it for
    people to build. The instructions could have been as simple as:

        gcc myprog.c

    and it will create myprog which can be invoked as ./myprog, or as
    myprog.exe which on Windows is invoked as just myprog.


    Yes, gcc could have been made that way. I expect people would have been
    fine with that. But it's not made that way, and most people seem fine
    with what gcc actually does. I doubt there are many people who think defaulting to a.out is a particular advantage these days (presumably it
    was a good idea historically), but I doubt that there are many who are
    bothered by it.

    But because of this stupid quirk in gcc that could have been fixed in
    three lines of code, millions of people have to add extra instructions:

        gcc myprog.c -o myprog

    You don't get this in other languages.

    gcc is not a language. But without having tried every other compiler
    (for C or other languages), I'd be loath to claim there are no other
    compilers that also have a default output file. I don't think you have
    either.

    I believe, however, that I can honestly say I don't know if any of the
    other C compilers (or linkers, as that's more accurate) I have used over
    the decades have a default output file, and what it might be. I've
    always specified the output filename I want.


    It is just Wrong.

    Hardcoding the name of an output file is something a beginner might do,
    or in the early stages of an an application (I do that myself).

    But that program version is never going to see the light of day. So how
    the hell did that idiotic behaviour of gcc (and the worse one of as)
    ever escape into the wild? QoI was lacking.


    Do you not think that someone else might have noticed if it were such an
    issue? Have you ever heard anyone else complain? gcc is something like
    four decades old, and *nix and C compilers are another decade older.

    Meanwhile everybody is defending it, and there are even people like you saying that is an advantage!


    Maybe to some people it /is/ an advantage. Have you ever considered that?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to David Brown on Mon Jan 15 20:41:22 2024
    David Brown <david.brown@hesbynett.no> writes:
    On 15/01/2024 18:30, Scott Lurndal wrote:
    David Brown <david.brown@hesbynett.no> writes:
    On 15/01/2024 03:14, bart wrote:

    <snip>

    The only compiler I used with an inline
    assembly of comparable power and integration to gcc's, but a different
    syntax, was Diab Data.

    Now there's a name I haven't heard in decades. What ever happened
    to them? We worked with them back in the early 90's using their
    88100 compiler. It was impressive, particularly compared to the
    PCC port that Motorola provided. Greenhills was ok, but diab
    produced better code. gcc was still pretty primitive in those days.

    A good Norweigian company.

    A good /Swedish/ company, not Norwegian!

    Hm. I could have sworn the folks we dealt with were
    in Norway - perhaps a branch office?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Chris M. Thomasson on Mon Jan 15 23:24:07 2024
    On 2024-01-15, Chris M. Thomasson <chris.m.thomasson.1@gmail.com> wrote:
    On 1/15/2024 8:29 AM, David Brown wrote:
    On 15/01/2024 17:04, David Brown wrote:
    On 15/01/2024 08:40, Kaz Kylheku wrote:

    A mul_add primitive might make sense if the processor had such a thing >>>> in one instruction.

    With GCC inline assembly, you want to only put the essentials into it: >>>> only do what is necessary and only bundle together what must be
    bundled.


    Yes.  For inline assembly, simple is not really important, but small
    is beautiful!  Say exactly what you mean, no more and no less, and let
    the compiler do what it does best.
    [...]

    Wrt inline GCC assembly, remember __volatile__ and memory? ;^)

    The volatile is only important if you're writing sync primitives, so the compiler won't move loads or stores to one side or the other of the
    inline assembly. If you're just doing some calculation, it's
    counterproductive to tell the compiler not to move the code.

    "memory" is only needed if your inline code clobbers memory. E.g.
    one of the operands is a pointer, and the code writes through it.
    That location isn't one of the output operands.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to bart on Mon Jan 15 23:28:34 2024
    On 2024-01-15, bart <bc@freeuk.com> wrote:
    c:\c>copy hello.c c_code_main
    1 file(s) copied.

    There goes that damned AT&T instruction syntax for file copying, same
    like in Unix.

    Doesn't it bother you that it isn't: copy destination source?

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Kaz Kylheku on Tue Jan 16 00:04:35 2024
    On 15/01/2024 23:28, Kaz Kylheku wrote:
    On 2024-01-15, bart <bc@freeuk.com> wrote:
    c:\c>copy hello.c c_code_main
    1 file(s) copied.

    There goes that damned AT&T instruction syntax for file copying, same
    like in Unix.

    Doesn't it bother you that it isn't: copy destination source?

    I haven't said anything about in which direction the data goes.

    This is something that tends to depend on device, so Motorola went left
    to right, and Zilog/Intel went right to left.

    So it did seem odd for this x86 assembler to do the opposite of Intel.

    However, doesn't it bother /you/ that AT&T also does the opposite of not
    only how assignment works in C, but in most languages? That would be
    more pertinent than somebody's choices of command-line syntax.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to bart on Tue Jan 16 01:21:18 2024
    On 2024-01-15, bart <bc@freeuk.com> wrote:
    On 15/01/2024 17:35, Scott Lurndal wrote:
    bart <bc@freeuk.com> writes:

    You're having a laugh, surely?

    No. I'm serious. [DWORD] is useless cruft.

    I don't use [DWORD], whatever that means.

    Meanwhile %% in front of every register name, f after a label, and ""
    and \n and \t on every line is useful cruft!

    AT&T is bad enough even without the
    travesty of it displayed here:

    "jmp 2f\n"
    "3:\tmovsx %%bx, %%rbx\n\t"

    If you don't understand the standard C escapes, you really
    should go back to read the standard carefluly.

    I understand C escape codes. I am asking WHAT THE FUCK ARE THEY DOING IN EVERY LINE OF AN ASSEMBLY PROGRAM?

    The instruction template is just a string literal to the
    compiler. It specifies text to be inserted into the assembly
    output.

    Some assembly languages require the whitespace; you need
    instructions to be on separate lines.

    GCC does not look inside this template other than to replace
    % codes like %0 (the first register).

    In my example, I put the newlines and tabs together on the right

    "imul %3, %2\n\t"
    "add %1, %2\n\t"
    "mov %1, %0\n\t"

    Thanks to these newlines and tabs, the textual output (generated .s
    file if we use gcc -S) has this in it:

    #APP
    # 24 "inline.c" 1
    imul %edx, %esi
    add %esi, %edi
    mov %edi, %edi

    # 0 "" 2
    #NO_APP
    movl 8(%rsp), %eax
    movl 16(%rsp), %edx

    GCC's code is using eight spaces; mine is tabs. The indentation on
    imul is spaces: that was generated by GCC. You don't have to
    indent your first instruction.

    You can understand why GCC went with this textual templating approach, since the number of back-end assembly languages is astonishing. In some cases, I think, GCC supports more than one assembler syntax for the same architecture. Historically it has worked with assemblers that didn't come from GNU.

    It would not be practical for GNU C to have a bazillino assembly language syntaxes in its grammar.

    Just admit that my approach to inline assembler is better and give it up.

    Your approach to inline assembler has nicer looking syntax, but
    semantically, it doesn't seem to be on same level as the GCC approach.

    GCC's inline assembly does nice things that are might not be possible in your implementation. (Or pretty much anyone else's).

    Some of it has been sufficiently exemplified elsewhere in the thread.

    GCC can choose registers for your inline assembly block, and will automatically move between those registers and the operands they are connected to (if necessary). The inline code seamlessly integrates with the code generated by the compiler. You can write primitives that generate code as well as compiler built-ins.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Kaz Kylheku on Tue Jan 16 11:30:56 2024
    On 16/01/2024 01:21, Kaz Kylheku wrote:
    On 2024-01-15, bart <bc@freeuk.com> wrote:

    [Inline assembly]

    The instruction template is just a string literal to the
    compiler. It specifies text to be inserted into the assembly
    output.

    Some assembly languages require the whitespace; you need
    instructions to be on separate lines.

    GCC does not look inside this template other than to replace
    % codes like %0 (the first register).

    In my example, I put the newlines and tabs together on the right

    "imul %3, %2\n\t"
    "add %1, %2\n\t"
    "mov %1, %0\n\t"

    Thanks to these newlines and tabs, the textual output (generated .s
    file if we use gcc -S) has this in it:

    #APP
    # 24 "inline.c" 1
    imul %edx, %esi
    add %esi, %edi
    mov %edi, %edi

    # 0 "" 2
    #NO_APP
    movl 8(%rsp), %eax
    movl 16(%rsp), %edx


    This is still peculiar: why prioritise the appearance of the
    intermediate code which I assume you're rarely going to look at?

    It's the version with strings and escape codes that you're going to be
    writing and maintaining, and that people will see in the C sources!

    This is akin to a language allowing embedded C but you have to write it
    like this:

    clang{"\tprintf(\"A=%%d\\n\", a);\n"};

    so that the generated version looks like:

    printf("A=%d\n", a);

    Except you can't refer to 'a' directly, you have to write it as %0 and
    then have some extra mechanism to somehow map that to 'a'.

    You can understand why GCC went with this textual templating approach, since the number of back-end assembly languages is astonishing. In some cases, I think, GCC supports more than one assembler syntax for the same architecture. Historically it has worked with assemblers that didn't come from GNU.

    gcc is a project 10s of 1000s of files. If a particular configuration
    targets one architecture out of 100, it can also support the ASM syntax
    for that architecture.

    You don't need to have the ASM syntax embedded within the C grammar. Not
    so specifically anyway; you allow a bunch of keyword and register tokens
    within asm{...}.

    Yes it's a bit harder, but if I can do it within a 0.3MB product, gcc
    can do it within 24MB.

    It would not be practical for GNU C to have a bazillino assembly language syntaxes in its grammar.

    Just admit that my approach to inline assembler is better and give it up.

    Your approach to inline assembler has nicer looking syntax, but
    semantically, it doesn't seem to be on same level as the GCC approach.

    GCC's inline assembly does nice things that are might not be possible in your implementation. (Or pretty much anyone else's).

    Some of it has been sufficiently exemplified elsewhere in the thread.

    GCC can choose registers for your inline assembly block,

    The point of ASM is that /you/ call the shots.

    It's not just more natural syntax, but better integration within the
    host language. More example, I can 'goto' to a label within an ASSEM
    block, and 'jmp' out of the ASSEM block to a HLL label, or to an label
    within a separate ASSEM block further on.

    and will automatically
    move between those registers and the operands they are connected to (if necessary). The inline code seamlessly integrates with the code generated by the compiler. You can write primitives that generate code as well as compiler
    built-ins.

    OK. But it's not an 'assembler' as is generally understood. Mine is; it
    looks exactly line normal assembly, and it is written inline to the HLL.

    Although these days I keep such hybrid functions within their own modules.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to David Brown on Tue Jan 16 11:54:10 2024
    On 15/01/2024 16:29, David Brown wrote:
    On 15/01/2024 17:04, David Brown wrote:
    On 15/01/2024 08:40, Kaz Kylheku wrote:

    A mul_add primitive might make sense if the processor had such a thing
    in one instruction.

    With GCC inline assembly, you want to only put the essentials into it:
    only do what is necessary and only bundle together what must be
    bundled.


    Yes.  For inline assembly, simple is not really important, but small
    is beautiful!  Say exactly what you mean, no more and no less, and let
    the compiler do what it does best.


    And move to C++20.  Then you can write (for the mythical madd
    instruction) :

    constexpr int mul_add(int x, int y, int z)
    __attribute__((const))
    {
      if (std::is_constant_evaluated()) {
         return x + y * z;
       } else {
         asm("madd %[x], %[y], %[z]"
             : [x] "+r" (x) : [y] "g" (y), [z] "g" (z));
        return x;
       }
    }


    Now the compiler can evaluate at compile time if the parameters are
    known, or using the super-efficient madd instruction at runtime if
    necessary.


    Apologies for replying to my own post, but of course this can be done in
    C with gcc extensions (and when we have inline assembly, we are already relying on gcc extensions):

    static inline int mul_add(int x, int y, int z)
    __attribute__((const))
    {
      if (__builtin_constant_p(x + y * z)) {
         return x + y * z;
      } else {
         asm("madd %[x], %[y], %[z]"

    Which processor is this for again?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to David Brown on Tue Jan 16 12:26:00 2024
    On 15/01/2024 15:23, David Brown wrote:
    On 15/01/2024 03:14, bart wrote:

    You pass the relevant data into and out of the inline assembly.  If you think you need access to other symbols in the assembly, you are (almost certainly) doing things wrong.  You are trying to do the compiler's job behind its back, and that is not a good idea.

    Not with gcc. You don't want to mess with that.

    But /I/ use inline assembler when /I/ in in charge of the code.

    If /I/ had to write extensive programs in gcc inline assembly, then
    put a gun to my head now!

    If you are trying to write extensive programs in assembly, you are
    already getting it wrong.

    I want to write HLL functions that may have a number of lines in
    assembly, from one line up to a few dozen.

      Inline assembly is for things that cannot be
    expressed in high level languages, or the very rare occasions where you
    know a way to do something in assembly that is very much more efficient
    than the compiler can generate, and the code is speed critical, and
    there are no built-ins for the task, and no target intrinsics provided
    by the processor manufacturer.


    Take this example in C:

        int a;

        void F(void) {
             int b=2, c=3;
             static int d=4;

             a = b + c * d;
        }

    I will now show it in my language but with that assignment replaced by
    inline assembly:

         int a

         proc F=
             int b:=2, c:=3
             static int d=4

             assem
                 mov   rax, [c]               # (note my ints are 64 bits)
                 imul2 rax, [d]
                 add   rax, [b]
                 mov   [a], rax
             end
         end

    My question is: what would the C version look like with that line in
    gcc inline assembly? (In both cases, 'a' should end up with the value
    14.)

    void F(void) {
        int b = 2:
        int c = 3;
        static int d = 4;

        asm ("imul2 %[c], %[d]\n\t"
             "add %[c], %[b]"
            : [c] "+g" (c) : [b] "g" (b), [d] "g" (d));
        a = c;
    }

    Sorry, but you've turned it into gobbledygook. My example was for x64
    which is a 1.5 address machine, here you've turned it into a 2-address
    machine. Could I make it 3-address? What are the rules?

    It is a different language.


    The generated result (from <https://godbolt.org>) is :

    F():
            mov     eax, 3
            imul2 eax, 4
            add eax, 2
            mov     DWORD PTR a[rip], eax
            ret

    The initialisations I used were so I could test that it gave the correct results. Without them, godbolt gives me this for the body of the function:


    movl d.0(%rip), %edx
    movl -8(%rbp), %eax
    imul2 %eax, %edx
    add %eax, -4(%rbp)
    movl %eax, -8(%rbp)
    movl -8(%rbp), %eax
    movl %eax, a(%rip)


    My version (which evaluates a=b+c*d; somehow your version modifies c)
    gives me this (D0 == rax):

    mov D0, [Dframe+test.f.c]
    imul2 D0, [test.f.d]
    add D0, [Dframe+test.f.b]
    mov [test.a], D0

    Unsurprisingly, this is exactly the ASM I typed (plus the necessary name qualifiers). That is the entire point.

    If I tweak your C version, make a,b,c,d all external statics, and apply
    -O3, godbolt gives me this:

    imul2 c(%rip), d(%rip)
    add c(%rip), b(%rip)
    movl c(%rip), %eax
    movl %eax, a(%rip)
    ret

    This is slightly worrying: imul2 is not a valid instruction (it's
    specific to my assembler). While add can't take two memory operands.

    So it looks like it can only do so much checking. (Using gcc locally
    gave valid assembly.)

    So it all seems hit and miss.

    I'll reply to the second half of your post later.

    Let me just say that, in my interpreter, the extensive use of inline
    assembly in one module, makes some programs run twice as fast, as a gcc-O3-compiled C rendering.

    It also lets me write trivial solutions to the LIBFFI problem.

    It works.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Chris M. Thomasson on Tue Jan 16 14:24:19 2024
    On 15/01/2024 21:27, Chris M. Thomasson wrote:
    On 1/15/2024 8:29 AM, David Brown wrote:
    On 15/01/2024 17:04, David Brown wrote:
    On 15/01/2024 08:40, Kaz Kylheku wrote:

    A mul_add primitive might make sense if the processor had such a thing >>>> in one instruction.

    With GCC inline assembly, you want to only put the essentials into it: >>>> only do what is necessary and only bundle together what must be
    bundled.


    Yes.  For inline assembly, simple is not really important, but small
    is beautiful!  Say exactly what you mean, no more and no less, and
    let the compiler do what it does best.
    [...]

    Wrt inline GCC assembly, remember __volatile__ and memory? ;^)


    Yes, these are useful, and I use them - when appropriate.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to bart on Tue Jan 16 14:22:37 2024
    On 16/01/2024 01:04, bart wrote:
    On 15/01/2024 23:28, Kaz Kylheku wrote:
    On 2024-01-15, bart <bc@freeuk.com> wrote:
         c:\c>copy hello.c c_code_main
              1 file(s) copied.

    There goes that damned AT&T instruction syntax for file copying, same
    like in Unix.

    Doesn't it bother you that it isn't: copy destination source?

    I haven't said anything about in which direction the data goes.

    This is something that tends to depend on device, so Motorola went left
    to right, and Zilog/Intel went right to left.

    So it did seem odd for this x86 assembler to do the opposite of Intel.

    However, doesn't it bother /you/ that AT&T also does the opposite of not
    only how assignment works in C, but in most languages? That would be
    more pertinent than somebody's choices of command-line syntax.


    AFAIUI, AT&T syntax was first for the PDP11, which used left to right direction. It was later modified and adopted for x86 before Intel had
    actually made a good assembly syntax (AFAIK).

    I think it has become more common to use right-to-left ordering for
    assemblies, but there has never been a real consensus.

    In the early days, different groups made their own assemblers, and
    sometimes their own assembly language or mnemonics. Certainly they
    would use their own choice of directives and other syntax, keeping them
    the same as they had for other targets. And if you already make
    assemblers that have left-to-right syntax, then it makes sense to keep
    that for new targets - it means more re-use of your assembler source
    code, and more familiarity for users who deal with multiple targets.
    The majority of people working on assembly code for the x86 on *nix
    systems (at least early on) would have been familiar with the AT&T
    syntax for PDP11, m68k, or other processors - keeping the same style for
    x86 made their life easier.

    Given a completely free choice, I'd probably choose Intel format for x86
    rather than AT&T. But since I don't write x86 assembly, and can read
    either format on godbolt, I don't mind much either way.

    And given a completely free choice to design a new assembly language for
    a processor (pre-existing or new), I think I'd go for a right-to-left assignment. As you say, it fits better with most programming languages
    (other than Forth, Cobol, and some programmable calculator languages).

    As for other things like the % before register names - well, you either
    have to have special markings for register names or you have to have
    special marking for external symbols (at least those from HLL's that
    don't carefully avoid clashes with register names). No choice is going
    to be perfect for everyone's tastes.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Chris M. Thomasson on Tue Jan 16 14:38:38 2024
    On 16/01/2024 03:18, Chris M. Thomasson wrote:
    On 1/15/2024 3:24 PM, Kaz Kylheku wrote:
    On 2024-01-15, Chris M. Thomasson <chris.m.thomasson.1@gmail.com> wrote:
    On 1/15/2024 8:29 AM, David Brown wrote:
    On 15/01/2024 17:04, David Brown wrote:
    On 15/01/2024 08:40, Kaz Kylheku wrote:

    A mul_add primitive might make sense if the processor had such a
    thing
    in one instruction.

    With GCC inline assembly, you want to only put the essentials into >>>>>> it:
    only do what is necessary and only bundle together what must be
    bundled.


    Yes.  For inline assembly, simple is not really important, but small >>>>> is beautiful!  Say exactly what you mean, no more and no less, and let >>>>> the compiler do what it does best.
    [...]

    Wrt inline GCC assembly, remember __volatile__ and memory? ;^)

    The volatile is only important if you're writing sync primitives, so the
    compiler won't move loads or stores to one side or the other of the
    inline assembly.

    That is not what "volatile" means - either in normal C code, or for gcc
    inline assembly!

    If you're just doing some calculation, it's
    counterproductive to tell the compiler not to move the code.

    "memory" is only needed if your inline code clobbers memory. E.g.
    one of the operands is a pointer, and the code writes through it.
    That location isn't one of the output operands.


    I want my sync primitive code to be _un_abused by any "clever"
    optimizations. I want it is as it is.

    What a strange idea!

    I want the compiler to be clever and optimise code. But I want to tell
    the compiler enough about my inline assembly so that it can optimise appropriately.

    For synchronisation inline assembly, a "memory" clobber is often
    required - but usually it is not enough on its own (you need memory
    barrier assembly instructions too). And for lots of other uses of
    inline assembly, you don't want memory clobbers as they can be costly - sometimes very wasteful.

    "volatile" on inline assembly tells the compiler that the code may use
    or cause side-effects other than just what it is told by the inputs,
    outputs and clobbers. It will enforce an ordering with respect to other volatile inline assembly, and other C volatile accesses - but it will
    not be ordered with respect to other calculations, non-volatile inline assembly, or non-volatile loads and stores.

    You might think that using "volatile" and a "memory" clobber stops any re-ordering of code in relation to your inline assembly, but you'd be
    wrong. And if you think that, then perhaps an external assembly file is
    the easiest way to get the guarantees you want.

    No rouge compiler thinking that
    link-time optimizations are all fun and joy, lets dance around the sync instructions... Step on them, ruin them, make them incorrect. Even GCC
    had a nasty error with an optimization wrt Pthread
    pthread_mutex_trylock(). Simply ruined is correctness wrt the standard.


    You've mentioned this many times - do you have a reference that gives
    the source of this function (at the time when there was an issue), and a description or report of what you think gcc did wrong? I am curious as
    to whether it was a bug in the code or a bug in gcc (gcc is certainly
    not bug-free).

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to bart on Tue Jan 16 14:42:27 2024
    On 16/01/2024 12:54, bart wrote:
    On 15/01/2024 16:29, David Brown wrote:
    On 15/01/2024 17:04, David Brown wrote:
    On 15/01/2024 08:40, Kaz Kylheku wrote:

    A mul_add primitive might make sense if the processor had such a thing >>>> in one instruction.

    With GCC inline assembly, you want to only put the essentials into it: >>>> only do what is necessary and only bundle together what must be
    bundled.


    Yes.  For inline assembly, simple is not really important, but small
    is beautiful!  Say exactly what you mean, no more and no less, and
    let the compiler do what it does best.


    And move to C++20.  Then you can write (for the mythical madd
    instruction) :

    constexpr int mul_add(int x, int y, int z)
    __attribute__((const))
    {
      if (std::is_constant_evaluated()) {
         return x + y * z;
       } else {
         asm("madd %[x], %[y], %[z]"
             : [x] "+r" (x) : [y] "g" (y), [z] "g" (z));
        return x;
       }
    }


    Now the compiler can evaluate at compile time if the parameters are
    known, or using the super-efficient madd instruction at runtime if
    necessary.


    Apologies for replying to my own post, but of course this can be done
    in C with gcc extensions (and when we have inline assembly, we are
    already relying on gcc extensions):

    static inline int mul_add(int x, int y, int z)
    __attribute__((const))
    {
      if (__builtin_constant_p(x + y * z)) {
          return x + y * z;
       } else {
          asm("madd %[x], %[y], %[z]"

    Which processor is this for again?


    It's for a cpu with a "madd" instruction that implements "x = x + y * z"
    in a single instruction - as Kaz pointed out, doing this in inline
    assembly would make sense if it the cpu had such a dedicated
    instruction. Inline assembly is typically used in such situations,
    where the processor has a more efficient instruction sequence than the
    compiler can generate.

    But this mythical cpu uses Intel-style assembly syntax, for your
    convenience :-)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to bart on Tue Jan 16 15:06:10 2024
    On 16/01/2024 12:30, bart wrote:
    On 16/01/2024 01:21, Kaz Kylheku wrote:
    On 2024-01-15, bart <bc@freeuk.com> wrote:

    [Inline assembly]

    The instruction template is just a string literal to the
    compiler. It specifies text to be inserted into the assembly
    output.

    Some assembly languages require the whitespace; you need
    instructions to be on separate lines.

    GCC does not look inside this template other than to replace
    % codes like %0 (the first register).

    In my example, I put the newlines and tabs together on the right

       "imul %3, %2\n\t"
       "add %1, %2\n\t"
       "mov %1, %0\n\t"

    Thanks to these newlines and tabs, the textual output (generated .s
    file if we use gcc -S) has this in it:

    #APP
    # 24 "inline.c" 1
             imul %edx, %esi
             add %esi, %edi
             mov %edi, %edi

    # 0 "" 2
    #NO_APP
             movl    8(%rsp), %eax
             movl    16(%rsp), %edx


    This is still peculiar: why prioritise the appearance of the
    intermediate code which I assume you're rarely going to look at?

    It's often just habit. The tab character is entirely unnecessary, at
    least for gas (other assemblers may complain about instructions
    appearing at the start of the line). You do need some kind of
    separation between the assembly instructions - if you don't use "\n",
    then you could also use ";" (again, I know it works for gas). "\n\t" is
    a common habit, however, and has the benefit of looking nice if you
    examine the generated assembly - and if you are working with inline
    assembly, sometimes that is a very useful thing to check.


    It's the version with strings and escape codes that you're going to be writing and maintaining, and that people will see in the C sources!


    Well, you don't often write inline assembly - its rare to write it.
    It's typically the kind of thing you write once for your particular instruction, then stick it away in a header somewhere. You might use it
    often, but you don't need to read or edit the code often.

    In reality, you don't even often write the inline assembly yourself -
    you include a manufacturer-supplied header full of "intrinsics" - small
    inline assembly functions wrapping useful assembly instructions.

    The whole thing would be nicer if C (or at least gcc) supported a
    multi-line string syntax akin to Python's """ ... """ syntax. But it
    doesn't. (Actually, such an extension could be very useful for lots of things.)

    Just admit that my approach to inline assembler is better and give it
    up.

    Your approach to inline assembler has nicer looking syntax, but
    semantically, it doesn't seem to be on same level as the GCC approach.

    GCC's inline assembly does nice things that are might not be possible
    in your
    implementation. (Or pretty much anyone else's).

    Some of it has been sufficiently exemplified elsewhere in the thread.

    GCC can choose registers for your inline assembly block,

    The point of ASM is that /you/ call the shots.

    No.

    The point of inline assembly is to do something that you can't do in C
    alone (even with compiler extensions).


    It's not just more natural syntax, but better integration within the
    host language. More example, I can 'goto' to a label within an ASSEM
    block, and 'jmp' out of the ASSEM block to a HLL label, or to an label
    within a separate ASSEM block further on.


    You can "goto" C labels in gcc inline assembly. I can't imagine much real-world use for it, but you can do it. (You use "asm goto" and pass
    the labels into the assembly expression.)

    I think you are viewing inline assembly as a verbatim list of
    instructions inserted blindly into the code generated by the compiler.
    I view it more like an expression that fits into the flow of the C code,
    and the flow of the generated code. It is working /with/ the compiler,
    not /against/ it.

    and will automatically
    move between those registers and the operands they are connected to (if
    necessary). The inline code seamlessly integrates with the code
    generated by
    the compiler.  You can write primitives that generate code as well as
    compiler
    built-ins.

    OK. But it's not an 'assembler' as is generally understood. Mine is; it
    looks exactly line normal assembly, and it is written inline to the HLL.


    Exactly. gcc inline assembly is inline assembly - it is not an
    assembler. (gcc also supports what it calls "basic assembly syntax" -
    you create a "naked" function and give a list of assembly instructions.
    This is typically used for things like specialised startup code in a microcontroller, or for task switching in an RTOS.)

    Although these days I keep such hybrid functions within their own modules.


    It's always good to keep things like this tidied away.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Janis Papanagnou@21:1/5 to David Brown on Tue Jan 16 15:08:13 2024
    On 16.01.2024 14:42, David Brown wrote:
    On 16/01/2024 12:54, bart wrote:

    Which processor is this for again?

    It's for a cpu with a "madd" instruction that implements "x = x + y * z"
    in a single instruction - as Kaz pointed out, doing this in inline
    assembly would make sense if it the cpu had such a dedicated
    instruction. [...]

    I recall such a feature from a 35+ years old assembler project I did.
    It was on the TI TMS320C25 DSP, and the instruction was called 'MAC'
    (Multiply and ACcumulate). - Not sure it clarifies anything but just
    as an amendment if someone is interested in searching for keywords
    on such a function.

    Janis

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Scott Lurndal on Tue Jan 16 15:08:46 2024
    On 15/01/2024 21:41, Scott Lurndal wrote:
    David Brown <david.brown@hesbynett.no> writes:
    On 15/01/2024 18:30, Scott Lurndal wrote:
    David Brown <david.brown@hesbynett.no> writes:
    On 15/01/2024 03:14, bart wrote:

    <snip>

    The only compiler I used with an inline
    assembly of comparable power and integration to gcc's, but a different >>>> syntax, was Diab Data.

    Now there's a name I haven't heard in decades. What ever happened
    to them? We worked with them back in the early 90's using their
    88100 compiler. It was impressive, particularly compared to the
    PCC port that Motorola provided. Greenhills was ok, but diab
    produced better code. gcc was still pretty primitive in those days.

    A good Norweigian company.

    A good /Swedish/ company, not Norwegian!

    Hm. I could have sworn the folks we dealt with were
    in Norway - perhaps a branch office?


    It would be a little surprising, but certainly possible. Sweden has had
    been quite significant in the compiler world - IAR is a big name in
    embedded toolchains, and they are Swedish.

    You are sure you are not just one of these ignorant parochial Merican's
    who think Norway is the capital of Sweden? :-)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to David Brown on Tue Jan 16 15:15:16 2024
    On 15/01/2024 15:23, David Brown wrote:

    Let's look at an actual example from my own code, in an older project. I wanted an endian swap function on an ARM microcontroller, and for
    reasons that escape me for now, I did not want to use gcc's __builtin_bswap32, or an intrinsic from a header, or just plain C code
    (which modern gcc could optimise to a single "rev" instruction).  The
    code was probably originally written for quite an old version of the compiler.  So I wrote the function:

    static inline uint32_t swapEndian32(uint32_t x) {
            uint32_t y;
            asm ("rev %[y], %[x]" : [y] "=r" (y) : [x] "r" (x) : );
            return y;
    }

    This is, IMHO, quite clear once you know that gcc assembly consists of
    the assembly template, the outputs, then the inputs.

    Well, you've explained it. But I'm none the wiser. Let's break it down
    better:

    rev %[y], %[x] # rev appears to be an ARM instruction:
    # rev Rdest, Rsource
    [y] "=r" (y) # Outputs?
    [x] "r" (x) # Inputs?

    You're telling gcc that somehow, the value of x needs to get into a
    register (since rev doesn't work on memory, or immediates). And that the
    new value of y needs to come from a register.

    The compiler will decide which registers to use, and insert them into
    that instruction. And it will ensure that x is loaded into its register,
    if it is not already in one; and that y is loaded from its register, if
    it is not already in the prefered one.

    Since this is a return value, it will likely use R0 anyway.

    But I'm inferring this from the way I know that 'rev' must work, and
    from your comments. You seem to be expending a lot of effort however
    into explaining it to gcc.

    My function would be this on x64 (although I don't support bswap):

    fun swapends64(u64 x)u64 = assem mov rax, [x]; bswap rax; end

    This could have been shorter if 'bswap' had a separate dest register.
    Here, knowing that x is always going to be rcx, I could have copied
    straight from there, but would be bad form.

    I think a useful enhancement to my scheme would be allow 'x' for example
    to exist in static memory, stackframe, or in a register. The register
    allocator for locals can be made to work with the assembly: it will only
    choose registers that have not been used for anything else.

    So, there are plenty of opportunities to make my scheme even better.



      And it generates
    the code optimally - when used in an expression, there will be no extra moves, or data put on the stack, or wasted registers.  The compiler can
    move the code back and forth while optimising, eliminate calls when the result is used, and generally do its job just as well with this function
    as any other inline function or built in operator.


    You need to tell me, because I will otherwise not have a clue.

    It's clear that you haven't a clue.  So how can you justify ranting and raving against something you don't understand?

    I'm been familiar with x86 assembly for 40 years, so I should expect to understand it! But the answer is simple: what gcc provides is little to
    do with x86, and 90% of it seems made up.


    From what I've seen of gcc inline asm:

      * Code has to be written within string literals,

    Yes, obviously.  Assembly is not C, so writing assembly mixed in your C requires it to be in a format that is acceptable in C syntax (or at
    least close enough to C syntax to be a non-invasive extension).  String literals are also quite amenable to generation by macros, for those that
    want to write something complicated.

    So, how did I manage to get Intel-style assembly into my language? I
    didn't need to use strings.

    gcc should try harder!


    in dreadfil AT&T
        syntax.

    "Dreadful" is, again, /your/ opinion - not shared by everyone.  (I personally don't care either way.)

    This is the first hit for "at&t versus intel syntax": https://imada.sdu.dk/u/kslarsen/dm546/Material/IntelnATT.htm

    Its opinion is:

    "The AT&T form for instructions involving complex operations is very
    obscure compared to Intel syntax."

    The second hit is from stackexchange and has the remark: "I personally
    find "Intel syntax" much more readable, so that's why it surpises me."

    The third hit is from stackoverflow, and starts: "To me, Intel syntax is
    much easier to read."

    The fourth hit offered no opinion (but chose to go with Intel).

    The fifth is from Reddit and starts: "I'm curious whether more people
    use Intel or AT&T syntax for x86_64 assembly language programming? I've
    tried to use GNU GAS but found it a bit counterintuitive. On the other
    hand, I used NASM, and it felt a lot better."

    So, it's not just my opinion.

    It only applies to x86, not any
    other targets, and is easily changed by the "-masm=intel" flag

    That's usually how I view gcc assembly output. But it still manages to
    make it look terrible. Godbolt is much better as it filters out stuff
    that is not relevant.


    And apparently even with embedded \n line breaks. (Good
        grief - I think early 80s BASICs had more sophisticated facilities!)

    That is an inevitability for string literals.  And it doesn't matter
    much in practice, since most inline assembly (IME) consists of a single statement - gcc handles any moves that might be needed.

    I'm sorry, but that is not writing 'assembly'.

    Remember, the compiler passes the assembly on to the assembler - this is /not/ a C compiler with a built-in assembler.  And that's a good thing.
    Have you any idea how many assembly instructions there are for all the targets supported by gcc?  And you'd need to update gcc every time there
    was a new instruction, rather than just updating the assembler (which is
    a lot simpler).

    I wonder how many times people here have updated just 'as'? In any case,
    there are a number of ways around it, but as you have pointed out, you
    don't make serious use of assembly so it doesn't matter.

    Of course it would be /possible/ to extend gcc with a built-in
    assembler.  But what would that give you?  Lots of duplicate work to support C, C++, Fortran, Ada, and other languages?

    On top of the duplicate work you already need to support C, C++, Fortran
    and Ada?

    Well, you can forget the last two. But a lower level language like C,
    which is already known as a 'portable assembler', you'd think would have
    better facilities.

    I have a better idea: how about you take an existing, proper assembler,
    and build a C compiler around it?

      The assembler
    already handles assembly - why make an HLL do it?  It's a lot better to
    put the effort into reducing the number of times you actually need to
    write inline assembly, by improving the optimiser and builtin functions.


      * You mostly use offsets to get at local variables

    You never do that.  You are imagining things.  Or you are looking at
    some very odd inline assembly examples.


      * You apparently aren't allowed to use just any registers as you need
        to negotiate with gcc so as not to interfere with /its/ use of
        registers. So most examples I saw seemed to deal with this.

    Or, as sane people would say, you don't need to mess around trying to
    figure out what different registers are used for different purposes, or
    where your input data is, or where your output data should go - gcc will handle it all for you.

    As I've said repeatedly, this not assembly. You have to ask exactly why
    you need to use assembly. If it is in rare, special situations, then it
    is not a big deal to think about how it will work with registers.


    I consider that when writing assembly, YOU are in charge not the
    compiler. As you can see from mine:

      * It is written just as it would be in an actual ASM file

    Yes - and that's why it is so limited, and requires so much more
    assembly.  I prefer to let the compiler do what the compiler is good at.

    I do that when I write HLL code. But when I need ASM, it should be as
    simple as possible:

    a := asm rdtsc # low 32 bit of time stamp counter
    println a



      * You can refer to variables directly (the compiler will add what is
        needed to access locals or statics)

    I can refer to all the variables I want to - and coordinate with the
    compiler so that it knows what I am doing.  Cooperation works far better than some arrogant pompous fool claiming they know better, and ruining
    the optimiser's work.  Mind you, you wrote your compiler, so I suppose
    you /do/ know better than your compiler.


    If a function uses inline ASM, variables are kept in memory not
    registers.

    What a terrible pessimation.

    The need for assembly usually trumps whatever minor optimisation my
    compiler might do.


    (I might allow that at some point.) Most such functions however
    contain only ASM.


    What a terrible limitation.

    I didn't mention a limitation. My remarks mean my functions can comprise
    0% to 100% inline assembly, but quite often it will be 100%, by choice.
    For example, routines to do 128-bit arithmetic.


    That still lets ASM use the facilities of the HLL such as functions,
    declarations, named constants, scopes etc.

    I suppose you're going to suggest that gcc's facilities are superior...


    There really isn't the slightest doubts there.

    I'll happily agree that your inline assembly is simpler.  But in every
    other respect, it's not close to gcc's.

    But perhaps you don't care about efficient code generation (and to be
    fair, that is certainly not always important), and perhaps since your compiler doesn't do much optimising then there is little to be lost by failing to work along with the optimiser.


      And perhaps you have to write
    big sections of assembly because you can't write them in C and get fast results.

    My last post mentioned an app where my inline assembly, even combined
    with my non-optimised code for the rest, resulted in much faster
    runtimes than achieved by transpiling to C.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to David Brown on Tue Jan 16 15:24:11 2024
    On 15/01/2024 20:16, David Brown wrote:
    On 15/01/2024 19:41, bart wrote:

    [gcc writing.out/a.ext output executables by default.]

    Meanwhile everybody is defending it, and there are even people like
    you saying that is an advantage!


    Maybe to some people it /is/ an advantage.  Have you ever considered that?

    Sure. The same way that the fallthrough behaviour of C's switch
    statement is considered an advantage by some. Meanwhile, 99% of the time ...

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to bart on Tue Jan 16 16:45:30 2024
    On 16/01/2024 16:24, bart wrote:
    On 15/01/2024 20:16, David Brown wrote:
    On 15/01/2024 19:41, bart wrote:

    [gcc writing.out/a.ext output executables by default.]

    Meanwhile everybody is defending it, and there are even people like
    you saying that is an advantage!


    Maybe to some people it /is/ an advantage.  Have you ever considered
    that?

    Sure. The same way that the fallthrough behaviour of C's switch
    statement is considered an advantage by some. Meanwhile, 99% of the time

    A default a.out filename is of no advantage to /me/, but Gabriel says
    he/she finds it useful, and I've no reason to doubt him/her.

    Default fallthrough switch behaviour is different in that it is
    potentially counter-productive and dangerous. As someone who uses good
    tools, and knows how to use them, it is not an issue for my own code -
    but it can be a source of error in other people's code.

    I can imagine bugs in real, released code as a result of default
    fallthrough in switches. I cannot imagine problems caused by default
    output filenames - it is, at most, an extremely minor inconvenience for
    a few people.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to bart on Tue Jan 16 16:29:07 2024
    On 16/01/2024 13:26, bart wrote:
    On 15/01/2024 15:23, David Brown wrote:
    On 15/01/2024 03:14, bart wrote:

    You pass the relevant data into and out of the inline assembly.  If
    you think you need access to other symbols in the assembly, you are
    (almost certainly) doing things wrong.  You are trying to do the
    compiler's job behind its back, and that is not a good idea.

    Not with gcc. You don't want to mess with that.

    No. But I prefer it that way - I am much happier with a compiler that
    has a (fairly) precise way to express the interaction between inline
    assembly and the rest of the compiler, than one that leaves you guessing
    and hoping that your use of registers will not conflict with the compiler's.


    But /I/ use inline assembler when /I/ in in charge of the code.

    If /I/ had to write extensive programs in gcc inline assembly, then
    put a gun to my head now!

    If you are trying to write extensive programs in assembly, you are
    already getting it wrong.

    I want to write HLL functions that may have a number of lines in
    assembly, from one line up to a few dozen.

    I want to be /able/ to do that, but I very much don't want to /have/ to
    do that. I certainly don't want to have to mess around with the donkey
    work of moving data around and in and out of registers when the compiler
    can do that for me! I use inline assembly when the compiler can't do
    the job or use the particular instruction(s) by itself - and no more
    than that.


      Inline assembly is for things that cannot be expressed in high level
    languages, or the very rare occasions where you know a way to do
    something in assembly that is very much more efficient than the
    compiler can generate, and the code is speed critical, and there are
    no built-ins for the task, and no target intrinsics provided by the
    processor manufacturer.


    Take this example in C:

        int a;

        void F(void) {
             int b=2, c=3;
             static int d=4;

             a = b + c * d;
        }

    I will now show it in my language but with that assignment replaced
    by inline assembly:

         int a

         proc F=
             int b:=2, c:=3
             static int d=4

             assem
                 mov   rax, [c]               # (note my ints are 64 bits)
                 imul2 rax, [d]
                 add   rax, [b]
                 mov   [a], rax
             end
         end

    My question is: what would the C version look like with that line in
    gcc inline assembly? (In both cases, 'a' should end up with the value
    14.)

    void F(void) {
         int b = 2:
         int c = 3;
         static int d = 4;

         asm ("imul2 %[c], %[d]\n\t"
              "add %[c], %[b]"
             : [c] "+g" (c) : [b] "g" (b), [d] "g" (d));
         a = c;
    }

    Sorry, but you've turned it into gobbledygook. My example was for x64
    which is a 1.5 address machine, here you've turned it into a 2-address machine. Could I make it 3-address? What are the rules?

    It is a different language.

    You are misunderstanding the gcc syntax here. Now that I see what you
    mean, I realise I should probably have explained it earlier.

    Basically, gcc inline assembly statements look like this :

    asm(<asm template> : <outputs> : <inputs> : <clobbers>);

    The <asm template> is a string (often written using C string
    concatenation if it spans multiple lines). This is a /template/ - it
    contains special characters and sequences that get filled in before it
    is written out to the assembler. In particular, each input and output
    operand is replaced.

    The <inputs> and <outputs> are lists of input and output constraints for
    data that is passed into and out of the assembly expression. A
    constraint consists of an optional symbolic name inside square brackets,
    an operand constraint string, and a C expression inside parentheses.

    Inside the assembly template, you can refer to these operands by
    positional number (%0 for the first, %1 for the next, etc.), or by the
    symbolic name using the format %[name]. That is what the "%[c]", etc.,
    means in the code I wrote. (The symbolic name does not have to bear any connection to the variable name in C - indeed, the input and output
    expressions do not have to refer to C variables. But /if/ they refer to variables, then I often pick the same name.) Some people prefer to use parameter numbers, others (like me) prefer names.

    When the compiler is handling the inline assembly line, it first matches
    the input and output operands to their constraints. A constraint "r"
    means "any general-purpose register". "m" means "addressable memory",
    "i" means "immediate value", and "g" means "general - any of r, m or i".
    There are a great many other constraint codes available, most of which
    are specific for specific targets.

    So if the data that is to go into an input with constraint "r" is
    already in a register - say "r8" - then any use of that operand in the
    assembly template will be replaced by "r8" - or "%r8" or whatever format
    is needed by the assembler. If the input data happens to be known at compile-time (as is the case for your local variables here), then the
    compiler will first pick a free register - say, "rbx" - and then
    generate whatever code is needed to load that register with the known
    number. That might be a "load" instruction, a "move" instruction, or a
    bunch of instructions needed to access constant data from a store
    relative to the current PC - whatever the target processor needs. If
    the input expression is "foo() + 3 * xs[43]", it will evaluate that
    expression and store the result in the free register.

    If the constraint is "g", then it can be a lot more flexible - that's
    suitable for x86 assembly, where many instructions can handle a memory reference, an immediate, or a register. And if it is a memory
    reference, the compiler will replace the operand in the assembly
    template with whatever format suits the target - whether that be
    "[rsi+4]", "16(si)", "*0x1234" or anything else suitable for the target.

    Outputs are similarly matched with free registers, memory addresses, or whatever suits the constraint (an "i" constraint is obviously not
    helpful here). And after the assembly has been executed, this output
    will then be assigned to the expression given in the output operand
    list. For simple cases where the output operand is a local variable,
    usually nothing will be needed because the compiler would match the
    output operand to a register and use that for the local variable
    afterwards. But the expression could be any lvalue, and need assignment afterwards.

    The "+g" (which would actually have been better as "+r") says that the
    operand is an input as well as an output.

    All in all, it means the compiler handles register allocation and moving
    data into and out of registers - if needed.


    The syntax can cover a lot more than this - modifiers for the operands
    (very useful if you have, say, a 16-bit target and want to access the
    high and low halves of a 32-bit operand), constraints of all sorts, flag register changes, assembly templates that adjust automatically for Intel
    and AT&T syntax, and so on. But that's too much for a Usenet post!



    The generated result (from <https://godbolt.org>) is :

    F():
             mov     eax, 3
             imul2 eax, 4
             add eax, 2
             mov     DWORD PTR a[rip], eax
             ret

    The initialisations I used were so I could test that it gave the correct results. Without them, godbolt gives me this for the body of the function:


            movl    d.0(%rip), %edx
            movl    -8(%rbp), %eax
            imul2 %eax, %edx
            add %eax, -4(%rbp)
            movl    %eax, -8(%rbp)
            movl    -8(%rbp), %eax
            movl    %eax, a(%rip)


    You need to initialise the values, or else the code has undefined or unspecified behaviour - input operands are read, so they need to have
    values from somewhere.

    But there you see the power of gcc's inline assembly. I gave the
    results with -O2, because it is unnatural to use gcc without
    optimisation enabled. Your results are for -O0. And you can see that
    gcc handles the movement in and out of registers as needed, and is happy
    to replace the "%[b]" with reading memory directly from the stack.


    My version (which evaluates a=b+c*d; somehow your version modifies c)
    gives me this (D0 == rax):

              mov       D0,    [Dframe+test.f.c]
              imul2     D0,    [test.f.d]
              add       D0,    [Dframe+test.f.b]
              mov       [test.a],    D0


    "c" is a local variable - modifying it is fine. So my assembly matched:

    c *= d;
    c += b;

    Then "a = c" was in C.

    I could have created a new local variable "res", then had the assembly for :

    res = c;
    res *= d;
    res += b;
    a = res;


    void F(void) {
    int b=2, c=3;
    static int d=4;

    int res;
    asm (
    "mov %[res], %[c];\n\t"
    "imul %[res], %[d];\n\t"
    "add %[res], %[b];\n\t"
    "mov %[a], %[res];\n\t"
    : [res] "=&r" (res), [a] "=m" (a)
    : [b] "g" (b), [c] "g" (c), [d] "g" (d));
    }

    That results in the same code when optimised, and a little extra at -O0
    (since the compiler wants to keep the scratch variable "res" around).

    (I am ending the assembly lines here with ";\n\t" - the extra semicolon
    changes nothing in the behaviour, but makes it easy to see on godbolt
    which lines are generated from the inline assembly.)


    Unsurprisingly, this is exactly the ASM I typed (plus the necessary name qualifiers). That is the entire point.

    If I tweak your C version, make a,b,c,d all external statics, and apply
    -O3, godbolt gives me this:

            imul2 c(%rip), d(%rip)
            add c(%rip), b(%rip)
            movl    c(%rip), %eax
            movl    %eax, a(%rip)
            ret

    This is slightly worrying: imul2 is not a valid instruction (it's
    specific to my assembler). While add can't take two memory operands.


    Yes, that is correct. If the operands are all from memory, then at
    least one constraint should be "r" rather than "g" to ensure that we
    have at least one register operand. And we'd want a scratch variable,
    rather than modifying "c". So you'd have something like this :


    void F(void) {
    extern int A, B, C, D;

    int res = C;
    asm ("imul %[res], %[d];\n\t"
    "add %[res], %[b];\n\t"
    : [res] "+r" (res)
    : [b] "g" (B), [d] "g" (D));
    A = res;
    }

    Again, only two lines of inline assembly, generating:

    F:
    mov eax, DWORD PTR C[rip]
    imul eax, DWORD PTR D[rip];
    add eax, DWORD PTR B[rip];

    mov DWORD PTR A[rip], eax
    ret


    So it looks like it can only do so much checking. (Using gcc locally
    gave valid assembly.)


    gcc can't check the assembly at all.

    So it all seems hit and miss.

    Assembly is not for the faint-hearted.


    I'll reply to the second half of your post later.

    Let me just say that, in my interpreter, the extensive use of inline
    assembly in one module, makes some programs run twice as fast, as a gcc-O3-compiled C rendering.

    That can sometimes happen, for particular kinds of code. It is not
    often that hand-written assembly will be so significantly faster than
    gcc unless there are close matches for unusual instructions that the
    compiler does not generate. But it can certainly happen.

    Often the C code can be improved in various ways - perhaps using
    extensions (such as gcc attributes). Getting the very best out of a
    compiler for key performance-critical code is rarely just a matter of
    writing "-O3" and hoping for the best.

    If you can boil this down to a short and manageable piece of code, then
    it might be fun to look at ways of improving the speed using either pure standard C, or gcc extensions, and compare it to the hand-generated
    assembly. I realise that making such as small sample that keeps the
    effect is unlikely to be a trivial task. (And if you do this, put it in
    a new thread :-) )


    It also lets me write trivial solutions to the LIBFFI problem.

    It works.


    I'm sure your inline assembly /does/ work, for your needs. But it is
    not "better" than gcc's inline assembly, and it would not do the job
    that many others need from inline assembly.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Janis Papanagnou on Tue Jan 16 16:54:34 2024
    On 16/01/2024 15:08, Janis Papanagnou wrote:
    On 16.01.2024 14:42, David Brown wrote:
    On 16/01/2024 12:54, bart wrote:

    Which processor is this for again?

    It's for a cpu with a "madd" instruction that implements "x = x + y * z"
    in a single instruction - as Kaz pointed out, doing this in inline
    assembly would make sense if it the cpu had such a dedicated
    instruction. [...]

    I recall such a feature from a 35+ years old assembler project I did.
    It was on the TI TMS320C25 DSP, and the instruction was called 'MAC' (Multiply and ACcumulate). - Not sure it clarifies anything but just
    as an amendment if someone is interested in searching for keywords
    on such a function.


    I was too blind to notice that this completely synthetic example
    expression actually is a useful function that is a single accelerated instruction on many architectures. (And I've just recently finished a
    project that uses MAC's in a filter.) Now I feel /really/ silly!


    Of course many architectures have multiply-accumulate instructions, in
    all sorts of variants, because they are key operations in many DSP
    algorithms and filters. They can be integer, floating point, saturated integer, scaled integer. In DSPs they are often very complex, such as
    having operands via pointer registers that scan through buffers (perhaps circular buffers), all in a single cycle.

    They are also sometimes known as "FMA" (fused multiply add).

    x86-64 has some SIMD/AVX512 MAC instructions, I believe.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to bart on Tue Jan 16 15:55:42 2024
    bart <bc@freeuk.com> writes:
    On 16/01/2024 01:21, Kaz Kylheku wrote:
    On 2024-01-15, bart <bc@freeuk.com> wrote:

    [Inline assembly]

    The instruction template is just a string literal to the
    compiler. It specifies text to be inserted into the assembly
    output.

    Some assembly languages require the whitespace; you need
    instructions to be on separate lines.

    GCC does not look inside this template other than to replace
    % codes like %0 (the first register).

    In my example, I put the newlines and tabs together on the right

    "imul %3, %2\n\t"
    "add %1, %2\n\t"
    "mov %1, %0\n\t"

    Thanks to these newlines and tabs, the textual output (generated .s
    file if we use gcc -S) has this in it:

    #APP
    # 24 "inline.c" 1
    imul %edx, %esi
    add %esi, %edi
    mov %edi, %edi

    # 0 "" 2
    #NO_APP
    movl 8(%rsp), %eax
    movl 16(%rsp), %edx


    This is still peculiar: why prioritise the appearance of the
    intermediate code which I assume you're rarely going to look at?

    What leads you to the belief that anyone is 'prioritizing' the
    appearance of intermediate code?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to Janis Papanagnou on Tue Jan 16 15:57:45 2024
    Janis Papanagnou <janis_papanagnou+ng@hotmail.com> writes:
    On 16.01.2024 14:42, David Brown wrote:
    On 16/01/2024 12:54, bart wrote:

    Which processor is this for again?

    It's for a cpu with a "madd" instruction that implements "x = x + y * z"
    in a single instruction - as Kaz pointed out, doing this in inline
    assembly would make sense if it the cpu had such a dedicated
    instruction. [...]

    I recall such a feature from a 35+ years old assembler project I did.
    It was on the TI TMS320C25 DSP, and the instruction was called 'MAC' >(Multiply and ACcumulate). - Not sure it clarifies anything but just
    as an amendment if someone is interested in searching for keywords
    on such a function.

    Pretty much every modern architecture has multiply and accumulate
    instructions, even the ARM Cortex-M7 cores (MLA instruction).

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to David Brown on Tue Jan 16 16:02:37 2024
    David Brown <david.brown@hesbynett.no> writes:
    On 15/01/2024 21:41, Scott Lurndal wrote:
    David Brown <david.brown@hesbynett.no> writes:
    On 15/01/2024 18:30, Scott Lurndal wrote:
    David Brown <david.brown@hesbynett.no> writes:
    On 15/01/2024 03:14, bart wrote:

    <snip>

    The only compiler I used with an inline
    assembly of comparable power and integration to gcc's, but a different >>>>> syntax, was Diab Data.

    Now there's a name I haven't heard in decades. What ever happened
    to them? We worked with them back in the early 90's using their
    88100 compiler. It was impressive, particularly compared to the
    PCC port that Motorola provided. Greenhills was ok, but diab
    produced better code. gcc was still pretty primitive in those days.

    A good Norweigian company.

    A good /Swedish/ company, not Norwegian!

    Hm. I could have sworn the folks we dealt with were
    in Norway - perhaps a branch office?


    It would be a little surprising, but certainly possible. Sweden has had
    been quite significant in the compiler world - IAR is a big name in
    embedded toolchains, and they are Swedish.

    You are sure you are not just one of these ignorant parochial Merican's
    who think Norway is the capital of Sweden? :-)]

    No, I'm 7/8th norwegian, with a bit of swiss. While I haven't visited (yet),
    I do have relatives there. Think Luren dal.

    Granted it's been three decades since were were using diab compilers (1993ish)...

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to bart on Tue Jan 16 15:53:26 2024
    bart <bc@freeuk.com> writes:
    On 15/01/2024 23:28, Kaz Kylheku wrote:
    On 2024-01-15, bart <bc@freeuk.com> wrote:
    c:\c>copy hello.c c_code_main
    1 file(s) copied.

    There goes that damned AT&T instruction syntax for file copying, same
    like in Unix.

    Doesn't it bother you that it isn't: copy destination source?

    I haven't said anything about in which direction the data goes.

    This is something that tends to depend on device, so Motorola went left
    to right, and Zilog/Intel went right to left.

    So it did seem odd for this x86 assembler to do the opposite of Intel.

    However, doesn't it bother /you/ that AT&T also does the opposite of not

    It doesn't bother me at all. Every assembler I used was from->to
    from the PDP-11 through Burroughs mainframes.

    Only the intel assembler (and arm, which copied intel) reverses that.

    AT&T chose from->to to match the PDP-11 (and later VAX-11) assembler
    behavior. It made no sense to use the intel variety when they ported
    to intel.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to bart on Tue Jan 16 18:46:43 2024
    On 16/01/2024 16:15, bart wrote:
    On 15/01/2024 15:23, David Brown wrote:

    Let's look at an actual example from my own code, in an older project.
    I wanted an endian swap function on an ARM microcontroller, and for
    reasons that escape me for now, I did not want to use gcc's
    __builtin_bswap32, or an intrinsic from a header, or just plain C code
    (which modern gcc could optimise to a single "rev" instruction).  The
    code was probably originally written for quite an old version of the
    compiler.  So I wrote the function:

    static inline uint32_t swapEndian32(uint32_t x) {
             uint32_t y;
             asm ("rev %[y], %[x]" : [y] "=r" (y) : [x] "r" (x) : );
             return y;
    }

    This is, IMHO, quite clear once you know that gcc assembly consists of
    the assembly template, the outputs, then the inputs.

    Well, you've explained it. But I'm none the wiser. Let's break it down better:

        rev  %[y], %[x]           # rev appears to be an ARM instruction:

    Yes.

                                  # rev Rdest, Rsource

    Yes.

        [y] "=r" (y)              # Outputs?

    Yes.

        [x] "r" (x)               # Inputs?

    Yes.


    You're telling gcc that somehow, the value of x needs to get into a
    register (since rev doesn't work on memory, or immediates). And that the
    new value of y needs to come from a register.

    Yes.


    The compiler will decide which registers to use, and insert them into
    that instruction.

    Yes.

    And it will ensure that x is loaded into its register,
    if it is not already in one; and that y is loaded from its register, if
    it is not already in the prefered one.


    Yes.

    All good here!

    Since this is a return value, it will likely use R0 anyway.

    Well, that would be the case if "swapEndian32" were generated as a
    stand-alone function. (Both x and y would use r0.) But as a small
    static inline function, this would normally be inlined directly in
    functions that use it :


    #include <stdint.h>

    uint32_t swapEndian32(uint32_t x) {
    uint32_t y;
    asm ("rev %[y], %[x]" : [y] "=r" (y) : [x] "r" (x) : );
    return y;
    }

    void swap_lots(const uint32_t * restrict in, uint32_t * restrict out,
    uint32_t n) {
    while (n--) {
    *out++ = swapEndian32(*in++);
    *out++ = swapEndian32(*in++);
    *out++ = swapEndian32(*in++);
    *out++ = swapEndian32(*in++);
    }
    }

    ARM GCC 13.2.0 with options "-O2 -Wall -Wextra -mcpu=cortex-m7" :

    swapEndian32:
    rev r0, r0
    bx lr
    swap_lots:
    cbz r2, .L11
    add ip, r2, #-1
    adds r0, r0, #16
    adds r1, r1, #16
    push {r4, r5}
    .L5:
    add ip, ip, #-1
    adds r1, r1, #16
    cmp ip, #-1
    ldrd r5, r4, [r0, #-16]
    ldrd r2, r3, [r0, #-8]
    rev r5, r5
    rev r4, r4
    rev r2, r2
    rev r3, r3
    add r0, r0, #16
    strd r5, r4, [r1, #-32]
    strd r2, r3, [r1, #-24]
    bne .L5
    pop {r4, r5}
    bx lr
    .L11:
    bx lr



    But I'm inferring this from the way I know that 'rev' must work, and
    from your comments. You seem to be expending a lot of effort however
    into explaining it to gcc.

    If you look at the "swap_lots" function above, you can see the
    advantage. The compiler can use double-register loads and stores for
    twice the memory efficiency (the Cortex-M7 has some 64-bit internal
    buses), and it can schedule the instructions better (that core is
    dual-issue and pipelined, but not OOO). This means the results are many
    times faster than if everything were pushed sequentially through "rev
    r0, r0".



    My function would be this on x64 (although I don't support bswap):

       fun swapends64(u64 x)u64 = assem mov rax, [x]; bswap rax; end


    And how efficient would your "swap_lots" function be?

    Here you can also see the massive advantage of gcc's use of string
    assembly templates that it passes on to the assembler - you never get
    the situation where the compiler doesn't support a particular assembly instruction.

    This could have been shorter if 'bswap' had a separate dest register.
    Here, knowing that x is always going to be rcx, I could have copied
    straight from there, but would be bad form.

    I agree that would be bad form, unless you know for sure that it would
    always be in the one particular register. Again, gcc's syntax shines -
    gcc will sort this out for you, generating any register moves that are
    needed and skipping any that are unnecessary.


    I think a useful enhancement to my scheme would be allow 'x' for example
    to exist in static memory, stackframe, or in a register. The register allocator for locals can be made to work with the assembly: it will only choose registers that have not been used for anything else.

    So, there are plenty of opportunities to make my scheme even better.


    Sure.



      And it generates the code optimally - when used in an expression,
    there will be no extra moves, or data put on the stack, or wasted
    registers.  The compiler can move the code back and forth while
    optimising, eliminate calls when the result is used, and generally do
    its job just as well with this function as any other inline function
    or built in operator.


    You need to tell me, because I will otherwise not have a clue.

    It's clear that you haven't a clue.  So how can you justify ranting
    and raving against something you don't understand?

    I'm been familiar with x86 assembly for 40 years, so I should expect to understand it! But the answer is simple: what gcc provides is little to
    do with x86, and 90% of it seems made up.

    Oh, I know you understand x86 assembly. It's the gcc inline assembly
    you didn't understand at all. (Now you understand a good deal about it.)



    From what I've seen of gcc inline asm:

      * Code has to be written within string literals,

    Yes, obviously.  Assembly is not C, so writing assembly mixed in your
    C requires it to be in a format that is acceptable in C syntax (or at
    least close enough to C syntax to be a non-invasive extension).
    String literals are also quite amenable to generation by macros, for
    those that want to write something complicated.

    So, how did I manage to get Intel-style assembly into my language? I
    didn't need to use strings.

    Your language is not C either.


    gcc should try harder!

    No, it has a solution that works fine. It needs somewhat more
    punctuation than optimal, but otherwise it's good enough for the purpose.

    Of course it would always be possible to do better. But mixing in a
    completely different kind of language in the middle of a high level
    language is not helpful. You'd need to make huge changes to the parsers
    just to handle the mix of line-oriented assembly and non-line-oriented
    main code (in all the languages that gcc supports - in the main tree,
    and all the extra ones run as separate projects). You'd need to deal
    with the dozens of different mainline targets (and the other targets
    that are separate projects). You'd need to keep updating this for every
    little change to every processor target - duplicating work done already
    by binutils and other assemblers. You'd then want to change clang, icc, CodeWarrior, and other C compilers (and their C++ counterparts, and
    their Rust, D, Pascal, and anything else that supports inline assembly)
    - or you'd lose compatibility. Oh, and then you'd want to change all
    the existing uses of inline gcc in user code.

    It would be an enormous effort, merely to give a minor convenience
    improvement to the tiny proportion of gcc users who ever write inline
    assembly. (Many /use/ it, with pre-written code in headers, but they
    don't /write/ it themselves.)



    in dreadfil AT&T
        syntax.

    "Dreadful" is, again, /your/ opinion - not shared by everyone.  (I
    personally don't care either way.)

    This is the first hit for "at&t versus intel syntax": https://imada.sdu.dk/u/kslarsen/dm546/Material/IntelnATT.htm

    Its opinion is:

    "The AT&T form for instructions involving complex operations is very
    obscure compared to Intel syntax."

    Yes, /opinion/ is the key point.

    That post also says "The advantage of AT&T syntax in this situation is obvious".

    I have no horse in this race - I don't care either way. People can have whatever preferences and dislikes they want.

    One thing we can be sure of is that numbers do not turn opinions into facts.

    The reality is that AT&T syntax can be simpler and neater for some
    things, while Intel syntax can be simpler and neater for others. I
    don't think either of them are particularly good or clear. I don't
    think AT&T's way of writing scaled indexing is good - I don't think
    Intel's "DWORD PTR" stuff is at all nice or necessary. My opinion is
    that they are equally bad in different ways, but I can live with either.


    So, it's not just my opinion.

    I never suggested you were alone. But it is just opinion - not fact.


    It only applies to x86, not any other targets, and is easily changed
    by the "-masm=intel" flag

    That's usually how I view gcc assembly output. But it still manages to
    make it look terrible. Godbolt is much better as it filters out stuff
    that is not relevant.


    gcc assembly output can be controlled in various ways (and you can get
    an output listing from the assembler too, or use objdump on the
    resulting binaries or object files). But of course the assembly
    generated by gcc has lots of directives - it has to convey a great deal
    of extra information downstream for things like debugging information.


    And apparently even with embedded \n line breaks. (Good
        grief - I think early 80s BASICs had more sophisticated facilities!) >>
    That is an inevitability for string literals.  And it doesn't matter
    much in practice, since most inline assembly (IME) consists of a
    single statement - gcc handles any moves that might be needed.

    I'm sorry, but that is not writing 'assembly'.

    It is writing inline assembly.

    We are not talking about writing full programs in assembly here - we are writing minimal pieces of assembly, integrated along with a high level language. If one instruction is all that's needed, then that's all that
    is needed.


    Remember, the compiler passes the assembly on to the assembler - this
    is /not/ a C compiler with a built-in assembler.  And that's a good
    thing. Have you any idea how many assembly instructions there are for
    all the targets supported by gcc?  And you'd need to update gcc every
    time there was a new instruction, rather than just updating the
    assembler (which is a lot simpler).

    I wonder how many times people here have updated just 'as'?

    The toolchain developers will.

    In any case,
    there are a number of ways around it, but as you have pointed out, you
    don't make serious use of assembly so it doesn't matter.

    For /serious/ use of assembly - writing large sections of assembly -
    then it might be more convenient to write pure assembly files. But it's certainly entirely possible to write large sections of gcc inline
    assembly. But it is almost never a good idea to write assembly if you
    can get the same results writing HLL code.


    Of course it would be /possible/ to extend gcc with a built-in
    assembler.  But what would that give you?  Lots of duplicate work to
    support C, C++, Fortran, Ada, and other languages?

    On top of the duplicate work you already need to support C, C++, Fortran
    and Ada?

    Yes, on top of that.


    Well, you can forget the last two.

    Why? People use inline assembly in Fortran and Ada.

    But a lower level language like C,
    which is already known as a 'portable assembler', you'd think would have better facilities.

    C is not, and never has been, a "portable assembler". The fact that
    some people are badly mistaken about this does not make it a relevant
    factor for what a C compiler should or should not support.

    And gcc has vastly better inline assembly facilities than your language
    and tools. /Vastly/ better. I thought I'd demonstrated that by now -
    but perhaps it's just a question of the order that posts have been read
    and written. I can't blame you for misconceptions you held and wrote
    about before you read more about gcc's inline assembly (and before I
    explained it more fully).

    I don't claim, in any way, that gcc's inline assembly syntax is perfect
    (quite aside from any opinions on AT&T vs Intel - after all, x86
    assembly is not something I use or care about). But I /do/ claim it is entirely useable, extremely powerful, and is used to great effect by
    lots of people.


    I have a better idea: how about you take an existing, proper assembler,
    and build a C compiler around it?

    And who would benefit?

    Seriously - /why/ do you think it is important for gcc to have a neater
    syntax for inline assembly? Would more people use it than do today -
    and would that be a good thing? Would the people who use it today make
    fewer mistakes? Would they be able to write it more efficiently, and
    would that be a significant impact on their work?

    Let's take an example from before:

    void F(void) {
    int b=2, c=3;
    static int d=4;

    asm ("imul %[c], %[d]\n\t"
    "add %[c], %[b]\n\t"
    : [c] "+r" (c) : [b] "g" (b), [d] "g" (d));
    a = c;
    }

    Suppose gcc's inline assembly was changed to remove the need for
    quotation marks and to put the input and output operands first, with a
    fuller syntax. (I'm keeping the principle of these operands, and the
    assembly template, as they are vital to functionality.) This would,
    IMHO, be a step forward in looking nice:

    void FF(void) {
    int b=2, c=3;
    static int d=4;

    asm(
    input g : op_b = b;
    input g : op_d = d;
    inout r : op_c = c;

    {
    imul op_c, op_d
    add op_c, op_b
    });

    a = c;
    }

    What do we actually win here? The costs are significant efforts to
    change the parsers, and incompatibility with other compilers and code.
    What are the gains, who gains, and is it worth it? I just can't see it.

    Now, if gcc were to get a multiline string feature like Python, /then/
    we'd be getting somewhere. Adding that one feature would be really nice
    in many places in C and C++ code, and would mean that - for free - you'd
    get this step up:

    void F(void) {
    int b=2, c=3;
    static int d=4;

    asm ("""
    imul %[c], %[d]
    add %[c], %[b]
    """ : [c] "+r" (c) : [b] "g" (b), [d] "g" (d));
    a = c;
    }

    The strings are simpler, and there's no ugly line endings or \t codes.



      The assembler already handles assembly - why make an HLL do it?
    It's a lot better to put the effort into reducing the number of times
    you actually need to write inline assembly, by improving the optimiser
    and builtin functions.


      * You mostly use offsets to get at local variables

    You never do that.  You are imagining things.  Or you are looking at
    some very odd inline assembly examples.


      * You apparently aren't allowed to use just any registers as you need >>>     to negotiate with gcc so as not to interfere with /its/ use of
        registers. So most examples I saw seemed to deal with this.

    Or, as sane people would say, you don't need to mess around trying to
    figure out what different registers are used for different purposes,
    or where your input data is, or where your output data should go - gcc
    will handle it all for you.

    As I've said repeatedly, this not assembly. You have to ask exactly why
    you need to use assembly. If it is in rare, special situations, then it
    is not a big deal to think about how it will work with registers.


    You only need assembly on rare occasions - as long as your HLL and
    compiler are good enough. That is precisely why it is okay if inline
    assembly is a bit cumbersome.

    But I've no idea why you think something is only assembly if you have to
    mess around with registers manually. It's a "no true Scotsman"
    argument, with no content. Still, if you want to do that, gcc inline
    assembly won't hinder you.


    I consider that when writing assembly, YOU are in charge not the
    compiler. As you can see from mine:

      * It is written just as it would be in an actual ASM file

    Yes - and that's why it is so limited, and requires so much more
    assembly.  I prefer to let the compiler do what the compiler is good at.

    I do that when I write HLL code. But when I need ASM, it should be as
    simple as possible:

          a := asm rdtsc               # low 32 bit of time stamp counter
          println a


    What about a 64-bit version?

    static inline uint64_t get_timestamp64(void) {
    uint64_t lo, hi;
    asm volatile ( "rdtsc" : "=a" (lo), "=d" (hi) );
    return lo | (hi << 32);
    }

    static inline uint32_t get_timestamp32(void) {
    uint32_t lo;
    asm volatile ( "rdtsc" : "=a" (lo) :: "rdx" );
    return lo;
    }

    It's not hugely difficult. And it doesn't have risks like causing
    trouble if the compiler used the "d" register for something else.

    Mind you, if I had to work with a compiler and HLL that didn't support inlining, or macros, I suppose I might think a little differently. I
    would not want to have to copy and paste any kind of inline assembly
    every time I want to use it without the overhead of a function call. Fortunately, I use a good compiler and good languages, so that is not a
    concern for me.



      * You can refer to variables directly (the compiler will add what is >>>     needed to access locals or statics)

    I can refer to all the variables I want to - and coordinate with the
    compiler so that it knows what I am doing.  Cooperation works far
    better than some arrogant pompous fool claiming they know better, and
    ruining the optimiser's work.  Mind you, you wrote your compiler, so I
    suppose you /do/ know better than your compiler.


    If a function uses inline ASM, variables are kept in memory not
    registers.

    What a terrible pessimation.

    The need for assembly usually trumps whatever minor optimisation my
    compiler might do.


    Ah, there we differ. I use tools that do good optimisations. And
    perhaps 70% of the use-cases of inline assembly will be lost if the tool
    can't generate efficient code when there is inline assembly.


    (I might allow that at some point.) Most such functions however
    contain only ASM.


    What a terrible limitation.

    I didn't mention a limitation. My remarks mean my functions can comprise
    0% to 100% inline assembly, but quite often it will be 100%, by choice.
    For example, routines to do 128-bit arithmetic.

    Fair enough - I misunderstood. (For some C compilers I have used,
    functions are /either/ C /or/ assembly - you can't mix them.)



    That still lets ASM use the facilities of the HLL such as functions,
    declarations, named constants, scopes etc.

    I suppose you're going to suggest that gcc's facilities are superior...


    There really isn't the slightest doubts there.

    I'll happily agree that your inline assembly is simpler.  But in every
    other respect, it's not close to gcc's.

    But perhaps you don't care about efficient code generation (and to be
    fair, that is certainly not always important), and perhaps since your
    compiler doesn't do much optimising then there is little to be lost by
    failing to work along with the optimiser.


      And perhaps you have to write big sections of assembly because you
    can't write them in C and get fast results.

    My last post mentioned an app where my inline assembly, even combined
    with my non-optimised code for the rest, resulted in much faster
    runtimes than achieved by transpiling to C.

    Yes - and I agree that it can happen. But see my reply in to that other
    post for a challenge, if you are interested. (No pressure.)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Scott Lurndal on Tue Jan 16 19:03:05 2024
    On 16/01/2024 17:02, Scott Lurndal wrote:
    David Brown <david.brown@hesbynett.no> writes:
    On 15/01/2024 21:41, Scott Lurndal wrote:
    David Brown <david.brown@hesbynett.no> writes:
    On 15/01/2024 18:30, Scott Lurndal wrote:
    David Brown <david.brown@hesbynett.no> writes:
    On 15/01/2024 03:14, bart wrote:

    <snip>

    The only compiler I used with an inline
    assembly of comparable power and integration to gcc's, but a different >>>>>> syntax, was Diab Data.

    Now there's a name I haven't heard in decades. What ever happened
    to them? We worked with them back in the early 90's using their
    88100 compiler. It was impressive, particularly compared to the
    PCC port that Motorola provided. Greenhills was ok, but diab
    produced better code. gcc was still pretty primitive in those days. >>>>>
    A good Norweigian company.

    A good /Swedish/ company, not Norwegian!

    Hm. I could have sworn the folks we dealt with were
    in Norway - perhaps a branch office?


    It would be a little surprising, but certainly possible. Sweden has had
    been quite significant in the compiler world - IAR is a big name in
    embedded toolchains, and they are Swedish.

    You are sure you are not just one of these ignorant parochial Merican's
    who think Norway is the capital of Sweden? :-)]


    I hope you noticed the smiley :-)

    No, I'm 7/8th norwegian, with a bit of swiss. While I haven't visited (yet), I do have relatives there. Think Luren dal.


    I would say you are of Norwegian decent, or have Norwegian family roots
    - it's not the same as being Norwegian. You need to at least visit the country! Alternatively, you need to eat a /lot/ of brunost to improve
    your credentials.

    I looked up "Lurendal" on Google maps. It's in Sweden :-) Maybe your
    parents told you they were Norwegian, because they know that Norwegians
    are superior to Swedes in every way...

    (There are a few place names in Norway with "Luren" in them, and of
    course spellings change over time between family names and place names.)

    Granted it's been three decades since were were using diab compilers (1993ish)...

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to David Brown on Tue Jan 16 18:45:01 2024
    David Brown <david.brown@hesbynett.no> writes:
    On 16/01/2024 17:02, Scott Lurndal wrote:
    David Brown <david.brown@hesbynett.no> writes:
    On 15/01/2024 21:41, Scott Lurndal wrote:
    David Brown <david.brown@hesbynett.no> writes:
    On 15/01/2024 18:30, Scott Lurndal wrote:
    David Brown <david.brown@hesbynett.no> writes:
    On 15/01/2024 03:14, bart wrote:

    <snip>

    The only compiler I used with an inline
    assembly of comparable power and integration to gcc's, but a different >>>>>>> syntax, was Diab Data.

    Now there's a name I haven't heard in decades. What ever happened >>>>>> to them? We worked with them back in the early 90's using their
    88100 compiler. It was impressive, particularly compared to the
    PCC port that Motorola provided. Greenhills was ok, but diab
    produced better code. gcc was still pretty primitive in those days. >>>>>>
    A good Norweigian company.

    A good /Swedish/ company, not Norwegian!

    Hm. I could have sworn the folks we dealt with were
    in Norway - perhaps a branch office?


    It would be a little surprising, but certainly possible. Sweden has had >>> been quite significant in the compiler world - IAR is a big name in
    embedded toolchains, and they are Swedish.

    You are sure you are not just one of these ignorant parochial Merican's
    who think Norway is the capital of Sweden? :-)]


    I hope you noticed the smiley :-)

    No, I'm 7/8th norwegian, with a bit of swiss. While I haven't visited (yet),
    I do have relatives there. Think Luren dal.


    I would say you are of Norwegian decent, or have Norwegian family roots
    - it's not the same as being Norwegian.

    Point.

    You need to at least visit the Country!

    My folks have been there a couple of times, and looked
    up distant relatives from both sides (the other side
    was from the Bergen area, IIRC).

    Alternatively, you need to eat a /lot/ of brunost to improve
    your credentials.

    Does lutefisk and lefse count? Had a small earthquake while
    typing this. Probably less than M3.


    I looked up "Lurendal" on Google maps. It's in Sweden :-)

    Blame my great great grandfather who changed his name from
    Olson to Johnson to Lurndal shortly after immigrating here,
    and shortly before serving in the Union army.


    Maybe your
    parents told you they were Norwegian, because they know that Norwegians
    are superior to Swedes in every way...

    The area where they settled (western wisconsin) was Norwegian, with
    a fair bit of competition from Swedish and German immigrants. The
    small (Pop. 50) town near my grandfathers farm had two churches,
    both Lutheran, one for the German population and one for the
    Norwegians (the Swedes were in the next village over).



    (There are a few place names in Norway with "Luren" in them, and of
    course spellings change over time between family names and place names.)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to bart on Tue Jan 16 18:39:28 2024
    On 2024-01-16, bart <bc@freeuk.com> wrote:
    On 16/01/2024 01:21, Kaz Kylheku wrote:
    On 2024-01-15, bart <bc@freeuk.com> wrote:

    [Inline assembly]

    The instruction template is just a string literal to the
    compiler. It specifies text to be inserted into the assembly
    output.

    Some assembly languages require the whitespace; you need
    instructions to be on separate lines.

    GCC does not look inside this template other than to replace
    % codes like %0 (the first register).

    In my example, I put the newlines and tabs together on the right

    "imul %3, %2\n\t"
    "add %1, %2\n\t"
    "mov %1, %0\n\t"

    Thanks to these newlines and tabs, the textual output (generated .s
    file if we use gcc -S) has this in it:

    #APP
    # 24 "inline.c" 1
    imul %edx, %esi
    add %esi, %edi
    mov %edi, %edi

    # 0 "" 2
    #NO_APP
    movl 8(%rsp), %eax
    movl 16(%rsp), %edx


    This is still peculiar: why prioritise the appearance of the
    intermediate code which I assume you're rarely going to look at?

    The the assembly language is whitespace sensitive. It requires
    the newlines; and possibly also the leading indentation.

    Chances are good you will look at the assembly output while
    debugging.

    It's the version with strings and escape codes that you're going to be writing and maintaining, and that people will see in the C sources!

    This is akin to a language allowing embedded C but you have to write it
    like this:

    clang{"\tprintf(\"A=%%d\\n\", a);\n"};

    so that the generated version looks like:

    printf("A=%d\n", a);

    Support for a multi-line string literal which allows literal
    newlines to denote themselves would also work.

    Except you can't refer to 'a' directly, you have to write it as %0 and
    then have some extra mechanism to somehow map that to 'a'.

    You *can* refer to registers directly in GCC inline assembly.

    You just usually don't *want* to.

    There has to be a good reason for that, like using a certain instruction
    that only takes specific register as a source or destination operand.

    (When you use a specific register in the template, you must inform the
    compiler by including it in the list of "clobbers". Clobbers
    are the last argument:

    asm ("template" : outputs : inputs : clobbers)

    if we clobber rax and rdx the clobbers might look like "rax", "rdx".

    Then the compiler knows that if some variable is held in any of those registers, it must be spilled to memory before that code can be executed
    (or some other strategy must be taken, like not allocating those
    registers).

    You can understand why GCC went with this textual templating approach, since >> the number of back-end assembly languages is astonishing. In some cases, I >> think, GCC supports more than one assembler syntax for the same architecture.
    Historically it has worked with assemblers that didn't come from GNU.

    gcc is a project 10s of 1000s of files. If a particular configuration
    targets one architecture out of 100, it can also support the ASM syntax
    for that architecture.

    Not reasonably so. The parser of every front end would have to have
    a special case for that assembly language.

    The proposal does not pass a cost/benefit analysis, due to the
    high cost and low benefit.

    You don't need to have the ASM syntax embedded within the C grammar. Not
    so specifically anyway; you allow a bunch of keyword and register tokens within asm{...}.

    keywords and tokens are grammar.

    A reasonable compromise would be to have some multi-line literal.

    Yes it's a bit harder, but if I can do it within a 0.3MB product, gcc
    can do it within 24MB.

    That's quite literally fallacious. A big project worked on by many
    people and widely used simply cannot do anything that a tiny one-person
    project can do. Not at the process level, nor the code change level.

    For that matter, in a one-person compiler that targets twelve
    architectures, we wouldn't necessarily approach inline assembly the same
    way as one which targets only x86.

    It would not be practical for GNU C to have a bazillino assembly language
    syntaxes in its grammar.

    Just admit that my approach to inline assembler is better and give it up. >>
    Your approach to inline assembler has nicer looking syntax, but
    semantically, it doesn't seem to be on same level as the GCC approach.

    GCC's inline assembly does nice things that are might not be possible in your
    implementation. (Or pretty much anyone else's).

    Some of it has been sufficiently exemplified elsewhere in the thread.

    GCC can choose registers for your inline assembly block,

    The point of ASM is that /you/ call the shots.

    Why don't you educate the GCC mailing list?

    In the GNU C inline assembly (called "Extended Assembly") you do call
    the shots. The instructions in your template are used verbatim: no
    instructions are added, deleted or renamed. You just have to
    coordinate with the compiler. If you don't inform the compiler about
    what you are doing, things will go wrong. Optionally, you can request
    that the compiler prepare operands for you and give you registers.

    It's not just more natural syntax, but better integration within the

    No man-made syntax is natural.

    host language. More example, I can 'goto' to a label within an ASSEM
    block, and 'jmp' out of the ASSEM block to a HLL label, or to an label
    within a separate ASSEM block further on.

    I don't know whether GCC inline assembly can do that or not. While
    you probably can't refer to an ordinary C goto label, GCC does have
    computed labels. Ther is a way to capture a label as a void * value
    I think. If you can get that as an operand into an asm block, it may
    be possible to jump to it.

    It doesn't strike me as a great idea, honestly.

    and will automatically
    move between those registers and the operands they are connected to (if
    necessary). The inline code seamlessly integrates with the code generated by >> the compiler. You can write primitives that generate code as well as compiler
    built-ins.

    OK. But it's not an 'assembler' as is generally understood.

    Yes it is; it uses assembly language instructions, which are integrated
    into assembly language output, fed into an assembler.

    The fact that operands like %1 are replaced by registers just means
    that it's a kind of macro assembly language.

    Mine is; it
    looks exactly line normal assembly, and it is written inline to the HLL.

    Yes, and you do get points, from other programmers who have access to
    the code, for it looking nice, and paying homage to Intel.

    The machine running the end result and the users don't care so much.

    The GNU "Extended Asm" as the documentation calls it has fantastic functionality though, due to the way you can declare contracts between
    the assembly fragment and the compiler.

    How things look does become important when you are writing tens of
    thousands of lines of code. Extended Asm isn't used for even hundreds of
    lines of code. In a typical project that needs any inline assembly at
    all, it's a very small amount of code.

    A large block of assembly will more likely be written as a .S file,
    not inline.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Janis Papanagnou on Tue Jan 16 18:52:48 2024
    On 2024-01-16, Janis Papanagnou <janis_papanagnou+ng@hotmail.com> wrote:
    On 16.01.2024 14:42, David Brown wrote:
    On 16/01/2024 12:54, bart wrote:

    Which processor is this for again?

    It's for a cpu with a "madd" instruction that implements "x = x + y * z"
    in a single instruction - as Kaz pointed out, doing this in inline
    assembly would make sense if it the cpu had such a dedicated
    instruction. [...]

    I recall such a feature from a 35+ years old assembler project I did.
    It was on the TI TMS320C25 DSP, and the instruction was called 'MAC' (Multiply and ACcumulate). - Not sure it clarifies anything but just
    as an amendment if someone is interested in searching for keywords
    on such a function.

    It is obviously useful for evaluating polynomials via Horner's rule,
    which involves multiplying the accumulator by a coefficient, and then
    adding a term.

    Ax^3 + Bx^2 + Cx + D -> x(Ax^2 + Bx + c) + D
    -> x(x(Ax + B) + C) + D

    O is output/accumuator:

    O = A
    O *= x; O += B; // multiply + add steps
    O *= x; O += C;
    O *= x: O += D;

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to David Brown on Tue Jan 16 19:44:39 2024
    On 15/01/2024 11:45, David Brown wrote:
    On 12/01/2024 18:09, bart wrote:

    download to a target board via a debugger,

    (Hey, I used to do that! Not a makefile in sight either; how is that
    possible?


    Grow up.

    I used to do that with no special tools, no external software and no
    external languages. I had to write assemblers for any new devices I
    has to use.)


    Yes, I've heard it before.  If you wanted a keyboard, you had to carve
    it out of a rock with your teeth.

    No. I bought a keyboard for £12. I didn't try and make one (I was
    unemployed and broke) but it was hopeless.

    (BTW that keyboard was a joy to use: it used a simple 8-bit port, with
    the top bit strobing when a key was ready. Compare with what's involved
    with using a USB keyboard today, if you didn't have a 10GB OS to take
    care of it.)

    When I learned assembly, I assembled code to hex by hand.  On paper.  I don't consider that particularly relevant to my work today.

    You need to get it right:

    * I built a machine that could only be programmed in actual binary

    * That binary code was used to write a hex editor

    * The hex editor was used to write an assembler

    * The assembler was used to write the compiler for my first language.
    (Which was then used for areas of interest which included 3D graphics,
    image processing and frame-grabbing, also using my hardware)

    * Later this compiler was rebooted on a better machine (with actual
    floppy disks!), and eventually it was self-hosted.

    (This is when I first looked at the K&R book - it cost me £12, £2 more
    than my Z80 processor - and dismissed it.)

    * Among many other tasks, the tool was used to write comprehensive
    assemblers for the 80188, and an Intel controller, 8035 or 8051.

    The point is, getting an entirely different, self-sufficient perspective compared with those who even then were using traditional tools.

    I had to solve the problem for example of combining multiple object
    files (in my own format of course) into one executable. Everybody else
    used a complex bit of software called TaskBuilder, or Linker. I just
    dashed together a program which could do the job as fast as it could
    read from disk.

    Task accomplished. But people are still messing about with object files
    and linkers now (and those mysterious .a and .lib files even when it's
    going to be using shared library anyway).

    They are taking those traditional tools and making them bigger and more complex.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to bart on Tue Jan 16 20:09:53 2024
    bart <bc@freeuk.com> writes:
    On 15/01/2024 11:45, David Brown wrote:
    On 12/01/2024 18:09, bart wrote:

    (BTW that keyboard was a joy to use: it used a simple 8-bit port, with
    the top bit strobing when a key was ready. Compare with what's involved
    with using a USB keyboard today, if you didn't have a 10GB OS to take
    care of it.)

    If you ignore, of course, the fact that a 64KB BIOS can easily handle
    the entire USB stack sufficient to support both USB mass storage
    devices, networking devices (PXE) and USB Human Interface Devices (keyboards, mice).

    No 10GB OS involvement.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Scott Lurndal on Tue Jan 16 21:06:10 2024
    On 16/01/2024 20:09, Scott Lurndal wrote:
    bart <bc@freeuk.com> writes:
    On 15/01/2024 11:45, David Brown wrote:
    On 12/01/2024 18:09, bart wrote:

    (BTW that keyboard was a joy to use: it used a simple 8-bit port, with
    the top bit strobing when a key was ready. Compare with what's involved
    with using a USB keyboard today, if you didn't have a 10GB OS to take
    care of it.)

    If you ignore, of course, the fact that a 64KB BIOS can easily handle
    the entire USB stack sufficient to support both USB mass storage
    devices, networking devices (PXE) and USB Human Interface Devices (keyboards, mice).

    No 10GB OS involvement.


    It seemed to take quite a few years before the early Linuxes I played
    around with 25+ years ago managed to support USB, among other things.

    So it's only easy if you know how. Reading the current key on the Z80
    (already in ASCII) was one IN instruction.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Scott Lurndal on Tue Jan 16 23:00:50 2024
    On 16/01/2024 19:45, Scott Lurndal wrote:
    David Brown <david.brown@hesbynett.no> writes:
    On 16/01/2024 17:02, Scott Lurndal wrote:
    David Brown <david.brown@hesbynett.no> writes:
    On 15/01/2024 21:41, Scott Lurndal wrote:
    David Brown <david.brown@hesbynett.no> writes:
    On 15/01/2024 18:30, Scott Lurndal wrote:
    David Brown <david.brown@hesbynett.no> writes:
    On 15/01/2024 03:14, bart wrote:

    <snip>

    The only compiler I used with an inline
    assembly of comparable power and integration to gcc's, but a different >>>>>>>> syntax, was Diab Data.

    Now there's a name I haven't heard in decades. What ever happened >>>>>>> to them? We worked with them back in the early 90's using their >>>>>>> 88100 compiler. It was impressive, particularly compared to the >>>>>>> PCC port that Motorola provided. Greenhills was ok, but diab
    produced better code. gcc was still pretty primitive in those days. >>>>>>>
    A good Norweigian company.

    A good /Swedish/ company, not Norwegian!

    Hm. I could have sworn the folks we dealt with were
    in Norway - perhaps a branch office?


    It would be a little surprising, but certainly possible. Sweden has had >>>> been quite significant in the compiler world - IAR is a big name in
    embedded toolchains, and they are Swedish.

    You are sure you are not just one of these ignorant parochial Merican's >>>> who think Norway is the capital of Sweden? :-)]


    I hope you noticed the smiley :-)

    No, I'm 7/8th norwegian, with a bit of swiss. While I haven't visited (yet),
    I do have relatives there. Think Luren dal.


    I would say you are of Norwegian decent, or have Norwegian family roots
    - it's not the same as being Norwegian.

    Point.

    You need to at least visit the Country!

    My folks have been there a couple of times, and looked
    up distant relatives from both sides (the other side
    was from the Bergen area, IIRC).

    Bergen's a nice city. It rains a lot, but otherwise it's a pleasant place.


    Alternatively, you need to eat a /lot/ of brunost to improve
    your credentials.

    Does lutefisk and lefse count?

    Everyone likes lefser, but if you can claim to like lutefisk with a
    straight face, you must be Norwegian!

    (For those that don't know, "lutefisk" is made by drying cod completely,
    then soaking it in draincleaner, then washing it, then boiling it. It
    doesn't beat the Swedish canned fermented fish or Icelandic sharks
    buried for months in the sand, but it's definitely not something to be
    eaten lightly.)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to David Brown on Tue Jan 16 22:10:05 2024
    David Brown <david.brown@hesbynett.no> writes:
    On 16/01/2024 19:45, Scott Lurndal wrote:
    David Brown <david.brown@hesbynett.no> writes:

    Alternatively, you need to eat a /lot/ of brunost to improve
    your credentials.

    Does lutefisk and lefse count?

    Everyone likes lefser, but if you can claim to like lutefisk with a
    straight face, you must be Norwegian!

    Eat? Yes. Holiday tradition.
    Like? One can eat anything if it is drowned in enough butter.

    The feeling of that gelatinous mass sliding down the back
    of your throat is unforgettable.


    (For those that don't know, "lutefisk" is made by drying cod completely,
    then soaking it in draincleaner, then washing it, then boiling it. It >doesn't beat the Swedish canned fermented fish or Icelandic sharks
    buried for months in the sand, but it's definitely not something to be
    eaten lightly.)


    David is not exaggerating about the drain cleaner....

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Richard Harnden@21:1/5 to David Brown on Tue Jan 16 22:18:22 2024
    On 16/01/2024 22:00, David Brown wrote:

    (For those that don't know, "lutefisk" is made by drying cod completely,
    then soaking it in draincleaner, then washing it, then boiling it.  It doesn't beat the Swedish canned fermented fish or Icelandic sharks
    buried for months in the sand, but it's definitely not something to be
    eaten lightly.)


    Local: It's a delicacy
    Jeremy Clarkson: Haven't you people heard of chocolate?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to David Brown on Tue Jan 16 22:42:57 2024
    On 16/01/2024 17:46, David Brown wrote:
    On 16/01/2024 16:15, bart wrote:

    void swap_lots(const uint32_t * restrict in, uint32_t * restrict out, uint32_t n) {
        while (n--) {
            *out++ = swapEndian32(*in++);
            *out++ = swapEndian32(*in++);
            *out++ = swapEndian32(*in++);
            *out++ = swapEndian32(*in++);
        }
    }

    What's 'n' here? Are 4n bytes being transformed, or 16n?

    My function would be this on x64 (although I don't support bswap):

        fun swapends64(u64 x)u64 = assem mov rax, [x]; bswap rax; end


    And how efficient would your "swap_lots" function be?

    How do you measure efficiency? This task seems memory-bound anyway.

    Using exactly that function (I now support 'bswap'), I can process a 100M-element u64 array (0.8GB) in .35 seconds, or 2.3GB/second.

    Using an inlining macro didn't make much difference.

    I then tried adding 'byteswap' as an intrinsic operator within my
    language. Then that 2.3GB/s became 3.4GB/s. I used a simple loop with no unrolling.

    I can't run your code directly, as a I don't have 'rev'. But adjusting
    that swap function to instead apply '~', processing a 200M-element u32
    array (also 0.8GB), took 0.22 seconds or 3.6GB/s. (This was doing n/4 iterations of your unrolled loop.)

    But this is not really about inline assembly any more, but ways to make
    best use of memory bandwidth.

    (I have an advantage in working 64 bits at a time, but you'd think gcc
    would be able to take care of that; wasn't that your point?)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Kaz Kylheku on Wed Jan 17 00:11:01 2024
    On 16/01/2024 18:39, Kaz Kylheku wrote:
    On 2024-01-16, bart <bc@freeuk.com> wrote:

    gcc is a project 10s of 1000s of files. If a particular configuration
    targets one architecture out of 100, it can also support the ASM syntax
    for that architecture.

    Not reasonably so. The parser of every front end would have to have
    a special case for that assembly language.

    The proposal does not pass a cost/benefit analysis, due to the
    high cost and low benefit.

    You don't need to have the ASM syntax embedded within the C grammar. Not
    so specifically anyway; you allow a bunch of keyword and register tokens
    within asm{...}.

    keywords and tokens are grammar.

    So how does the grammar of HTML work: does it also include the entire
    grammar of JavaScript?

    JS appears inside <script> ... </script> tags.

    My assemble appears in a 'assem ... end block', or there's a one-liner
    version which starts with 'asm'.

    When my parser sees 'assem' or 'asm', it calls one of these functions:

    if lx.subcode=0 then
    p:=readassemline()
    else
    p:=readassemblock()
    fi

    They are in a 250-line module that deals with assembly. They return an
    'assem' AST node.

    Now, my assembly syntax is not Intel, just Intel style, and is flavoured
    with elements of my HLL syntax (so uses 'u32' for example rather than
    'dword').

    There is a separate set of tables within the target-specific backend,
    which contains all the supported x64 opcodes and registgers for example.
    This part is needed anyway to generate x64 native code, whether assembly
    source or binary.

    When the global symbol table is initialised, the contents of those
    opcode and register tables are added to it.

    It's done in a way so that, if not in an ASM block, those can be used as
    normal identifiers:

    int mov, rax, push

    mov := rax + push

    If I wanted to refer to those from inline assembly, I have to escape them:

    asm push [`push]

    So, with a different architecture, I have a different backend and
    different set opcode and register names.

    The 250-line parsing module could possibly be made to deal with more
    than one target, otherwise it's not a great imposition.

    This stuff needn't be that hard. There just seems unwillingness to make
    things easy and sensible.

    Perhaps in a world full of C macros, makefile syntax and bash scripts,
    the assembly syntax isn't considered too outre.

    But, you say assembly isn't used much; have you considered that that
    might be because it's such a pig to use?



    How things look does become important when you are writing tens of
    thousands of lines of code.

    But not thousands, or even hundreds? Or dozens? Code can always benefit
    from being readable and also not being painful to type.

    Personally I think /any/ conventional assembly looks dreadful in even
    small amounts, including mine. But gcc-style is a LOT worse.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Chris M. Thomasson on Wed Jan 17 02:21:57 2024
    On 2024-01-17, Chris M. Thomasson <chris.m.thomasson.1@gmail.com> wrote:
    On 1/16/2024 5:38 AM, David Brown wrote:
    On 16/01/2024 03:18, Chris M. Thomasson wrote:
    On 1/15/2024 3:24 PM, Kaz Kylheku wrote:
    On 2024-01-15, Chris M. Thomasson <chris.m.thomasson.1@gmail.com> wrote: >>>>> On 1/15/2024 8:29 AM, David Brown wrote:
    On 15/01/2024 17:04, David Brown wrote:
    On 15/01/2024 08:40, Kaz Kylheku wrote:
    [...]
    You've mentioned this many times - do you have a reference that gives
    the source of this function (at the time when there was an issue), and a
    description or report of what you think gcc did wrong?  I am curious as
    to whether it was a bug in the code or a bug in gcc (gcc is certainly
    not bug-free).


    I think I found it David!

    https://groups.google.com/g/comp.programming.threads/c/Y_Y2DZOWErM/m/nuyEoKq0onUJ

    David Schwartz has since been acquired by and merged with David
    Butenhoff, to form David Schwartzenhoff. The new motto is "bigger living through slightly less concurrency".

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Chris M. Thomasson on Wed Jan 17 03:03:47 2024
    On 2024-01-17, Chris M. Thomasson <chris.m.thomasson.1@gmail.com> wrote:
    On 1/16/2024 5:08 PM, Chris M. Thomasson wrote:
    On 1/16/2024 5:38 AM, David Brown wrote:
    On 16/01/2024 03:18, Chris M. Thomasson wrote:
    On 1/15/2024 3:24 PM, Kaz Kylheku wrote:
    On 2024-01-15, Chris M. Thomasson <chris.m.thomasson.1@gmail.com>
    wrote:
    On 1/15/2024 8:29 AM, David Brown wrote:
    On 15/01/2024 17:04, David Brown wrote:
    On 15/01/2024 08:40, Kaz Kylheku wrote:
    [...]
    You've mentioned this many times - do you have a reference that gives
    the source of this function (at the time when there was an issue), and
    a description or report of what you think gcc did wrong?  I am curious
    as to whether it was a bug in the code or a bug in gcc (gcc is
    certainly not bug-free).


    I think I found it David!

    https://groups.google.com/g/comp.programming.threads/c/Y_Y2DZOWErM/m/nuyEoKq0onUJ





    Here is a nice quote from Dave Butenhof in that thread: ____________________________

    Dave Butenhof's profile photo
    Dave Butenhof
    Nov 15, 2007, 3:12:14 PM
    to
    Chris Thomasson wrote:
    "Zeljko Vrba" <zvrba....@ieee-sb1.cc.fer.hr> wrote in message news:slrnfjnt6l...@ieee-sb1.cc.fer.hr...
    On 2007-11-14, Chris Thomasson <cri...@comcast.net> wrote:

    If GCC performs the optimization that David Schwartz pointer out, your >>> basically screwed. AFAICT, GCC is totally busted if it allows stores to >>> escape a critical-section. This is a race-condition waiting to
    happen. I am

    How is the compiler supposed to know where a CS begins and ends? should
    it have a knowledge of every imaginable official and unofficial API?

    I was under the impression that POSIX puts some restrictions on
    compilers. Humm... I can't really remember where I heard that right now, but I sure think I did. Humm...
    The point is that POSIX puts restrictions on the behavior of a
    conforming system. That includes library, kernel, and compiler. If the
    RESULT doesn't behave like POSIX, then it's not POSIX.

    A compiler that's part of a conforming POSIX system environment can't generate code that breaks synchronization. How it and the rest of the
    system accomplish that is unspecified.

    Often, it means simply not performing risky optimizations. But if they
    are enabled, then the system needs to be able to detect and avoid
    performing them in "dangerous" areas of code. (A complicated problem,
    but nothing's impossible.)

    Basically, the optimization can only be performed when the value
    which controls the selection statement can be shown not to have
    been derived from the result of a pthread function, such that the value indicates whether the caller owns or does not own a lock.

    This is very difficult.

    Even a value scanned from a stream can be such a value:

    int result = pthred_mutex_trylock(...);

    FILE *f = fopen("foo.txt", "w");
    fprintf(f, "%d\n", result);
    fclose(f);
    system("mv foo.txt bar.txt");

    int x;
    FILE *g = fopen("bar.txt", "r");
    fscanf(g, "%d", &f);
    fclose(g);

    if (x == 0) {
    a++;
    }

    A better angle on it would be not to pursue the analysis of x at all,
    but concentrate on a. If the variable a isn't something that can be
    thread shared, then the optimization is okay.

    If a is a local variable whose address has never been taken (or
    else has never escaped from this scope), then it's should be
    to convert this to:

    int __inc = (x == 0);
    a += __inc;

    If a is something potentially shared, then no.

    Programs that want good optimization should concentrate on using
    automatic (not static) local variables as much as possible, in such a
    way that it's clear that those variables don't escape into other scopes.
    Those kinds of variables can be aggressively optimized, threads or not.


    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Scott Lurndal on Wed Jan 17 11:16:28 2024
    On 12/01/2024 16:50, Scott Lurndal wrote:
    bart <bc@freeuk.com> writes:
    On 12/01/2024 13:40, David Brown wrote:
    On 12/01/2024 00:20, bart wrote:

    But with 'as', it just sits there. I wonder what it's waiting for; for
    me to type in ASM code live from the terminal?

    It does that so you can pipe the assembler source code in to the
    assembler.

    $ cat file.s | as

    $ cat file.c | cpp | c0 | c1 | c2 | as > file.o

    or, if you want, you can type in the assembler source directly.

    Or you can save it in a file and supply the file argument to the command.

    None of which your stuff supports, which makes it useless to me.


    I had a spare 15 minutes so I got my scripting language to do this:

    csource:=(
    "#include <stdio.h>",
    "int main(void) {",
    " puts(""Fahrenheit 451"");",
    "}")

    csource -> mcc -> aa -> run

    'mcc' turns a source string (or a list of strings as here) into a string containing assembly code.

    'aa' turns an assembly string into a string containing binary PE data.

    'run' runs that PE data. Output is:

    Fahrenheit 451

    Those 3 functions are 40-50 lines. Here's another invocation:

    readstrfile("/c/hello.c") -> mcc -> aa -> run

    A shell seems to be a poor man's scripting language.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to bart on Wed Jan 17 12:41:01 2024
    On 16/01/2024 22:06, bart wrote:
    On 16/01/2024 20:09, Scott Lurndal wrote:
    bart <bc@freeuk.com> writes:
    On 15/01/2024 11:45, David Brown wrote:
    On 12/01/2024 18:09, bart wrote:

    (BTW that keyboard was a joy to use: it used a simple 8-bit port, with
    the top bit strobing when a key was ready. Compare with what's involved
    with using a USB keyboard today, if you didn't have a 10GB OS to take
    care of it.)

    If you ignore,  of course, the fact that a 64KB BIOS can easily handle
    the entire USB stack sufficient to support both USB mass storage
    devices, networking devices (PXE) and USB Human Interface Devices
    (keyboards, mice).

    No 10GB OS involvement.


    It seemed to take quite a few years before the early Linuxes I played
    around with 25+ years ago managed to support USB, among other things.

    At that time, device support on Linux was very limited. But it took
    off, and since perhaps 15 years ago it is rarely an issue in practice.
    Out of the box support is certainly a world ahead of Windows, and new
    buses (such as new USB speeds) are always supported first on Linux.

    The last time I had trouble with USB and keyboards was something like 5
    or 6 years ago - with Windows. It was a Dell server, where all the USB
    ports were USB 3. The keyboard and mice were fine in the BIOS and Dell
    setup stuff, but partway through the Windows Server installation,
    Windows wanted to move to its own system and drivers - and did not have
    USB 3 support out the box. It was an utterly absurd situation where the installation process needed an extra drivers disk from Dell just to get
    the keyboard working, even though it worked fine in the first part of
    the installation. (And needless to say, Linux had no issues with it.)


    So it's only easy if you know how. Reading the current key on the Z80 (already in ASCII) was one IN instruction.

    Making support for USB keyboards (either as host or device) on modern microcontrollers isn't bad, assuming you have basic USB libraries for
    your device. It's certainly much more involved than it used to be - but
    it is also more flexible.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Chris M. Thomasson on Wed Jan 17 13:43:29 2024
    On 17/01/2024 02:04, Chris M. Thomasson wrote:
    On 1/16/2024 6:06 AM, David Brown wrote:

    Well, you don't often write inline assembly - its rare to write it.
    It's typically the kind of thing you write once for your particular
    instruction, then stick it away in a header somewhere.  You might use
    it often, but you don't need to read or edit the code often.[...]

    As soon as you use inline assembler in a file, you sort of "need" to?

    Nonsense.

    How often do you use headers from the standard library, or third party libraries, or other code? All the time, I'd assume. How often do you
    read through these headers? Very rarely. How often do you edit them?
    Never.

    You do not need to read code of any kind, in order to use it.

    Some kinds of code can be considered advanced, or ugly, or hard to
    comprehend. inline assembly often falls into that category, as do
    complicated macros, and some of the more cryptic corners of C++. That
    code should be written by someone who knows what they are doing, tested
    well, the interface described, and then the code is hidden away. Most
    other people using the code never see it, and might not understand it
    even if they did.


    It is entirely reasonable to structure your headers like this:

    // Return x + (y * z)
    static inline int madd(int x, int y, int z);

    ...

    // Implementation details
    static inline int madd(int x, int y, int z) {
    asm (...




    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Chris M. Thomasson on Wed Jan 17 13:28:33 2024
    On 17/01/2024 02:08, Chris M. Thomasson wrote:
    On 1/16/2024 5:38 AM, David Brown wrote:
    On 16/01/2024 03:18, Chris M. Thomasson wrote:
    On 1/15/2024 3:24 PM, Kaz Kylheku wrote:
    On 2024-01-15, Chris M. Thomasson <chris.m.thomasson.1@gmail.com>
    wrote:
    On 1/15/2024 8:29 AM, David Brown wrote:
    On 15/01/2024 17:04, David Brown wrote:
    On 15/01/2024 08:40, Kaz Kylheku wrote:
    [...]
    You've mentioned this many times - do you have a reference that gives
    the source of this function (at the time when there was an issue), and
    a description or report of what you think gcc did wrong?  I am curious
    as to whether it was a bug in the code or a bug in gcc (gcc is
    certainly not bug-free).


    I think I found it David!

    https://groups.google.com/g/comp.programming.threads/c/Y_Y2DZOWErM/m/nuyEoKq0onUJ


    Looking at the start of that thread:

    int trylock()
    {
    int res;
    res = pthread_mutex_trylock(&mutex);
    if (res == 0)
    ++acquires_count;
    return res;
    }

    It is perfectly reasonable for a pre-C11 C compiler to change that to:

    int trylock()
    {
    int res;
    res = pthread_mutex_trylock(&mutex);
    int x = acquires_count;
    if (res == 0)
    ++x;
    acquires_count = x;
    return res;
    }

    That's the way C was defined, prior to C11 support for multithreading environments. The compiler could assume that it alone had full control
    over all data, unless it was defined as volatile. So to make this code "correct" (according to the user's hopes) in pre-C11, "acquires_count"
    must be volatile.

    C11 disabled such optimisations, because they could introduce data races
    that did not exist before. But C11 was not around at that time. So
    having this optimisation was not a gcc bug - unless POSIX precluded such optimisations and gcc was used in a POSIX-compatibility mode. (I don't
    know the details of POSIX requirements.)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to bart on Wed Jan 17 14:25:54 2024
    On 16/01/2024 23:42, bart wrote:
    On 16/01/2024 17:46, David Brown wrote:
    On 16/01/2024 16:15, bart wrote:

    void swap_lots(const uint32_t * restrict in, uint32_t * restrict out,
    uint32_t n) {
         while (n--) {
             *out++ = swapEndian32(*in++);
             *out++ = swapEndian32(*in++);
             *out++ = swapEndian32(*in++);
             *out++ = swapEndian32(*in++);
         }
    }

    What's 'n' here? Are 4n bytes being transformed, or 16n?

    In this case, 4n 32-bit words. It's just example code to demonstrate a
    point, not to be a particularly useful function in reality.


    My function would be this on x64 (although I don't support bswap):

        fun swapends64(u64 x)u64 = assem mov rax, [x]; bswap rax; end


    And how efficient would your "swap_lots" function be?

    How do you measure efficiency? This task seems memory-bound anyway.


    That will depend on the sizes, cache, etc., as well as the target. The
    target I was using here is a Cortex M7 - with data in tightly-coupled
    memory that runs at core speed, it would not be memory bound.

    Efficiency is measured in clock cycles. (It can also be measured in
    code size, but that's usually not as important. If it were, we would
    not be doing manual loop unrolling here.)

    Using exactly that function (I now support 'bswap'), I can process a 100M-element u64 array (0.8GB) in .35 seconds, or 2.3GB/second.


    On what target? Do you have an ARM Cortex-M microcontroller for testing
    this?

    Using a built-in bswap function kind of defeats the point of using
    inline assembly. gcc has __builtin_bswap32 too, or it can be written
    manually in C and optimised by the compiler to "rev" instructions. The
    point is a demonstration of how gcc's inline assembly can work with the optimiser for surrounding code, not how fast your PC can swap endianness!

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to David Brown on Wed Jan 17 14:51:54 2024
    On 17/01/2024 13:25, David Brown wrote:
    On 16/01/2024 23:42, bart wrote:
    On 16/01/2024 17:46, David Brown wrote:
    On 16/01/2024 16:15, bart wrote:

    void swap_lots(const uint32_t * restrict in, uint32_t * restrict out,
    uint32_t n) {
         while (n--) {
             *out++ = swapEndian32(*in++);
             *out++ = swapEndian32(*in++);
             *out++ = swapEndian32(*in++);
             *out++ = swapEndian32(*in++);
         }
    }

    What's 'n' here? Are 4n bytes being transformed, or 16n?

    In this case, 4n 32-bit words.  It's just example code to demonstrate a point, not to be a particularly useful function in reality.


    My function would be this on x64 (although I don't support bswap):

        fun swapends64(u64 x)u64 = assem mov rax, [x]; bswap rax; end


    And how efficient would your "swap_lots" function be?

    How do you measure efficiency? This task seems memory-bound anyway.


    That will depend on the sizes, cache, etc., as well as the target.  The target I was using here is a Cortex M7 - with data in tightly-coupled
    memory that runs at core speed, it would not be memory bound.

    Efficiency is measured in clock cycles.  (It can also be measured in
    code size, but that's usually not as important.  If it were, we would
    not be doing manual loop unrolling here.)

    Using exactly that function (I now support 'bswap'), I can process a
    100M-element u64 array (0.8GB) in .35 seconds, or 2.3GB/second.


    On what target?  Do you have an ARM Cortex-M microcontroller for testing this?

    I have an RPi4 somewhere; I only know it has a 64-bit ARM chip. ARM
    processor and architecture models are mystery to me. My tests were done
    on some AMD Ryzen device, probably bottom of the range.

    (x64 processors are a bit of a mystery too! I just have little interest
    beyond whether it's x64 or ARM64; I don't come across anything else.)

    Using a built-in bswap function kind of defeats the point of using
    inline assembly.

    I didn't support 'bswap' at all. I first has to add it to my assembler,
    then roll that out to the backend of my compiler.

    This was necessary both to be able to use it in inline assembler, and
    for the compiler backend to be able to deal with that instruction,
    whatever had generated it.

    So I wanted to know whether building it to the language in would be
    worth doing, performance-wise (probably not). Although there are other advantages of having it as an operator, since it could be used in-place
    as 'bswap:=A[i]', and here it would be able to choose 32- and 64-bit variations.

    But it also highlighted issues with my inline assembler within macros
    used to emulate inline functions, such as this:

    macro byteswap(x) = assem mov rax, [x] ...

    'x' can only be a simple variable at the invocation, not an arbitrary expression like 'A[i]'. This suggested an enhancement:

    assem (expr) ...

    which first evaluates the expression to a known register. (I could just
    do 'expr; assem ...' which /probably/ does the same, but it's risky.)

    So doing the exercise helped in determining what might be a suitable compromise. Although it would need explicit forms for 32- vs 64-bit
    operations.

    ('assem(expr) ...' is also partway to doing what gcc asm{} does in
    creating an interface, but retaining the same syntax.)

      gcc has __builtin_bswap32 too, or it can be written
    manually in C and optimised by the compiler to "rev" instructions.  The point is a demonstration of how gcc's inline assembly can work with the optimiser for surrounding code, not how fast your PC can swap endianness!

    It showed that using an actual function call, and not unrolling loops,
    wasn't that slow, not on my PC.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to bart on Wed Jan 17 16:11:22 2024
    bart <bc@freeuk.com> writes:
    On 16/01/2024 18:39, Kaz Kylheku wrote:
    On 2024-01-16, bart <bc@freeuk.com> wrote:

    gcc is a project 10s of 1000s of files. If a particular configuration
    targets one architecture out of 100, it can also support the ASM syntax
    for that architecture.

    Not reasonably so. The parser of every front end would have to have
    a special case for that assembly language.

    The proposal does not pass a cost/benefit analysis, due to the
    high cost and low benefit.

    You don't need to have the ASM syntax embedded within the C grammar. Not >>> so specifically anyway; you allow a bunch of keyword and register tokens >>> within asm{...}.

    keywords and tokens are grammar.

    So how does the grammar of HTML work: does it also include the entire
    grammar of JavaScript?

    JS appears inside <script> ... </script> tags.

    From the structured general markup language, or extensable markup
    language (SGML/XML) perspective, anything between tags is completely
    invisible. Semantics of any tag is Application dependent.

    HTML (a non-proper subset of XML) applies semantics to
    tags defined by HTML such as <script/>, <a/>, et alia.

    An HTML interpreter interprets the tags and will pass the
    data to the appropriate entity (in this case, a javascript
    or ecmascript interpreter).

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to bart on Wed Jan 17 19:07:25 2024
    On 17/01/2024 15:51, bart wrote:
    On 17/01/2024 13:25, David Brown wrote:
    On 16/01/2024 23:42, bart wrote:
    On 16/01/2024 17:46, David Brown wrote:
    On 16/01/2024 16:15, bart wrote:

    void swap_lots(const uint32_t * restrict in, uint32_t * restrict
    out, uint32_t n) {
         while (n--) {
             *out++ = swapEndian32(*in++);
             *out++ = swapEndian32(*in++);
             *out++ = swapEndian32(*in++);
             *out++ = swapEndian32(*in++);
         }
    }

    What's 'n' here? Are 4n bytes being transformed, or 16n?

    In this case, 4n 32-bit words.  It's just example code to demonstrate
    a point, not to be a particularly useful function in reality.


    My function would be this on x64 (although I don't support bswap):

        fun swapends64(u64 x)u64 = assem mov rax, [x]; bswap rax; end >>>>>

    And how efficient would your "swap_lots" function be?

    How do you measure efficiency? This task seems memory-bound anyway.


    That will depend on the sizes, cache, etc., as well as the target.
    The target I was using here is a Cortex M7 - with data in
    tightly-coupled memory that runs at core speed, it would not be memory
    bound.

    Efficiency is measured in clock cycles.  (It can also be measured in
    code size, but that's usually not as important.  If it were, we would
    not be doing manual loop unrolling here.)

    Using exactly that function (I now support 'bswap'), I can process a
    100M-element u64 array (0.8GB) in .35 seconds, or 2.3GB/second.


    On what target?  Do you have an ARM Cortex-M microcontroller for
    testing this?

    I have an RPi4 somewhere; I only know it has a 64-bit ARM chip. ARM
    processor and architecture models are mystery to me. My tests were done
    on some AMD Ryzen device, probably bottom of the range.

    The ARM processors on a Pi are much more like a PC processor than an ARM microcontroller (even though the M7 I use mostly is 600 MHz).


    (x64 processors are a bit of a mystery too! I just have little interest beyond whether it's x64 or ARM64; I don't come across anything else.)

    Using a built-in bswap function kind of defeats the point of using
    inline assembly.

    I didn't support 'bswap' at all. I first has to add it to my assembler,
    then roll that out to the backend of my compiler.

    This was necessary both to be able to use it in inline assembler, and
    for the compiler backend to be able to deal with that instruction,
    whatever had generated it.

    So I wanted to know whether building it to the language in would be
    worth doing, performance-wise (probably not). Although there are other advantages of having it as an operator, since it could be used in-place
    as 'bswap:=A[i]', and here it would be able to choose 32- and 64-bit variations.

    But it also highlighted issues with my inline assembler within macros
    used to emulate inline functions, such as this:

        macro byteswap(x) = assem mov rax, [x] ...

    'x' can only be a simple variable at the invocation, not an arbitrary expression like 'A[i]'. This suggested an enhancement:

       assem (expr) ...

    which first evaluates the expression to a known register.

    This sounds like you are taking inspiration from the discussion and
    making your language support more of gcc's inline assembly features!

    (I could just
    do 'expr; assem ...' which /probably/ does the same, but it's risky.)

    So doing the exercise helped in determining what might be a suitable compromise. Although it would need explicit forms for 32- vs 64-bit operations.

    ('assem(expr) ...' is also partway to doing what gcc asm{} does in
    creating an interface, but retaining the same syntax.)

      gcc has __builtin_bswap32 too, or it can be written manually in C
    and optimised by the compiler to "rev" instructions.  The point is a
    demonstration of how gcc's inline assembly can work with the optimiser
    for surrounding code, not how fast your PC can swap endianness!

    It showed that using an actual function call, and not unrolling loops,
    wasn't that slow, not on my PC.


    It showed that - unsurprisingly - running this on a huge block of data
    is determined by cache and memory speed. It probably doesn't make much difference to your timings if "bswap" returned its argument untouched.

    Thus you missed the point about optimising code. (You did, however,
    show that optimising code is not always important - if the code itself
    is not the bottleneck, optimising it won't help.)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to bart on Wed Jan 17 18:47:01 2024
    On 2024-01-17, bart <bc@freeuk.com> wrote:
    On 12/01/2024 16:50, Scott Lurndal wrote:
    bart <bc@freeuk.com> writes:
    On 12/01/2024 13:40, David Brown wrote:
    On 12/01/2024 00:20, bart wrote:

    But with 'as', it just sits there. I wonder what it's waiting for; for
    me to type in ASM code live from the terminal?

    It does that so you can pipe the assembler source code in to the
    assembler.

    $ cat file.s | as

    $ cat file.c | cpp | c0 | c1 | c2 | as > file.o

    or, if you want, you can type in the assembler source directly.

    Or you can save it in a file and supply the file argument to the command.

    None of which your stuff supports, which makes it useless to me.

    I had a spare 15 minutes so I got my scripting language to do this:

    csource:=(
    "#include <stdio.h>",
    "int main(void) {",
    " puts(""Fahrenheit 451"");",
    "}")

    csource -> mcc -> aa -> run

    'mcc' turns a source string (or a list of strings as here) into a string containing assembly code.

    'aa' turns an assembly string into a string containing binary PE data.

    'run' runs that PE data. Output is:

    Fahrenheit 451

    Those 3 functions are 40-50 lines. Here's another invocation:

    Scott's script is just that one line; just the external commands are
    called. It doesn't require function wrappers around external commands.

    We have a ton of useful scripting languages; yet the shell is not
    going away for process control tasks.

    The only reason to use the shell for larger coding tasks that go
    beyonod process control is that is that it's the only language you can
    rely on being installed.

    A good many scripting languages require a shell in order to build,
    such as for runing a ./configure script.

    Those projects aren't doing that in order to amuse the user with
    irony; boostrapping always proceeds from what we have available, to what
    we would like to ultimately use.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Kaz Kylheku on Wed Jan 17 19:42:54 2024
    On 17/01/2024 18:47, Kaz Kylheku wrote:
    On 2024-01-17, bart <bc@freeuk.com> wrote:

    csource -> mcc -> aa -> run

    'mcc' turns a source string (or a list of strings as here) into a string
    containing assembly code.

    'aa' turns an assembly string into a string containing binary PE data.

    'run' runs that PE data. Output is:

    Fahrenheit 451

    Those 3 functions are 40-50 lines. Here's another invocation:

    Scott's script is just that one line; just the external commands are
    called.

    So was my invocation. Scott's example didn't provide source code, it
    comes from a file, as did my second example.

    It doesn't require function wrappers around external commands.

    We have a ton of useful scripting languages; yet the shell is not
    going away for process control tasks.

    The only reason to use the shell for larger coding tasks that go
    beyonod process control is that is that it's the only language you can
    rely on being installed.

    One big problem is that it tends to be OS-specific so is not
    cross-platform. So you can't run .BAT files on Linux, and you can't run
    . files (that is, Linux shell scripts) on Windows. (File extensions
    would be mighty useful here!)

    Real scripting languages can work across OSes and also come with their
    own libraries. Linux shell seems to require most of Linux to function.


    A good many scripting languages require a shell in order to build,
    such as for runing a ./configure script.

    Funnily enough, mine don't.

    My scripting language builds with this one command on Windows (and
    without invoking dozens of other processes; just itself):

    mm qq

    When I was testing it on Linux, I could build it on Linux from original
    sources using:

    ./mu qq

    (This used a C intermediate file and needed a C installation. 'mu' is a
    binary that could itself be built with a command like: cc mu.c ...)

    I'm not suggesting mine is used, just saying it is possible to have a cross-platform language on a small scale and that can be built using a
    minimal tool-set.

    Certainly one that is sufficient for the minor scripting tasks we're
    taking about.

    (As for Bash, is it a language running in a permanent REPL loop, or is
    it more of an application?)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to bart on Wed Jan 17 22:18:43 2024
    bart <bc@freeuk.com> writes:
    On 17/01/2024 18:47, Kaz Kylheku wrote:
    On 2024-01-17, bart <bc@freeuk.com> wrote:

    A good many scripting languages require a shell in order to build,
    such as for runing a ./configure script.

    Funnily enough, mine don't.

    Perhaps you find it humorous. A posix shell comes with pretty
    much every system used for software development (even windows
    has WSL for serious software development).

    Your scripting language isn't available on any of them.

    (As for Bash, is it a language running in a permanent REPL loop, or is
    it more of an application?)

    It's an executable, just like everything else on unix/linux.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Scott Lurndal on Wed Jan 17 23:48:30 2024
    On 17/01/2024 22:18, Scott Lurndal wrote:
    bart <bc@freeuk.com> writes:
    On 17/01/2024 18:47, Kaz Kylheku wrote:
    On 2024-01-17, bart <bc@freeuk.com> wrote:

    A good many scripting languages require a shell in order to build,
    such as for runing a ./configure script.

    Funnily enough, mine don't.

    Perhaps you find it humorous. A posix shell comes with pretty
    much every system used for software development (even windows
    has WSL for serious software development).

    I suspect it's more that it's been browbeaten into having such a system
    because so many developers were deliberately, inadvertently or
    spitefully building in so many dependencies from the Unix world into
    their software and their procedures.

    Your scripting language isn't available on any of them.

    (As for Bash, is it a language running in a permanent REPL loop, or is
    it more of an application?)

    It's an executable, just like everything else on unix/linux.

    So, it's code, just like on every computer.

    That's useful to know, thanks.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to bart on Thu Jan 18 00:25:16 2024
    On 2024-01-17, bart <bc@freeuk.com> wrote:
    On 17/01/2024 22:18, Scott Lurndal wrote:
    bart <bc@freeuk.com> writes:
    On 17/01/2024 18:47, Kaz Kylheku wrote:
    On 2024-01-17, bart <bc@freeuk.com> wrote:

    A good many scripting languages require a shell in order to build,
    such as for runing a ./configure script.

    Funnily enough, mine don't.

    Perhaps you find it humorous. A posix shell comes with pretty
    much every system used for software development (even windows
    has WSL for serious software development).

    I suspect it's more that it's been browbeaten into having such a system because so many developers were deliberately, inadvertently or
    spitefully building in so many dependencies from the Unix world into
    their software and their procedures.

    You can get shell scripts running on Windows. Macs support then
    natively.

    The availability of the shell language is better than any other;
    the places where it doesn't run out of the box are not many.

    Also, no operating system that is still in wide use has a better
    command language.

    We could imagine, say, writing the build scripting of a project in the
    batch file language of CMD.EXE, and making that work on all the
    platforms: Mac, GNU Linuxes, Solaris, OpenBSD, Android/Termux what
    have you.

    For all the inconvenience of having to install that interpreter first,
    we would then have to contend with a comically poor quality language.
    It's worse than some CS freshman's semester project.

    Shell has multiple implementations. At one point I tested the ./configure script of my TXR project with Bash, Zsh, Dash, Ash, public domain Ksh,
    as well as some POSIX shells of some proprietary Unixes like Solaris's
    POSIX shell.

    Though the shell language is quirky, it is well specified and understood
    to the point that fairly complex code can be written that works across
    multiple implementations.

    As far as Windows goes, nobody actually builds my program for Windows
    other than me. I provide a GUI installer. That's what you do for
    Windows. How your stuff builds on Windows is almost immaterial.

    The only people building your FOSS stuff from code are (1) maintainers
    of FOSS distros, who always use environments with shells and make, etc;
    and (2) a few enthusiast users who build for themselves, also in such environments. Anyone who has something to do on Windows gets your
    installer.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Kaz Kylheku on Thu Jan 18 00:47:57 2024
    On 18/01/2024 00:25, Kaz Kylheku wrote:
    On 2024-01-17, bart <bc@freeuk.com> wrote:
    On 17/01/2024 22:18, Scott Lurndal wrote:
    bart <bc@freeuk.com> writes:
    On 17/01/2024 18:47, Kaz Kylheku wrote:
    On 2024-01-17, bart <bc@freeuk.com> wrote:

    A good many scripting languages require a shell in order to build,
    such as for runing a ./configure script.

    Funnily enough, mine don't.

    Perhaps you find it humorous. A posix shell comes with pretty
    much every system used for software development (even windows
    has WSL for serious software development).

    I suspect it's more that it's been browbeaten into having such a system
    because so many developers were deliberately, inadvertently or
    spitefully building in so many dependencies from the Unix world into
    their software and their procedures.

    You can get shell scripts running on Windows.

    You mean via layers like WSL or MSYS2 or CYGWIN? I don't really consider
    that running on Windows.

    Macs support then
    natively.

    The availability of the shell language is better than any other;
    the places where it doesn't run out of the box are not many.

    Also, no operating system that is still in wide use has a better
    command language.

    Yes, I have an idea what you mean by better. Something that gives low
    priority to user-friendliness and that is bristling with advanced
    features that mean loads of gotchas.

    I'm not sure what happened on my journey with computers that I'm totally oblivious to shells, other than they provide the most basic commands to manipulate files and launch programs.

    We could imagine, say, writing the build scripting of a project in the
    batch file language of CMD.EXE, and making that work on all the
    platforms: Mac, GNU Linuxes, Solaris, OpenBSD, Android/Termux what
    have you.

    For all the inconvenience of having to install that interpreter first,
    we would then have to contend with a comically poor quality language.
    It's worse than some CS freshman's semester project.

    What language do you have in mind? (Bear in mind that this is exactly
    what I've long thought about C.)


    Shell has multiple implementations. At one point I tested the ./configure script of my TXR project with Bash, Zsh, Dash, Ash, public domain Ksh,
    as well as some POSIX shells of some proprietary Unixes like Solaris's
    POSIX shell.

    I wonder why any other scripting languages exist? Why does TXR? What's
    missing from Bash (and why are there so many variations)?

    I can tell you that mine came into being as an add-on scripting language
    for my applications. I think it was about 15 years before it became an independent language.


    Though the shell language is quirky, it is well specified and understood
    to the point that fairly complex code can be written that works across multiple implementations.

    As far as Windows goes, nobody actually builds my program for Windows
    other than me. I provide a GUI installer. That's what you do for
    Windows.

    How do you get past AV software?

    How your stuff builds on Windows is almost immaterial.

    The only people building your FOSS stuff from code are (1) maintainers
    of FOSS distros, who always use environments with shells and make, etc;
    and (2) a few enthusiast users who build for themselves, also in such environments. Anyone who has something to do on Windows gets your
    installer.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to bart on Thu Jan 18 04:30:45 2024
    On 2024-01-18, bart <bc@freeuk.com> wrote:
    On 18/01/2024 00:25, Kaz Kylheku wrote:
    On 2024-01-17, bart <bc@freeuk.com> wrote:
    On 17/01/2024 22:18, Scott Lurndal wrote:
    bart <bc@freeuk.com> writes:
    On 17/01/2024 18:47, Kaz Kylheku wrote:
    On 2024-01-17, bart <bc@freeuk.com> wrote:

    A good many scripting languages require a shell in order to build, >>>>>> such as for runing a ./configure script.

    Funnily enough, mine don't.

    Perhaps you find it humorous. A posix shell comes with pretty
    much every system used for software development (even windows
    has WSL for serious software development).

    I suspect it's more that it's been browbeaten into having such a system
    because so many developers were deliberately, inadvertently or
    spitefully building in so many dependencies from the Unix world into
    their software and their procedures.

    You can get shell scripts running on Windows.

    You mean via layers like WSL or MSYS2 or CYGWIN? I don't really consider
    that running on Windows.

    What is it? To the left of Windows? Behind Windows? Under Windows?

    It doesn't matter which one of these prepositions it is, because in this situation being discussed all those programs are doing is providing an environment for building something.

    That something itself doesn't require that shell environment in its
    own installation.

    Macs support then
    natively.

    The availability of the shell language is better than any other;
    the places where it doesn't run out of the box are not many.

    Also, no operating system that is still in wide use has a better
    command language.

    Yes, I have an idea what you mean by better. Something that gives low priority to user-friendliness and that is bristling with advanced
    features that mean loads of gotchas.

    In fact, a lot of the gotchas are gratuitous with respect to the
    features. But there is a way to do things correctly if you understand
    how command lines are expanded.

    I can write a piece of shell code where I know what it's going to do
    just about anywhere. It's harder than it needs to be, or at least
    sometimes, and you have to know more than you should. The code isn't
    something to be crazy about.

    But I feel that with things like the Microsoft .BAT language, for
    instance, you cannot hit the same quality target, no matter how much you
    know.

    Microsoft's PowerShell language is a lot better (if I believe what
    others say). It's not widely installed though. You'd be better off
    relying on Python from that perspective.

    We could imagine, say, writing the build scripting of a project in the
    batch file language of CMD.EXE, and making that work on all the
    platforms: Mac, GNU Linuxes, Solaris, OpenBSD, Android/Termux what
    have you.

    For all the inconvenience of having to install that interpreter first,
    we would then have to contend with a comically poor quality language.
    It's worse than some CS freshman's semester project.

    What language do you have in mind? (Bear in mind that this is exactly
    what I've long thought about C.)

    That batch file one in the previous paragraph.

    Shell has multiple implementations. At one point I tested the ./configure
    script of my TXR project with Bash, Zsh, Dash, Ash, public domain Ksh,
    as well as some POSIX shells of some proprietary Unixes like Solaris's
    POSIX shell.

    I wonder why any other scripting languages exist? Why does TXR? What's missing from Bash (and why are there so many variations)?

    A heck of a lot is missing from shells, but what's not missing is that
    they are there on the majority of today's platforms.

    It's a bootstrap step.

    It's like asking, what's missing from the BIOS (or any other boot
    environment) that we need to boostrap into an OS? If the OS is so
    great, why is there still the boot firmware when the machine fires up,
    and not just the OS?

    Boostrapping: going from something we have that doesn't meet all our
    needs, into something we want.

    But there is also the fact that nearly all scripting languages "suck"
    for the simple use case of coordinating commands.

    Also, I don't know of any non-shell scripting language that has mature
    POSIX job control. POSIX job control is a set of C APIs and their
    behavior geared toward implementing the interactive job control features
    of a job control shell. In a job control shell, you can move commands
    between foreground and background. The shell acts as a traffic cop,
    controlling moving those jobs between those states. Background jobs have limited access to the terminal. They can be suspended or executing.

    As far as Windows goes, nobody actually builds my program for Windows
    other than me. I provide a GUI installer. That's what you do for
    Windows.

    How do you get past AV software?

    For some AV to identify your installer as a threat, it has to be a false positive. I ran into that only once before.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Kaz Kylheku on Thu Jan 18 10:26:19 2024
    On 18/01/2024 04:30, Kaz Kylheku wrote:
    On 2024-01-18, bart <bc@freeuk.com> wrote:

    Microsoft's PowerShell language is a lot better (if I believe what
    others say). It's not widely installed though. You'd be better off
    relying on Python from that perspective.

    All I know about PowerShell is that instead of typing 'prog' to run the 'prog.exe' you've just created, you have to do '.\prog' just like you
    have to do './prog' in Linux.

    Plus it has a gazillion options, accessed from diverse places, so you
    can spend half your time just trying to get a consistent background
    colour. MS really know how to go to town on such things.

    (When I tried it just now, the colour of the text I was typing was the
    same colour as the background. That makes things interesting.)


    How do you get past AV software?

    For some AV to identify your installer as a threat, it has to be a false positive. I ran into that only once before.

    With TXR Win64 installer, Windows Defender stopped me from running the
    program (until I clicked 'more info' then there was an option to 'run
    anyway').

    Often, ZIPs are just ignored. (Even gmail will not have them as
    attachments.).

    More typically, you can get an actual EXE file, but it's treated as
    suspicious by AV and may be quarantined.

    This is why these days, if somebody wants to run a binary EXE, I make it available as a C source file.

    Since if they have experience of compiling programs from source, they
    will already know how to run such programs without AV interference.

    (Also, their AV will be familiar with the EXE patterns from their C
    compiler; the EXEs my compiler creates may look alien.)

    Note that a new Windows machine will stop you running /any/ executable
    that is not verified from Microsoft Store or wherever (not even their
    own cmd.exe), until you change a non-reversible setting.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Chris M. Thomasson on Thu Jan 18 13:00:03 2024
    On 17/01/2024 22:00, Chris M. Thomasson wrote:
    On 1/17/2024 4:43 AM, David Brown wrote:
    On 17/01/2024 02:04, Chris M. Thomasson wrote:
    On 1/16/2024 6:06 AM, David Brown wrote:

    Well, you don't often write inline assembly - its rare to write it.
    It's typically the kind of thing you write once for your particular
    instruction, then stick it away in a header somewhere.  You might
    use it often, but you don't need to read or edit the code often.[...]

    As soon as you use inline assembler in a file, you sort of "need" to?

    Nonsense.

    If I use inline asm in a file, I at least need to add in comments that
    this is arch specific code. A macro for the arch also might be in order.
    So, if the user compiles it on a different arch, well, the inline asm is eluded. Why is that wrong? You never did that before?


    There's nothing at all wrong with that. My disagreement was with your suggestion that people using inline assembly (defined in a header
    written by someone else) need to be able to read and/or edit the code.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Kaz Kylheku on Thu Jan 18 15:16:19 2024
    On 18/01/2024 04:30, Kaz Kylheku wrote:
    On 2024-01-18, bart <bc@freeuk.com> wrote:

    You mean via layers like WSL or MSYS2 or CYGWIN? I don't really consider
    that running on Windows.

    What is it? To the left of Windows? Behind Windows? Under Windows?

    It doesn't matter which one of these prepositions it is, because in this situation being discussed all those programs are doing is providing an environment for building something.

    Let's consider an OS like Android, to avoid a made-up one. (Put aside
    the likelihood that deep inside Android might be a Linux core. Think of
    the glossy-looking consumer OS that everyone knows.)

    Now, somebody brings out some piece of software that only builds under
    Android.

    To build it under Linux, requires installing a vast virtual Android OS
    to do that.

    Would you consider that being able to build 'under Linux'?

    (Let's not go into whether the resulting binary can run outside of Android.)

    For that matter (this might have been a better example), would you
    consider a project that builds using a massive Visual Studio
    installation to compiler 'under Linux' if you could somehow run run VS
    within Linux?

    Could you, in good faith, provide sources to that project and say it
    builds 'under Linux, no problem'. Never mind the considerable
    dependencies they would need and the significant likelihood of failure.

    Remember also that on Windows you want to end up with EXEs and DLLs that
    run under actual Windows. Not ELF and SO files.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to David Brown on Thu Jan 18 19:15:15 2024
    On 06/01/2024 07:39, David Brown wrote:
    On 05/01/2024 19:42, Kaz Kylheku wrote:
    On 2024-01-05, Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:
    When you wrote "in Linux", I wondered if you were being imprecise, but
    in fact that code is in the Linux kernel.

    That means the macros aren't directly available to normal C code, but
    you can always copy their definitions (*if* you're using a compiler
    that supports the __builtin_types_compatible_p extension).

    You can always copy their definitions, if you're using a compiler
    that doesn't arbitrarily define __GNUC__ without providing the
    associated behaviors:

    #ifdef __GNU__

    // define array_size in the Linux kernel way

    #else

    #define arrray_size(x) (sizeof (x)/sizeof *(x))

    #endif

    If you regularly build the code with a compiler that provides GNU
    extensions (like as part of your CI), you're covered, even if you're
    going to production with something else.

    I use C++ this way in C projects; I have some macro features that
    provide extra checks under C++. I get the benefit even if I just
    compile the code as C++ only once before every release.


    That is a good tactic if your code needs to be used with compilers that
    don't have such features for static checking (perhaps you are releasing
    your source code, and can't influence the tools or settings users use).
    In a way, you are using gcc (or C++) as a linter.

    I've done this myself when the actual target compiler had little in the
    way of static checking - I ran gcc in parallel, for a different target
    (the generated output was ignored), but with lots of warning flags that
    the real compiler did not support.


    This is pretty much how in the past I have suggested people use Tiny C:
    use it for very fast compilation turnaround. Use gcc from time to time
    for better error checking, and for production builds for faster code.

    Another is for situations where obtaining the small-footprint Tiny C
    compiler is simpler, or where it can be bundled with your source code.

    Yet another is when you have a compiler for another language that
    targets C code. Since the program has already been verified, the C code
    should be correct. Then extensive static checking is not needed; you
    just want a fast backend that doesn't take up 95% of the overall
    compilation time.

    Again, use gcc for a production version for some extra speed, but it is
    not essential to have it.

    This is a compiler you've dismissed as a toy.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to bart on Thu Jan 18 19:40:44 2024
    On 2024-01-18, bart <bc@freeuk.com> wrote:
    On 18/01/2024 04:30, Kaz Kylheku wrote:
    On 2024-01-18, bart <bc@freeuk.com> wrote:

    Microsoft's PowerShell language is a lot better (if I believe what
    others say). It's not widely installed though. You'd be better off
    relying on Python from that perspective.

    All I know about PowerShell is that instead of typing 'prog' to run the 'prog.exe' you've just created, you have to do '.\prog' just like you
    have to do './prog' in Linux.

    I don't know anything about PowerShell. In the case of the POSIX-like
    shells, this is just a consequence of the . directory not being listed
    in the PATH variable, which is an end-user configuration matter.

    If you care about the convenience of just typing "prog", more than
    about the security aspect, you can just add . to your PATH.

    Plus it has a gazillion options, accessed from diverse places, so you
    can spend half your time just trying to get a consistent background
    colour. MS really know how to go to town on such things.

    Looks like you have nowhere to run. Don't like Unix; and the Mirosoft's
    next generation shell is too complex.

    With TXR Win64 installer, Windows Defender stopped me from running the program (until I clicked 'more info' then there was an option to 'run anyway').

    Yes; what good would the name "Defender" be if you cold just download
    a random executable and run it without any prompt?

    If you get an installer signed by Microsoft, you can get around that.

    This is why these days, if somebody wants to run a binary EXE, I make it available as a C source file.

    But they need a C compiler EXE to compile it.

    This is a complete non-starter for applications which are complex
    and don't target programmers.

    Even programmers won't use a program on Windows that they have to figure
    out how to compile for Windows.

    Since if they have experience of compiling programs from source, they
    will already know how to run such programs without AV interference.

    OK, now I understand why you need C compiling to be dead easy.

    You'd like to ship .c files to Windows users.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to bart on Thu Jan 18 21:47:09 2024
    On 18/01/2024 16:16, bart wrote:
    On 18/01/2024 04:30, Kaz Kylheku wrote:
    On 2024-01-18, bart <bc@freeuk.com> wrote:

    You mean via layers like WSL or MSYS2 or CYGWIN? I don't really consider >>> that running on Windows.

    What is it? To the left of Windows? Behind Windows? Under Windows?

    It doesn't matter which one of these prepositions it is, because in this
    situation being discussed all those programs are doing is providing an
    environment for building something.

    Let's consider an OS like Android, to avoid a made-up one. (Put aside
    the likelihood that deep inside Android might be a Linux core. Think of
    the glossy-looking consumer OS that everyone knows.)

    (It is not a "likelihood" - it is a fact. Android is Linux with a
    particular set of libraries, gui and apps.)


    Now, somebody brings out some piece of software that only builds under Android.

    OK.

    To build it under Linux, requires installing a vast virtual Android OS
    to do that.

    Would you consider that being able to build 'under Linux'?


    That's a rather speculative and hypothetical idea that is very different
    from using mingw-64 to configure and compile software on Windows. After
    all, with mingw-64 / msys2, the resulting binary runs on Windows, not
    Linux. I use mingw-64 compiled binaries on Windows all the time, and
    they don't need even anything installed on the target system (except
    sometimes a few dll's, just like any Windows program). You do need some supporting basic programs in order to /build/ mingw-64 binaries - at
    least gcc, make, and perhaps some utilities like bash and sed. (Or you
    can install a mingw-64 target compiler on Linux and cross-compile -
    building the Windows binaries under Linux.) But yes, these are all
    programs build for Windows and running under Windows. They are not hard
    to install or harder to run compared to running them on anything else.

    WSL is more of a virtual machine, but it is well integrated into Windows.

    My daily work is building software with cross-compilers. Sometimes I
    build "under Linux", using Linux elf binaries like "make" and "arm-none-eabi-gcc". Sometimes I build "under Windows", using Windows
    exe files like "make.exe" and "arm-none-eabi-gcc.exe". In each case,
    there is a large tree full of toolchain programs, headers, target
    library files, etc., to support it - far bigger than my "msys2"
    installation (which is not needed for these builds). I consider it
    "building under Linux" or "building under Windows", because I am running
    those OS's and running programs for those OS's.

    (Let's not go into whether the resulting binary can run outside of
    Android.)

    For that matter (this might have been a better example), would you
    consider a project that builds using a massive Visual Studio
    installation to compiler 'under Linux' if you could somehow run run VS
    within Linux?


    Yes.

    Could you, in good faith, provide sources to that project and say it
    builds 'under Linux, no problem'. Never mind the considerable
    dependencies they would need and the significant likelihood of failure.


    Dependencies are part of life with computers. A Windows installation
    without additional software gives you Minesweeper and Wordpad. How can
    you possibly justify thinking that installing msys2 and mingw-64 for a compilation means you are "not building under Windows" while installing
    MSVC means you /are/ "building under Windows"? The MSVC installation is
    10 times the size of mingw-64.

    You are drawing completely arbitrary lines without any justification.

    Remember also that on Windows you want to end up with EXEs and DLLs that
    run under actual Windows. Not ELF and SO files.




    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Kaz Kylheku on Thu Jan 18 20:21:36 2024
    On 18/01/2024 19:40, Kaz Kylheku wrote:
    On 2024-01-18, bart <bc@freeuk.com> wrote:
    On 18/01/2024 04:30, Kaz Kylheku wrote:
    On 2024-01-18, bart <bc@freeuk.com> wrote:

    Microsoft's PowerShell language is a lot better (if I believe what
    others say). It's not widely installed though. You'd be better off
    relying on Python from that perspective.

    All I know about PowerShell is that instead of typing 'prog' to run the
    'prog.exe' you've just created, you have to do '.\prog' just like you
    have to do './prog' in Linux.

    I don't know anything about PowerShell. In the case of the POSIX-like
    shells, this is just a consequence of the . directory not being listed
    in the PATH variable, which is an end-user configuration matter.

    If you care about the convenience of just typing "prog", more than
    about the security aspect, you can just add . to your PATH.

    Plus it has a gazillion options, accessed from diverse places, so you
    can spend half your time just trying to get a consistent background
    colour. MS really know how to go to town on such things.

    Looks like you have nowhere to run. Don't like Unix; and the Mirosoft's
    next generation shell is too complex.

    As you know by now, I expect very little of shell programs.

    I also only use a tiny corner of Windows. Remember I used to be quite
    adept at using 8-bit machines with 64KB of memory and running off floppies.


    This is why these days, if somebody wants to run a binary EXE, I make it
    available as a C source file.

    But they need a C compiler EXE to compile it.

    It's either that or run my EXE.

    This is a complete non-starter for applications which are complex
    and don't target programmers.

    So how are those applications distributed on Linux?


    Even programmers won't use a program on Windows that they have to figure
    out how to compile for Windows.

    That would be me too, everytime I want try out someone's project or use
    some library.

    At least I make the process as simple as it can be without using a
    binary: my generated C code needs 3 files from Tiny C which come to
    235KB, and one C source file. It should build in a fraction of a second.

    But of course, you have to pick holes in that!

    "It's far too complicated". "Supply a binary instead".

    Whether someone will accept my binaries is up to them. But since my
    programs are related to languages, such people will be fairly technical.

    (I also have other choices: I can supply the bulk of an application in a special binary format of my own. That then only needs a 12KB actual EXE
    to launch, that should be easier to get past the AV. Or it can be an
    800-line C program to be compiled locally.)

    In the distant past when I had lots of non-technical customers then of
    course we supplied binaries. But no one was worried about viruses then.


    Since if they have experience of compiling programs from source, they
    will already know how to run such programs without AV interference.

    OK, now I understand why you need C compiling to be dead easy.

    You'd like to ship .c files to Windows users.

    Windows users who are programmers interested in different languages. Or
    who want to learn the simplest way of distributing an application which
    is one step away from binary.

    And there's a bonus: the same 'dead easy' way also works on Linux.

    But I've lost track of what you're objecting to:

    * If I supply a binary executable, is that good or bad from your POV?

    * If I supply a one-file C version that be trivially compiled locally
    into a binary, is that good or bad?

    * Is the only thing acceptable to you, to supply a C version in 178
    source files in 34 nested directories, together with a collection of 'configure' files and makefiles, and that will only build in a Linux environment?

    AFAIK, if I wanted to supply a program to any Linux user, I can't just
    supply a binary, since every Linux is a bit different and they run on
    more different kinds of processor.

    This is where MS Windows reigned supreme.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to bart on Thu Jan 18 23:29:39 2024
    On 2024-01-18, bart <bc@freeuk.com> wrote:
    On 18/01/2024 04:30, Kaz Kylheku wrote:
    On 2024-01-18, bart <bc@freeuk.com> wrote:

    You mean via layers like WSL or MSYS2 or CYGWIN? I don't really consider >>> that running on Windows.

    What is it? To the left of Windows? Behind Windows? Under Windows?

    It doesn't matter which one of these prepositions it is, because in this
    situation being discussed all those programs are doing is providing an
    environment for building something.

    Let's consider an OS like Android, to avoid a made-up one. (Put aside
    the likelihood that deep inside Android might be a Linux core. Think of
    the glossy-looking consumer OS that everyone knows.)

    Android is a Linux-based OS. It has a C library called Bionic.

    There is an environment you can install on Android called Termux.

    In Termux you get a shell with utilities and numerous common packages.
    git, ssh, you name it. You can compile programs like on other Unix-likes.

    I ported TXR to Android with Termux in literally minutes, originally.

    When Android rolled out pointer tagging, I had to do a few things to
    make it work, because it clashes with the NaN-boxing technique used
    for representing values. We turn on a build option
    CONFIG_NAN_BOXING_STRIP_TAG invented for that, which will strip
    the pointer tags from the heaps allocated from malloc, and put
    them back when calling free. In between that, we refer to the objects
    via tag-free pointers.

    In the test suite, a few things have to be done differently on Android.
    It is detected like this;

    (if (ignerr (dlsym (dlopen "libandroid.so") "AAsset_close"))
    :android
    ( ... else detect other platforms ...))

    I.e. if we are able to successfully open a library called
    "libandroid.so" and resolve the symbol "AAsset_close", then
    we conclude we are running on Android. I probably could have
    relied on some clues from the kernel.

    In other ways, it's just another Linux.

    Now, somebody brings out some piece of software that only builds under Android.

    To build it under Linux, requires installing a vast virtual Android OS
    to do that.

    There is application software for Android that cannot run on any
    other platform. People run this via emulators.

    Would you consider that being able to build 'under Linux'?

    Tentatively, yes, if:

    - you can prepare the files that need to be built just by
    storing them in the host Linux filesystem.

    - you can dispatch the build process from Linux, from a script.
    (No manual fiddling with the GUI of some emulator, and no
    remote logins to an emulator to run anything).

    - you can collect the deliverables in the host filesystem.

    such that this whole thing can be automated on a Linux build server,
    that runs without any manual steps, and which doesn't have to use
    networking to connect to any emulated machine to copy anything
    in or out.


    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to David Brown on Thu Jan 18 23:46:59 2024
    On 18/01/2024 20:47, David Brown wrote:
    On 18/01/2024 16:16, bart wrote:

    Could you, in good faith, provide sources to that project and say it
    builds 'under Linux, no problem'. Never mind the considerable
    dependencies they would need and the significant likelihood of failure.


    Dependencies are part of life with computers.  A Windows installation without additional software gives you Minesweeper and Wordpad.  How can
    you possibly justify thinking that installing msys2 and mingw-64 for a compilation means you are "not building under Windows" while installing
    MSVC means you /are/ "building under Windows"?  The MSVC installation is
    10 times the size of mingw-64.

    Actually I don't consider an MSVC dependency much better that MSYS2 and
    WSL. At least WSL works; I haven't had a working MSVC for years.

    MSVC/VS/etc is a vast, corporate monstrosity, as is most of Windows
    these days.

    But you can still do things on a small scale and informally as I do
    within a small corner of Windows.

    My last analogy didn't work very well, I'll try another one. Suppose you
    wanted a product but it only comes in kit form that you have to build
    yourself.

    It promises it only needs a hammer and a screwdriver. But it turns out
    there is one extra thing you need: a massive great factory next door
    that contains all the specialist equipment, processes and manpower
    needed to complete that job.

    Apart from that, then sure: you can assemble this 'at home'.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to bart on Fri Jan 19 10:06:58 2024
    On 18/01/2024 20:15, bart wrote:
    On 06/01/2024 07:39, David Brown wrote:
    On 05/01/2024 19:42, Kaz Kylheku wrote:
    On 2024-01-05, Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:
    When you wrote "in Linux", I wondered if you were being imprecise, but >>>> in fact that code is in the Linux kernel.

    That means the macros aren't directly available to normal C code, but
    you can always copy their definitions (*if* you're using a compiler
    that supports the __builtin_types_compatible_p extension).

    You can always copy their definitions, if you're using a compiler
    that doesn't arbitrarily define __GNUC__ without providing the
    associated behaviors:

    #ifdef __GNU__

    // define array_size in the Linux kernel way

    #else

    #define arrray_size(x) (sizeof (x)/sizeof *(x))

    #endif

    If you regularly build the code with a compiler that provides GNU
    extensions (like as part of your CI), you're covered, even if you're
    going to production with something else.

    I use C++ this way in C projects; I have some macro features that
    provide extra checks under C++. I get the benefit even if I just
    compile the code as C++ only once before every release.


    That is a good tactic if your code needs to be used with compilers
    that don't have such features for static checking (perhaps you are
    releasing your source code, and can't influence the tools or settings
    users use). In a way, you are using gcc (or C++) as a linter.

    I've done this myself when the actual target compiler had little in
    the way of static checking - I ran gcc in parallel, for a different
    target (the generated output was ignored), but with lots of warning
    flags that the real compiler did not support.


    This is pretty much how in the past I have suggested people use Tiny C:
    use it for very fast compilation turnaround. Use gcc from time to time
    for better error checking, and for production builds for faster code.

    My use was not like that. I used the other compiler because I had no
    choice - I would not intentionally use a weaker compiler when a better
    one was available, certainly not for something as irrelevant as compile
    speed. But gcc does not support all the devices I have used over the years.

    And I certainly don't run the weak tool regularly and gcc occasionally -
    that defeats the purpose of static error checking, when you want the
    results as soon as possible. As I said, I would run them in parallel,
    and if I were only going to run one, it would be gcc - the sooner a bug
    or potential problem is found, the better. (Thus it makes sense to use
    an IDE or advanced editor that checks many things on the fly, as you type.)

    If you are running more advanced static analysis tools, such as
    clang-analyzer, that can take a long time doing simulated execution and inter-module analysis, then you might not want to run it for every build.

    As a rule of thumb, if the static analysis takes less time than a sip of coffee, do it on every build. If it takes as long as making coffee, do
    it when you are making coffee.


    Another is for situations where obtaining the small-footprint Tiny C
    compiler is simpler, or where it can be bundled with your source code.


    That's very rarely a realistic scenario for anyone, and never has been
    for me.

    Yet another is when you have a compiler for another language that
    targets C code. Since the program has already been verified, the C code should be correct. Then extensive static checking is not needed; you
    just want a fast backend that doesn't take up 95% of the overall
    compilation time.

    I think it is better to say that when someone who has no connection to
    the development process, but simply receives the source code ready-made
    and fully debugged and tested, they have no need of extensive static
    checking. And that's true - any appropriate static checks should have
    been made already, so the code should compile warning-free (and
    obviously error-free). But usually the build times here are irrelevant
    - this person will build once, as part of the installation procedure -
    and may run the program many times. So that build should be done with a
    good compiler, not a "tiny" compiler, with optimisations enabled. And
    it might as well have static error checking enabled as the extra cost in
    time is negligible, and it might catch something the developer had not expected.

    If there is a good reason not to use a top quality compiler - such as
    lack of support for a particular target, or source code that uses
    extensions from a different tool - your hand is forced. If not, then I
    simply can't see any realistic circumstance where you would not want to
    use the best tools you have available. (And if you have a compiler
    better than gcc, use that instead - for at least some uses, icc or clang
    can be better than gcc.)


    Again, use gcc for a production version for some extra speed, but it is
    not essential to have it.

    This is a compiler you've dismissed as a toy.

    Yes, TCC is a toy. That does not mean it is not impressive in many
    ways, but it has almost no real-world use-cases where gcc would not be
    better. There may be circumstances where it is more convenient and good enough, making it the right choice.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to bart on Fri Jan 19 11:07:17 2024
    On 18/01/2024 21:21, bart wrote:


    AFAIK, if I wanted to supply a program to any Linux user, I can't just
    supply a binary, since every Linux is a bit different and they run on
    more different kinds of processor.


    As so often, "AFAIK" for you is not very far.

    For almost all Linux users, for almost all their programs, they download
    and install binaries - they don't compile from source. Compiling from
    source as a common method of distributing programs fell out of fashion
    some 20 or 30 years ago.

    Of course it is an absolutely essential aspect of free and open source
    that the source code is available, and you /can/ compile it yourself.
    But for the great majority, they don't do that. Intermediaries -
    typically Linux distributions - get the source and compile the binaries,
    and users download the binaries.


    For developers or companies who don't want to distribute source, or who
    want to provide the users the convenience of binaries but it's not
    appropriate to include the programs in distros, it is normal to provide
    only one or two binaries - an x86-64 binary, and sometimes also an
    AArch64 binary if that is a likely target.

    It can be more complicated if the software has to integrate tightly with different aspects of the system, but that's usually not an issue for
    most software.


    This is where MS Windows reigned supreme.

    You mean it is so much more limited?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to bart on Fri Jan 19 12:41:33 2024
    On 19/01/2024 12:17, bart wrote:
    On 19/01/2024 10:07, David Brown wrote:
    On 18/01/2024 21:21, bart wrote:


    AFAIK, if I wanted to supply a program to any Linux user, I can't
    just supply a binary, since every Linux is a bit different and they
    run on more different kinds of processor.


    As so often, "AFAIK" for you is not very far.

    For almost all Linux users, for almost all their programs, they
    download and install binaries - they don't compile from source.
    Compiling from source as a common method of distributing programs fell
    out of fashion some 20 or 30 years ago.

    That's good news. So I can build an ELF binary on my WSL today, I can
    email it to you and you can run it on the nearest Linux machine?

    I know little about WSL - I have never used it, and don't have a current Windows system. But if it is a Linux virtual system, and your elf file
    doesn't rely on uncommon shared libraries and doesn't have low-level interaction with the system (i.e., you'd call it an "application" rather
    than a "system utility"), the probably yes, it would work. Copy it to a
    USB stick, boot Linux, and try it yourself.


    I assume it will only work if that machine happens to be x64 (as that's
    what mine is)?

    Obviously.


    Or will it not work even then?

    What are the rules? Do I have to make a version for every variant of
    Linux and make it available in the 'apt-get' repository?

    If you want rules, find a suitable place to look for them or discuss
    them. I have no interest in talking more about this here, and I think
    I've already said more than most in this group are willing to do. It
    doesn't matter what anyone says, you will always twist things to fit
    your preconceived ideas that everything Linux-related is terrible in
    every way, and that somehow you think it all proves that your own tools
    are brilliant and the rest of the world is wrong.

    So try google.




    For developers or companies who don't want to distribute source, or
    who want to provide the users the convenience of binaries but it's not
    appropriate to include the programs in distros, it is normal to
    provide only one or two binaries - an x86-64 binary, and sometimes
    also an AArch64 binary if that is a likely target.

    It can be more complicated if the software has to integrate tightly
    with different aspects of the system, but that's usually not an issue
    for most software.


    This is where MS Windows reigned supreme.

    You mean it is so much more limited?

    In being able to run EXEs created by anyone on any machine, and being
    able to run them on any Windows computer, even years later.

    I run Linux programs on all sorts of machines without recompiling, years
    later. Of course the processor family must be compatible if you don't
    want to use emulation.


    (I think you can still run 32-bit EXEs compiled in the 1990s today.
    16-bit EXEs can still run today on a 32-bit Windows.)

    You can't get new 32-bit Windows systems now - there is no 32-bit
    Windows 11, and manufacturers have not been able to sell new machines
    with 32-bit Windows 10 for years. If you have to use 16-bit Windows
    programs on a modern computer, you can of course use Wine to run it on
    Linux.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to David Brown on Fri Jan 19 11:17:03 2024
    On 19/01/2024 10:07, David Brown wrote:
    On 18/01/2024 21:21, bart wrote:


    AFAIK, if I wanted to supply a program to any Linux user, I can't just
    supply a binary, since every Linux is a bit different and they run on
    more different kinds of processor.


    As so often, "AFAIK" for you is not very far.

    For almost all Linux users, for almost all their programs, they download
    and install binaries - they don't compile from source.  Compiling from source as a common method of distributing programs fell out of fashion
    some 20 or 30 years ago.

    That's good news. So I can build an ELF binary on my WSL today, I can
    email it to you and you can run it on the nearest Linux machine?

    I assume it will only work if that machine happens to be x64 (as that's
    what mine is)?

    Or will it not work even then?

    What are the rules? Do I have to make a version for every variant of
    Linux and make it available in the 'apt-get' repository?


    For developers or companies who don't want to distribute source, or who
    want to provide the users the convenience of binaries but it's not appropriate to include the programs in distros, it is normal to provide
    only one or two binaries - an x86-64 binary, and sometimes also an
    AArch64 binary if that is a likely target.

    It can be more complicated if the software has to integrate tightly with different aspects of the system, but that's usually not an issue for
    most software.


    This is where MS Windows reigned supreme.

    You mean it is so much more limited?

    In being able to run EXEs created by anyone on any machine, and being
    able to run them on any Windows computer, even years later.

    (I think you can still run 32-bit EXEs compiled in the 1990s today.
    16-bit EXEs can still run today on a 32-bit Windows.)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to David Brown on Fri Jan 19 13:18:17 2024
    On 19/01/2024 11:41, David Brown wrote:
    On 19/01/2024 12:17, bart wrote:

    It
    doesn't matter what anyone says, you will always twist things to fit
    your preconceived ideas that everything Linux-related is terrible in
    every way,

    That's funny. I find that Linux people always have terrible things to
    say about software development using Windows, usually totally unjustified.

    The main reason is that on Linux they are reliant on a huge mountain of dependencies that don't exist on Windows, even when creating supposedly cross-platform products.

    (That there have been many thousands of programs developed on Windows
    without that mountain totally passes them by.)

    My issues with it are when *I'm* expected to recreate that same mountain
    even when building an application supposedly written in C.




    and that somehow you think it all proves that your own tools
    are brilliant and the rest of the world is wrong.

    Um no. It's just that I can do without that mountain. If I can do that
    on Windows (remember, Windows doesn't equal Visual Studio; that's just
    one over-the-top application), I can do it on Linux. And I have done.

    So if I can do it, so can anyone. /That/ is what is superior.

    What /I/ do is to remove complexity and simplify. What everyone else
    does is to add complexity, extra dependencies and extra layers of
    software. Apparently it doesn't matter how big that mountain gets.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to David Brown on Fri Jan 19 15:03:47 2024
    On 19/01/2024 14:42, David Brown wrote:
    On 19/01/2024 14:18, bart wrote:

    It is also incomprehensible how someone can rant endlessly about "simplification" and "easy of use" when their claimed solution is to
    make non-standard limited duplications of the functionality already
    found on other machines.

    The programs I write ARE easy to build, using ONLY a compiler.

    You don't want to accept that, you'd rather it involved those mountains
    of stuff.

    Maybe you don't like it because you're rather just type 'make', no
    matter much pointless garbage is invoked. Fine, a makefile for most of
    my projects would be two lines long.

    Happy?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to bart on Fri Jan 19 15:42:13 2024
    On 19/01/2024 14:18, bart wrote:
    On 19/01/2024 11:41, David Brown wrote:
    On 19/01/2024 12:17, bart wrote:

    It doesn't matter what anyone says, you will always twist things to
    fit your preconceived ideas that everything Linux-related is terrible
    in every way,

    That's funny. I find that Linux people always have terrible things to
    say about software development using Windows, usually totally unjustified.

    And feel free to ignore these if you have good reason to believe they
    are not fact based. Don't feel free to exaggerate what people say (I
    say Linux is better for most development and programming work, other
    than when targeting Windows itself - but you /can/ use Windows for many
    tasks, and I do use Windows as well as Linux). And don't imagine that
    just because you feel someone has said something unjustifiably bad about
    X, it means it is appropriate to say something similarly unjustifiably
    bad about Y. That's just petty.


    The main reason is that on Linux they are reliant on a huge mountain of dependencies that don't exist on Windows, even when creating supposedly cross-platform products.

    You are making stuff up.

    People do cross-platform (and I assume by this you just mean "for x86-64
    Linux and x86-64 Windows") all the time. /You/ have difficulty with
    this kind of thing, but many other developer groups manage. (I'm sure
    there are others who have problems too.)

    There are no mountains.

    What there is, that seems to be a stumbling block for you, is a set of
    common utility programs that has been standardised and used on most
    serious OS's for decades (since most serious OS's, except for Windows,
    aim for POSIX compatibility). These are easily available on Windows
    too, and once you install them, they are there and usable. I've had
    them on every Windows PC I have used since I had internet access.

    I can fully understand how someone who is only familiar with Windows
    (and DOS before it) doesn't know about these utilities or where to get
    them, and finds them strange at first. It is incomprehensible, however,
    to be having the same questions, difficulties and complaints after a
    decade or so.

    It is also incomprehensible how someone can rant endlessly about "simplification" and "easy of use" when their claimed solution is to
    make non-standard limited duplications of the functionality already
    found on other machines.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to bart on Fri Jan 19 16:18:49 2024
    bart <bc@freeuk.com> writes:
    On 19/01/2024 10:07, David Brown wrote:
    On 18/01/2024 21:21, bart wrote:


    AFAIK, if I wanted to supply a program to any Linux user, I can't just
    supply a binary, since every Linux is a bit different and they run on
    more different kinds of processor.


    As so often, "AFAIK" for you is not very far.

    For almost all Linux users, for almost all their programs, they download
    and install binaries - they don't compile from source.  Compiling from
    source as a common method of distributing programs fell out of fashion
    some 20 or 30 years ago.

    That's good news. So I can build an ELF binary on my WSL today, I can
    email it to you and you can run it on the nearest Linux machine?

    That's not what David said. The major distributions offer
    repositories containing all supported packages (far more than will
    be installed by default), built for that specific distribution.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Janis Papanagnou@21:1/5 to David Brown on Fri Jan 19 17:32:14 2024
    On 19.01.2024 15:42, David Brown wrote:
    On 19/01/2024 14:18, bart wrote:
    On 19/01/2024 11:41, David Brown wrote:
    On 19/01/2024 12:17, bart wrote:

    It doesn't matter what anyone says, you will always twist things to
    fit your preconceived ideas that everything Linux-related is terrible
    in every way,

    That's funny. I find that Linux people always have terrible things to
    say about software development using Windows, usually totally
    unjustified.

    And feel free to ignore these if you have good reason to believe they
    are not fact based. Don't feel free to exaggerate what people say (I
    say Linux is better for most development and programming work, other
    than when targeting Windows itself - but you /can/ use Windows for many tasks, and I do use Windows as well as Linux). [...]

    There was a time when I dual booted (pre-installed) WinDOS and
    Linux. At some point I got aware that it makes no sense, Windows
    was - besides its inherent issues - also completely unnecessary
    (as could be seen soon); further installs were Linux-only.

    It's a phenomenon that the marketing division of MS did so great
    a job to mentally bind such a huge community. (There were other
    reasons as well (driver support of companies, pre-installed OS
    on hardware, gaming focus, or self-enforcing dissemination
    feedback processes), but discussion would lead too far here.)

    I've often observed uninformed WinDozers just spread uninformed
    nonsense. But never in such extreme stubbornness as in this thread.

    [...]
    [...]

    I can fully understand how someone who is only familiar with Windows
    (and DOS before it) doesn't know about these utilities or where to get
    them, and finds them strange at first. [...]

    This is part of the phenomenon.

    And as we see nothing helps if folks grew up and didn't leave
    that bubble, didn't even dare to have a look outside and try
    to grasp what's going on. Yet continuing uninformed rants about
    something they never experienced nor understood to a minimum
    extent.

    We have to accept though that the condition where someone is
    making specialized niche-software for a specific platform is
    also not fostering susceptive open-mindedness when confronted
    with the huge professional IT world.

    But I have hope; after issues with Windows I installed Linux
    even for my (very old) father, and he has no problems with it
    (using browser, email, and a text processor). And my children
    installed Linux by themselves, anyway, and they're programming
    their academic research tasks on that platform. - No lengthy
    stupid discussions as in this thread.

    Janis

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to bart on Fri Jan 19 18:12:32 2024
    On 19/01/2024 16:03, bart wrote:
    On 19/01/2024 14:42, David Brown wrote:
    On 19/01/2024 14:18, bart wrote:

    It is also incomprehensible how someone can rant endlessly about
    "simplification" and "easy of use" when their claimed solution is to
    make non-standard limited duplications of the functionality already
    found on other machines.

    The programs I write ARE easy to build, using ONLY a compiler.

    You don't want to accept that, you'd rather it involved those mountains
    of stuff.

    /Please/ stop making stupid, exaggerated and incorrect claims about what
    other people want or think.

    I don't give a *beep* how easy or hard your programs are to compile -
    they are utterly irrelevant to me. I don't have reason to believe they
    are very relevant to anyone else either. But even if they were useful
    to me, or at least of interest to me, it does not matter to me if they
    need a whole range of standard utilities to build - because every system
    I have, or ever have had, has the standard utilities.

    Why is it so difficult for you to understand the concept of not caring?
    It is absurd to suggest I /want/ your programs to need "make" or other utilities to compile - I *do* *not* *care* if it needs them or not.
    There is no advantage or disadvantage to me if it uses make, sed, awk,
    bash, date. There is no advantage or disadvantage to me if it does not
    need them either. It is irrelevant.

    It /would/ be an inconvenience to me if it required some obscure and
    rarely used compiler like your own tools. TCC is also pretty obscure,
    but Debian has it in it's repositories. Needing an invasive tool that
    screws up your system, like MSVC, would also be a pain, as would
    anything that required very specific versions of tools. But that would
    only matter if your programs were of interest.

    And of course I accept that /you/ think it is important that you don't
    use tools that everyone else finds convenient and useful. I don't
    understand your reasons for that - it seems to be based on a determined
    battle to ensure that you fail to get anything you deem to be "Linux
    related" to work, combined with a fanaticism about "simple" that
    completely misses the point. But I don't think anyone here doubts that
    /you/ think this is all desperately important, even though pretty much
    no one else cares.


    And /please/ stop making an arse of yourself here by calling a dozen
    standard utilities "mountains of stuff".

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Janis Papanagnou on Fri Jan 19 17:05:01 2024
    On 19/01/2024 16:32, Janis Papanagnou wrote:
    On 19.01.2024 15:42, David Brown wrote:
    On 19/01/2024 14:18, bart wrote:
    On 19/01/2024 11:41, David Brown wrote:
    On 19/01/2024 12:17, bart wrote:

    It doesn't matter what anyone says, you will always twist things to
    fit your preconceived ideas that everything Linux-related is terrible
    in every way,

    That's funny. I find that Linux people always have terrible things to
    say about software development using Windows, usually totally
    unjustified.

    And feel free to ignore these if you have good reason to believe they
    are not fact based. Don't feel free to exaggerate what people say (I
    say Linux is better for most development and programming work, other
    than when targeting Windows itself - but you /can/ use Windows for many
    tasks, and I do use Windows as well as Linux). [...]

    There was a time when I dual booted (pre-installed) WinDOS and
    Linux. At some point I got aware that it makes no sense, Windows
    was - besides its inherent issues - also completely unnecessary
    (as could be seen soon); further installs were Linux-only.

    It's a phenomenon that the marketing division of MS did so great
    a job to mentally bind such a huge community. (There were other
    reasons as well (driver support of companies, pre-installed OS
    on hardware, gaming focus, or self-enforcing dissemination
    feedback processes), but discussion would lead too far here.)

    I've often observed uninformed WinDozers just spread uninformed
    nonsense. But never in such extreme stubbornness as in this thread.

    [...]
    [...]

    I can fully understand how someone who is only familiar with Windows
    (and DOS before it) doesn't know about these utilities or where to get
    them, and finds them strange at first. [...]

    This is part of the phenomenon.

    And as we see nothing helps if folks grew up and didn't leave
    that bubble, didn't even dare to have a look outside and try
    to grasp what's going on. Yet continuing uninformed rants about
    something they never experienced nor understood to a minimum
    extent.

    It's not me who's in the bubble. How about that giant bubble called 'Linux'?

    People who complain about Windows will moan about something that is
    missing because Linux has it and Windows doesn't.

    My complaints about Linux are not about something missing from Linux
    that is in Windows. See the difference?


    We have to accept though that the condition where someone is
    making specialized niche-software for a specific platform is
    also not fostering susceptive open-mindedness when confronted
    with the huge professional IT world.

    But I have hope; after issues with Windows

    What kinds of issues? Many millions of ordinary people have used
    Windows. Not many ordinary people use raw Linux.




    I installed Linux
    even for my (very old) father, and he has no problems with it
    (using browser, email, and a text processor). And my children
    installed Linux by themselves, anyway, and they're programming
    their academic research tasks on that platform. - No lengthy
    stupid discussions as in this thread.

    Well, be oblivious to all the issues then. Clearly that giant mountain
    of stuff works for you and it works for your kids.

    I however can see when someone is using a sledgehammer to crack a nut.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to David Brown on Fri Jan 19 18:28:15 2024
    On 19/01/2024 17:12, David Brown wrote:
    On 19/01/2024 16:03, bart wrote:
    On 19/01/2024 14:42, David Brown wrote:
    On 19/01/2024 14:18, bart wrote:

    It is also incomprehensible how someone can rant endlessly about
    "simplification" and "easy of use" when their claimed solution is to
    make non-standard limited duplications of the functionality already
    found on other machines.

    The programs I write ARE easy to build, using ONLY a compiler.

    You don't want to accept that, you'd rather it involved those
    mountains of stuff.

    /Please/ stop making stupid, exaggerated and incorrect claims about what other people want or think.

    I don't give a *beep* how easy or hard your programs are to compile -
    they are utterly irrelevant to me.  I don't have reason to believe they
    are very relevant to anyone else either.  But even if they were useful
    to me, or at least of interest to me, it does not matter to me if they
    need a whole range of standard utilities to build - because every system
    I have, or ever have had, has the standard utilities.

    Why is it so difficult for you to understand the concept of not caring?
    It is absurd to suggest I /want/ your programs to need "make" or other utilities to compile - I *do* *not* *care* if it needs them or not.
    There is no advantage or disadvantage to me if it uses make, sed, awk,
    bash, date.  There is no advantage or disadvantage to me if it does not
    need them either.  It is irrelevant.

    It /would/ be an inconvenience to me if it required some obscure and
    rarely used compiler like your own tools.  TCC is also pretty obscure,
    but Debian has it in it's repositories.  Needing an invasive tool that screws up your system, like MSVC, would also be a pain, as would
    anything that required very specific versions of tools.  But that would
    only matter if your programs were of interest.

    And of course I accept that /you/ think it is important that you don't
    use tools that everyone else finds convenient and useful.  I don't understand your reasons for that

    Because they don't work. Things like makefiles, even designed to work on Windows, had a 50% failure rate.

    Higher if they originated on Linux.

    100% if they originated on Linux and expected to run in that
    environment. Your solution here is to replicated that environment even
    on Windows.

    Then the failure rate is much lower, if I use WSL (but it can still
    fail). However that doesn't produce the native binaries I need.

    You will say use MSYS2, and here I need to put my foot down - how many
    rabbit holes do I have to go down to just to build a C program?

    You obviously have a high tolerance of such rabbit holes.

    Apparently it is inconceivable to you to have an application written in
    C say, that can be compiled either on Linux or Windows using only a C
    compiler.

    - it seems to be based on a determined
    battle to ensure that you fail to get anything you deem to be "Linux
    related" to work, combined with a fanaticism about "simple" that
    completely misses the point.  But I don't think anyone here doubts that /you/ think this is all desperately important, even though pretty much
    no one else cares.


    And /please/ stop making an arse of yourself here by calling a dozen
    standard utilities "mountains of stuff".

    Half of a Linux system, many thousands of lines of scripting code and makefiles, to build a project of a few dozen source files, is not a
    mountain?

    I've been looking at someone's C++ project which is a compiler for what
    they call a 'toy' language. There are 33 .cpp files.

    One of the dependencies however is something called LLVM. I've
    downloaded what appears to be the bundle needed to build this project.

    It is 140,000 files. Just unzipping it took 15 minutes.

    You guys just don't get it. What /I/ do is the exact opposite of this.

    You think it doesn't matter? A number of major language implementations
    that are currently using an LLVM backend are thinking of moving away
    from LLVM, or offering an alternative, BECAUSE it is so slow and cumbersome.

    But I will leave this alone now.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to bart on Fri Jan 19 18:43:08 2024
    bart <bc@freeuk.com> writes:
    On 19/01/2024 17:12, David Brown wrote:
    On 19/01/2024 16:03, bart wrote:
    On 19/01/2024 14:42, David Brown wrote:
    On 19/01/2024 14:18, bart wrote:

    It is also incomprehensible how someone can rant endlessly about
    "simplification" and "easy of use" when their claimed solution is to
    make non-standard limited duplications of the functionality already
    found on other machines.

    The programs I write ARE easy to build, using ONLY a compiler.

    You don't want to accept that, you'd rather it involved those
    mountains of stuff.

    /Please/ stop making stupid, exaggerated and incorrect claims about what
    other people want or think.

    I don't give a *beep* how easy or hard your programs are to compile -
    they are utterly irrelevant to me.  I don't have reason to believe they
    are very relevant to anyone else either.  But even if they were useful
    to me, or at least of interest to me, it does not matter to me if they
    need a whole range of standard utilities to build - because every system
    I have, or ever have had, has the standard utilities.

    Why is it so difficult for you to understand the concept of not caring?
    It is absurd to suggest I /want/ your programs to need "make" or other
    utilities to compile - I *do* *not* *care* if it needs them or not.
    There is no advantage or disadvantage to me if it uses make, sed, awk,
    bash, date.  There is no advantage or disadvantage to me if it does not
    need them either.  It is irrelevant.

    It /would/ be an inconvenience to me if it required some obscure and
    rarely used compiler like your own tools.  TCC is also pretty obscure,
    but Debian has it in it's repositories.  Needing an invasive tool that
    screws up your system, like MSVC, would also be a pain, as would
    anything that required very specific versions of tools.  But that would
    only matter if your programs were of interest.

    And of course I accept that /you/ think it is important that you don't
    use tools that everyone else finds convenient and useful.  I don't
    understand your reasons for that

    Because they don't work. Things like makefiles, even designed to work on >Windows, had a 50% failure rate.

    Another number pulled out from your backside.

    But then you're just trolling.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to bart on Fri Jan 19 19:50:26 2024
    On 19/01/2024 18:05, bart wrote:
    On 19/01/2024 16:32, Janis Papanagnou wrote:


    And as we see nothing helps if folks grew up and didn't leave
    that bubble, didn't even dare to have a look outside and try
    to grasp what's going on. Yet continuing uninformed rants about
    something they never experienced nor understood to a minimum
    extent.

    It's not me who's in the bubble. How about that giant bubble called
    'Linux'?

    I suppose from inside your bubble, it looks like you are the only
    sensible person and everyone else is in the bubble. Everyone is
    marching out of step, except you.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to bart on Fri Jan 19 19:48:08 2024
    On 19/01/2024 19:28, bart wrote:
    On 19/01/2024 17:12, David Brown wrote:

    And of course I accept that /you/ think it is important that you don't
    use tools that everyone else finds convenient and useful.  I don't
    understand your reasons for that

    Because they don't work. Things like makefiles, even designed to work on Windows, had a 50% failure rate.

    And you have statistics to back that up? Other than personal anecdotes,
    of course. (And before you get carried away, stack overflow questions
    might show a non-zero failure rate, but not a 50% rate.)

    Claims made without evidence or reason can be dismissed without evidence
    or reason. /You/ have trouble building software - we know that, and no
    one's arguing against that.


    Apparently it is inconceivable to you to have an application written in
    C say, that can be compiled either on Linux or Windows using only a C compiler.

    I'll give you /one/ last chance to stop imaging what you think I am
    saying and fighting against your ridiculous straw-man arguments.
    Whenever you find yourself writing "You seem to think...", "You
    want...", "You prefer...", stop and delete the paragraph. You are wrong
    every single time.


    But I will leave this alone now.


    OK.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Tim Rentsch@21:1/5 to Keith Thompson on Sat Jan 20 14:29:17 2024
    Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:

    [...] Just including <unistd.h> has undefined behavior as far as
    ISO C is concerned, [...]

    Not true. The behavior of #include <unistd.h> is defined in
    section 6.10.2 p2. One of two things is true: either the header
    named is part of the implementation, or it isn't. If the named
    header is part of the implementation, then it constitutes a
    language extension, and so it must be documented (and defined).
    If the named header is not part of the implementation, it still
    must be a header that the implementation can process (because of
    the constraint in 6.10.2 p1), and it must be processed the same
    way as a header that is part of the implementation (because of
    how the behavior is defined in 6.10.2 p2). Implementations are
    not at liberty to do anything they want just because the header
    named is not part of the implementation; such headers must be
    processed in just the same way as standard or extension headers,
    no matter how it is that they happen to be in the set of places
    the implementation has defined (and documented) to hold headers.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From bart@21:1/5 to Kaz Kylheku on Sun Jan 21 00:40:01 2024
    On 17/01/2024 18:47, Kaz Kylheku wrote:
    On 2024-01-17, bart <bc@freeuk.com> wrote:

    The only reason to use the shell for larger coding tasks that go
    beyonod process control is that is that it's the only language you can
    rely on being installed.

    If we're going to be dealing with C sources, then I guess we can assume
    a C compiler is going to be available, otherwise we might as well pack
    up and go home.

    So everthing needed could be done in C code with a C compiler being the
    only dependency. If done nicely, the user gets one single C file to
    compile and run, which will build and install the main app as needed.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Tim Rentsch on Sun Jan 21 04:46:00 2024
    On 2024-01-20, Tim Rentsch <tr.17687@z991.linuxsc.com> wrote:
    Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:

    [...] Just including <unistd.h> has undefined behavior as far as
    ISO C is concerned, [...]

    Not true. The behavior of #include <unistd.h> is defined in
    section 6.10.2 p2. One of two things is true: either the header
    named is part of the implementation, or it isn't. If the named
    header is part of the implementation, then it constitutes a
    language extension, and so it must be documented (and defined).

    The problem with this reasoning is

    1. Implementations in fact have internal headers that are not
    documented, and which are not supposed to be included directly.
    You will not get documentation for every single header that
    is accessible via #include, and it is not reasonable for ISO C to
    require it. I don't see where it does.

    2. A documented extension continues to be undefined behavior.
    The behavior is "defined", but not "ISO C defined". So even
    if all the implementation's internal headers were documented
    as extensions, their use would still be UB.

    Undefined behavior is for which "this document imposes not
    requirements", right?

    It is not behavior for which "this document, together with the
    ocumentation accompanying a given implementation, imposes
    no requirements". Just "this document" (and no other).

    If "this document" imposes no requirements, it's "undefined behavior",
    no matter who or what imposes additional requirements!

    Intuitively, a header which is part of an implementation can do
    anything. For instance #include <pascal.h> can cause the rest of the translation unit to be analyzed as Pascal syntax, and not C.

    An implementation can provide a header <reboot.h>, including
    which causes a reboot at compile time, link time, or
    execution time.

    If such headers happen to exist, what in the standard is violated?

    I used to experience a lot of push-back against the above views,
    but I'm seeing that people are coming around.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From James Kuyper@21:1/5 to Kaz Kylheku on Sun Jan 21 10:56:55 2024
    On 1/20/24 23:46, Kaz Kylheku wrote:
    On 2024-01-20, Tim Rentsch <tr.17687@z991.linuxsc.com> wrote:
    ...
    named is part of the implementation, or it isn't. If the named
    header is part of the implementation, then it constitutes a
    language extension, and so it must be documented (and defined).

    The problem with this reasoning is
    1. Implementations in fact have internal headers that are not
    documented, and which are not supposed to be included directly. You
    will not get documentation for every single header that
    is accessible via #include, and it is not reasonable for ISO C to
    require it. I don't see where it does.

    "An implementation shall be accompanied by a document that defines all implementation-defined and locale-specific characteristics and all
    extensions." 4p9

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From James Kuyper@21:1/5 to Kaz Kylheku on Sun Jan 21 12:11:20 2024
    Initially, I accidentally sent my response as private e-mail to Kaz
    rather than to the group. With his permission, here's the response he sent:

    On 1/21/24 01:37, Kaz Kylheku wrote:
    On 2024-01-20 21:50, James Kuyper wrote:
    "An implementation shall be accompanied by a document that defines all
    implementation-defined and locale-specific characteristics and all
    extensions." 4p9
    That sentence says something subtly different from something like
    "an implementation shall document everything that looks stable enough
    to be an extension".

    It is saying that there is required to be a document, and that in
    that document there may be a list of one or more extensions.

    The standard is defining the *fact* that this list is exhaustive:
    it comprises all the extensions that are offered in the contract
    between implementor and programmer.

    Thus, for instance, if programmers empirically discover a behavior
    which is not defined by ISO C, and which is not in that list,
    it is not an extension. The standard states that that list has
    all the extensions, and that discovered feature is not on it.

    The sentence does not require implementors to document,
    as an extension, every header file that is reachable by an #include directive.
    The standard doesn't provide a definition for "extension", though it
    makes many uses of the term, identifying a variety of things as possible
    or common extensions to C. What you're saying is, essentially, that
    extensions are defined as such by the fact that they are listed as such
    in that document. The biggest problem I have with that is that ISO has a convention that allows them to say just that - all they would have had
    to do is put "extension" in italics in 4p9, and that would identify the sentence in which it occurs as the definition of the term. They didn't
    choose to do so. I concede that they might not have thought of doing so.

    However, it seems more reasonable to me that whether or not something is
    an extension is not supposed to be up to the implementor to decide.
    Instead, anything that is an extension must be listed. This would be an
    easier interpretation to defend if there were any explicit definition of "extension" in the standard.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to James Kuyper on Sun Jan 21 17:55:27 2024
    On 2024-01-21, James Kuyper <jameskuyper@alumni.caltech.edu> wrote:
    However, it seems more reasonable to me that whether or not something is
    an extension is not supposed to be up to the implementor to decide.
    Instead, anything that is an extension must be listed. This would be an easier interpretation to defend if there were any explicit definition of "extension" in the standard.

    The implication is that anything that works by accident must be listed
    as an extension. If function arguments happen to be evaluated left to
    right, with all side effects complete between them, this has to be
    listed as an extension. Then in the future when the vendor finds that inconvenient for further compiler work, they have to take away the
    extension.

    I don't see how it can not be the implementor's privilege to assert what
    is reliable for use and documented, versus what is not.

    Unless they have some legal contract with particular customers,
    requiring certain things be provided.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    NOTE: If you use Google Groups, I don't see you, unless you're whitelisted.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Tim Rentsch@21:1/5 to Keith Thompson on Sun Jan 21 12:36:44 2024
    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:

    Janis Papanagnou <janis_papanagnou+ng@hotmail.com> writes:

    This is a CPP question that arose last month. It's not about an
    actual issue with the software, just out of curiosity and to be sure >>>>> it works reliable (it seemingly does).

    In a C99 program on Linux (Ubuntu) I intended to use usleep() and
    then also strnlen().

    When I added usleep() and its include file I got an error and was
    asked to define the CPP tag '_BSD_SOURCE'. I did so, and because I
    wanted side effects of that tag kept as small as possible I
    prepended it just before the respective #include and put it at the
    end of my #include list

    ...other #includes...
    #define _BSD_SOURCE
    #include <unistd.h>

    But as got obvious *that* way there had been side-effects and I
    had to put the tag at the beginning of all include files (which
    astonished me)

    #define _BSD_SOURCE
    #include <unistd.h>
    ...other #includes here...

    For the strnlen() function I needed another CPP tag, '_GNU_SOURCE'.
    So now I have both CPP tag definitions before the includes

    I second the recommendations of Lowell Gilbert and others not to
    define _BSD_SOURCE or _GNU_SOURCE (especially not _GNU_SOURCE)
    but instead seek alternatives, which are readily available for
    the two functionalities being sought in this case.

    #define _GNU_SOURCE /* necessary for strnlen() in string.h */
    #define _BSD_SOURCE /* necessary for usleep() in unistd.h */
    ...all #includes here...

    For strnlen(), put an inline definition in a header file:

    #ifndef HAVE_strnlen_dot_h_header
    #define HAVE_strnlen_dot_h_header

    #include <stddef.h>

    static inline size_t
    strnlen( const char *s, size_t n ){
    extern void *memchr( const void *, int, size_t );
    const char *p = memchr( s, 0, n );
    return p ? (size_t){ p-s } : n;
    }

    #include <string.h>

    #endif

    Disclaimer: this code has been compiled but not tested.

    strnlen() is specified by POSIX. It might make sense to
    re-implement it if your code needs to work on a non-POSIX system
    (that doesn't also provide it). Why would you want to do so
    otherwise?

    I'm trying to provide a helpful answer to the person I was
    responding to, not espouse a philosophical viewpoint. Why do you
    feel the need to start a style debate?

    I don't. I simply asked you a question. You've refused to answer
    it, and I won't waste my time asking again.

    I did answer your question. My answer is "I'm trying to provide a
    helpful answer to the person I was responding to [...]". I don't
    know why you think that wasn't an answer.

    memchr() is declared in <string.h>. Why would you duplicate its
    declaration rather than just using `#include <string.h>`?

    I had a specific reason for writing the code the way I did.
    It wasn't important to explain that so I didn't.

    Unsurprisingly, you refuse to do so even when asked directly.

    You need to learn that declining is not refusing. Providing a
    more complete explanation would uselessly muddy the waters. You
    are completely free to ask any questions you want. You have no
    right to demand answers, especially when my judgment is that what
    you think of as an answer would be a waste of time. Stop being so self-centered.

    For usleep(), define an alternate function usnooze(), to be used
    in place of usleep(). In header file usnooze.h:

    [snip]

    If your code doesn't need to be portable to systems that don't
    provide usleep(), you can just use usleep(). If it does, its
    probably better to modify the code so it uses nanosleep().

    Not everyone agrees with that opinion. Again, I'm just trying to
    provide an answer helpful to OP, not advance an agenda. Like I
    said in the part of my posting that you left out, I don't want to
    get involved in a style war. If OP wants to modify his code to
    use nanosleep(), I'm fine with that. If want wants to keep using
    usleep() or switch to using usnooze(), I'm fine with that too. I
    think it's more important in this case to provide options than to
    try to change someone's point of view.

    As usual, you vaguely denigrate my opinion without sharing your own.

    I'm not denigrating your opinion. I'm simply pointing out that
    it is an opinion, and that other people have different opinions.
    I'm not presenting my opinion (even assuming I have one on this
    subject, which is not guaranteed), because my purpose in posting
    was to provide options to the OP, so they could make a decision
    based on /their/ needs and their own opinions. I didn't consider,
    or even start to consider, what my own opinions on the question
    might be.

    I have a general observation. In giving your opinions, often they
    include a degree of argument or justification for why the views
    expressed are a good idea. I have very little interest in getting
    into an argument over personal opinions. If you want to hear more
    of my opinions, probably it would help if you could focus on the
    opinions more, and on the justifications or arguments less. If
    and when I feel like it would be useful to express an opinion,
    that does not automatically mean I think it would be useful to
    explain it or justify it, or have any interest in doing so. For
    me the first step is always understanding what it is the other
    person is thinking (and hopefully vice versa); anything past
    that is optional, and in most cases not worth pursuing.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From James Kuyper@21:1/5 to Kaz Kylheku on Sun Jan 21 21:57:25 2024
    On 1/21/24 12:55, Kaz Kylheku wrote:
    On 2024-01-21, James Kuyper <jameskuyper@alumni.caltech.edu> wrote:
    However, it seems more reasonable to me that whether or not something is
    an extension is not supposed to be up to the implementor to decide.
    Instead, anything that is an extension must be listed. This would be an
    easier interpretation to defend if there were any explicit definition of
    "extension" in the standard.

    The implication is that anything that works by accident must be listed
    as an extension. If function arguments happen to be evaluated left to
    right, with all side effects complete between them, this has to be
    listed as an extension. Then in the future when the vendor finds that inconvenient for further compiler work, they have to take away the
    extension.

    No, that's unspecified behavior, if 4p9 were intended to be understood
    as mandating documentation of all unspecified behavior, it would make
    the category of implementation-defined behavior redundant.

    Since extensions are also explicitly prohibited from changing the
    behavior of strictly conforming code, I think that in order for
    something qualify as an extension, it has to define behavior that the
    standard leaves undefined. In a certain sense, every implementation
    implicitly defines that behavior - whatever it is that actually happens
    when the behavior is undefined is that definition. However, I think an extension should have to be explicitly activated by some deliberate user choice, such as a compiler option or by defining the behavior of some
    kinds of syntax not allowed by the C standard. That's just IMHO, of
    course - in the absence of a explicit definition in the standard, it's
    hard to be sure what the committee intended.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Tim Rentsch@21:1/5 to James Kuyper on Wed Jan 24 07:42:26 2024
    James Kuyper <jameskuyper@alumni.caltech.edu> writes:

    On 1/21/24 12:55, Kaz Kylheku wrote:

    On 2024-01-21, James Kuyper <jameskuyper@alumni.caltech.edu> wrote:

    However, it seems more reasonable to me that whether or not
    something is an extension is not supposed to be up to the
    implementor to decide. Instead, anything that is an extension
    must be listed. This would be an easier interpretation to defend
    if there were any explicit definition of "extension" in the
    standard.

    The implication is that anything that works by accident must be
    listed as an extension. If function arguments happen to be
    evaluated left to right, with all side effects complete between
    them, this has to be listed as an extension. Then in the future
    when the vendor finds that inconvenient for further compiler work,
    they have to take away the extension.

    No, that's unspecified behavior, if 4p9 were intended to be
    understood as mandating documentation of all unspecified behavior,
    it would make the category of implementation-defined behavior
    redundant.

    Quite so.

    Since extensions are also explicitly prohibited from changing the
    behavior of strictly conforming code, I think that in order for
    something qualify as an extension, it has to define behavior that
    the standard leaves undefined.

    The essential property of an extension is that it must specify a
    language feature or library feature that is not specified in the C
    standard. Implementations are free to /define/ undefined behavior
    any way they want, and doing so doesn't require an extension; the
    point of documenting an extension is to /specify what happens/ in
    certain cases, which also means giving a guarantee that what is
    documented is what will happen (with the understanding that the
    guarantee is only that the implementation will make its best
    efforts to do so, because some results are outside of an
    implementation's ability to control).

    (I note in passing that an odd consequence of how the C standard
    uses certain terminology is that "defining" something doesn't stop
    it from being "undefined". The term "undefined behavior" is thus
    something of a misnomer. But I digress.)

    Conversely, an extension can specify a feature that is not, or
    might not be, undefined behavior, but still needs to be documented
    as an extension. For example

    #include <foo.h>

    is not undefined behavior, but must be documented as an extension
    if the header <foo.h> is part of the implementation, because what
    is in the header <foo.h> is not specified in the C standard.
    Similarly, a declaration like

    typedef __uint128_t U128;

    is not necessarily undefined behavior, because implementations are
    free to define reserved identifiers any way they wish. But if it's
    important that it work, then it must be documented as an extension,
    as otherwise an implementation is free to reject it by virtue of
    using a language feature not specified in the C standard (which
    therefore means the program is not strictly conforming, and so can
    be rejected).

    In a certain sense, every
    implementation implicitly defines that behavior - whatever it is
    that actually happens when the behavior is undefined is that
    definition. However, I think an extension should have to be
    explicitly activated by some deliberate user choice, such as a
    compiler option or by defining the behavior of some kinds of
    syntax not allowed by the C standard. That's just IMHO, of course
    - in the absence of a explicit definition in the standard, it's
    hard to be sure what the committee intended.

    That's a QOI issue. I'm not convinced that it's a good idea in
    all cases. For example, either of the two cases mentioned above:

    #include <foo.h>

    typedef __uint128_t U128;

    do not IMO need an explicit user action to activate, because it's
    obvious they are making use of extensions, and are pretty unlikely
    to occur by accident. I concede however that other people may
    reasonably hold other views on that question.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Tim Rentsch@21:1/5 to Kaz Kylheku on Wed Jan 31 12:43:47 2024
    Kaz Kylheku <433-929-6894@kylheku.com> writes:

    On 2024-01-21, James Kuyper <jameskuyper@alumni.caltech.edu> wrote:

    However, it seems more reasonable to me that whether or not something is
    an extension is not supposed to be up to the implementor to decide.
    Instead, anything that is an extension must be listed. This would be an
    easier interpretation to defend if there were any explicit definition of
    "extension" in the standard.

    The implication is that anything that works by accident must be
    listed as an extension. If function arguments happen to be
    evaluated left to right, with all side effects complete between
    them, this has to be listed as an extension. Then in the future
    when the vendor finds that inconvenient for further compiler work,
    they have to take away the extension.

    I don't see how it can not be the implementor's privilege to
    assert what is reliable for use and documented, versus what is
    not.

    It's important to understand what constitutes an extension. And
    there are different kinds of extensions.

    If an implementation chooses, say, to evaluate function arguments
    always left-to-right, that is perfectly okay (and need not be an
    extension). But if an there is to be a /guarantee/ that function
    arguments will always be evaluated left-to-right, that means
    providing a guarantee that the C standard does not; thus we have an
    extension (to the requirements in the C standard), and the extension
    must be documented. This case is one kind of extension.

    If an implementation chooses to alter the semantics of, for example,
    an extern declaration inside a function body, so that the declared
    identifier has file scope rather than block scope, that is a
    different kind of extension. At the point of the extern declaration
    we can't actually detect any difference. A subsequent use, however,
    outside the function body, would normally be a constraint violation
    and so require a diagnostic, but under the altered semantics the
    identifier has already been declared and so no diagnostic is needed.
    (Note that this behavior is one of the cases listed in Annex J.5,
    "Common extensions".) This example illustrates a second kind of
    extension, and they always must be documented as such.

    If an implementation chooses to define, for example, __uint128_t to
    be the name of an integer type, that represents a third kind of
    extension. (Annex J.5 also mentions such definitions as an example
    on its list of "Common extensions".) Such cases must be documented
    as extensions because, one, the expected default is that there be no
    definition (which normally would cause a diagnostic because of some
    constraint violation), and two, only the implementation is in a
    position to know if the symbol in question was defined by the
    implementation or by something else. Clearly any definition the
    implementation provides signals a change from the expected default,
    which by any reasonable definition of the word constitutes an
    extension, and hence must be documented.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From David Brown@21:1/5 to Keith Thompson on Thu Feb 1 09:19:40 2024
    On 31/01/2024 22:41, Keith Thompson wrote:
    Tim Rentsch <tr.17687@z991.linuxsc.com> writes:
    Kaz Kylheku <433-929-6894@kylheku.com> writes:
    It's important to understand what constitutes an extension. And
    there are different kinds of extensions.

    If an implementation chooses, say, to evaluate function arguments
    always left-to-right, that is perfectly okay (and need not be an
    extension). But if an there is to be a /guarantee/ that function
    arguments will always be evaluated left-to-right, that means
    providing a guarantee that the C standard does not; thus we have an
    extension (to the requirements in the C standard), and the extension
    must be documented. This case is one kind of extension.

    If an implementation chooses to guarantee left-to-right evaluation, I
    don't see anything in the standard that requires that guarantee to be documented. (Of course it can and should be.)


    I would say that a "guarantee" is a promise - in this case, from the
    compiler writers to compiler users. It's not a guarantee if they don't
    tell anyone! Then it's just an implementation detail. But I don't see
    that it has to be documented in any particular way, or called an
    "extension". IIRC MSVC's "guarantee" that it won't use type-based alias analysis for optimisation (and thus casting between pointer types will
    work as some people think they should work) is "documented" in a blog
    post by one of the compiler developers.

    Of course it is up to the user to decide how much they trust the
    guaranteed additional behaviour. A mailing list post with "We'll
    evaluate left to right, or your money back!" or a note in a blog page
    would probably not inspire as much confidence as clear information in
    the main reference documentation for a compiler.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Tim Rentsch@21:1/5 to Kaz Kylheku on Sun Feb 11 17:38:21 2024
    Kaz Kylheku <433-929-6894@kylheku.com> writes:

    On 2024-01-20, Tim Rentsch <tr.17687@z991.linuxsc.com> wrote:

    Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:

    [...] Just including <unistd.h> has undefined behavior as far as
    ISO C is concerned, [...]

    Not true. The behavior of #include <unistd.h> is defined in
    section 6.10.2 p2. One of two things is true: either the header
    named is part of the implementation, or it isn't. If the named
    header is part of the implementation, then it constitutes a
    language extension, and so it must be documented (and defined).

    The problem with this reasoning is

    1. Implementations in fact have internal headers that are not
    documented, and which are not supposed to be included directly.
    You will not get documentation for every single header that
    is accessible via #include, and it is not reasonable for ISO C to
    require it. I don't see where it does.

    First, James Kuyper already identified section 4 paragraph 9.

    Second, implementations do have lots of internal headers that are
    not meant to be included directly, but just because such headers
    exist doesn't mean they can be #include'd. Consider these lines
    at the start of <bits/byteswap.h>

    #if !defined _BYTESWAP_H && !defined _NETINET_IN_H && !defined _ENDIAN_H
    # error "Never use <bits/byteswap.h> directly; include <byteswap.h> instead."
    #endif

    Clearly implementations can and do distinguish between headers
    that are meant to be user #include-able and those that are not,
    and take steps to protect the hidden ones. It seems obvious
    that implementations consider it reasonable to document headers
    that are meant to be user #include-able, and to protect the
    hidden headers so they can't be #included directly, because
    that is what existing implementations do.

    2. A documented extension continues to be undefined behavior.
    The behavior is "defined", but not "ISO C defined". So even
    if all the implementation's internal headers were documented
    as extensions, their use would still be UB.

    Undefined behavior is for which "this document imposes not
    requirements", right?

    It is not behavior for which "this document, together with the
    ocumentation accompanying a given implementation, imposes
    no requirements". Just "this document" (and no other).

    If "this document" imposes no requirements, it's "undefined
    behavior", no matter who or what imposes additional requirements!

    Your logic here is all messed up. The very first sentence uses
    circular reasoning. Next you ignore the point that the "define" in
    section 4 paragraph 9 is a Standard-imposed requirement, and surely
    the Standard means to require that implementations obey their own
    definitions, because that's what "define" means. The idea that
    having to document extensions doesn't imply an ISO C requirement is
    just being obtuse.

    Perhaps a more fundamental flaw is you have the key implication
    backwards. IF using a particular program construct or data value
    has been identified as undefined behavior, THEN the C standard
    imposes no requirements on those uses. The implication doesn't go
    the other way. For example, the C standard imposes no requirements
    for what happens on Tuesdays. That does not mean that compiling a
    program on a Tuesday is undefined behavior. The universe of
    discourse is program constructs and data values ("values" here also
    includes things such as trap representations). Saying "an unknown
    header is undefined behavior" is meaningless, because it's outside
    the universe of discourse, just like Tuesdays are. The C construct
    in question is "#include <unistd.h>", and the C standard defines
    behavior for that construct.

    Intuitively, a header which is part of an implementation can do
    anything. For instance #include <pascal.h> can cause the rest of
    the translation unit to be analyzed as Pascal syntax, and not C.

    An implementation can provide a header <reboot.h>, including
    which causes a reboot at compile time, link time, or
    execution time.

    If such headers happen to exist, what in the standard is violated?

    More bad logic. Of course the contents of a header or #include'd
    file can have, for example, syntax errors, that result in crossing
    the line into undefined behavior. The question is not whether an
    unknown header /can/ cross the line into undefined behavior but
    whether it /must/ cross that line. Whether a #include of an unknown
    header encounters undefined behavior depends on the contents of the
    header, and nothing else. A #include <unistd.h> MIGHT cross the
    line into UB land, but there is nothing that says it MUST cross that
    line.

    Note by the way that exactly the same reasoning applies to an
    unknown source file, such as #include "foo.h". If we don't know
    what's in foo.h (maybe it was left by a hacker in a little-used
    byway of where the implementation looks for #include'd source
    files), then undefined behavior is possible, but whether UB
    actually happens depends only on the contents of the file, not on
    whether we know what's in it.

    I used to experience a lot of push-back against the above views,
    but I'm seeing that people are coming around.

    I expect that is simply a consequence of your dogged persistence
    and the brighter people dropping out of the conversations. It's
    not whether some people start to be convinced, but rather which
    people are convinced, and how firmly convinced, and why.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From dave_thompson_2@comcast.net@21:1/5 to 433-929-6894@kylheku.com on Mon Feb 26 04:17:30 2024
    On Thu, 18 Jan 2024 19:40:44 -0000 (UTC), Kaz Kylheku <433-929-6894@kylheku.com> wrote:

    On 2024-01-18, bart <bc@freeuk.com> wrote:

    All I know about PowerShell is that instead of typing 'prog' to run the 'prog.exe' you've just created, you have to do '.\prog' just like you
    have to do './prog' in Linux.

    That's not true for 'prog', but is true for any name of a PS cmdlet or
    alias. In that case you must use a pathname (which can be EITHER
    .\name OR ./name) OR name.exe.

    CMD has some builtins which similarly make program files inaccessible
    by simple name, but fewer. The ones I recall off the top of my head
    are cd pushd popd dir mkdir md rmdir rd erase type set rem. And here
    .\name or name.exe works but not ./name.

    I don't know anything about PowerShell. In the case of the POSIX-like
    shells, this is just a consequence of the . directory not being listed
    in the PATH variable, which is an end-user configuration matter.

    If you care about the convenience of just typing "prog", more than
    about the security aspect, you can just add . to your PATH.

    No. In PS (_and_ in CMD) current directory aka dot is _always_ tried
    even if it's not in PATH (which by default it isn't). But cmdlets and
    aliases or builtins are tried first and override any file, per above.

    Unix shells similarly won't find by simple name a program in . even
    when . IS in PATH if the name is a builtin or keyword like 'cd' or
    'if' or 'test' (probably the most popular name to encounter this
    error), or a function or alias (shells don't come with lots of aliases predefined like PS, but many distros or users add them).

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to dave_thompson_2@comcast.net on Mon Feb 26 15:56:27 2024
    dave_thompson_2@comcast.net writes:
    On Thu, 18 Jan 2024 19:40:44 -0000 (UTC), Kaz Kylheku ><433-929-6894@kylheku.com> wrote:

    On 2024-01-18, bart <bc@freeuk.com> wrote:

    All I know about PowerShell is that instead of typing 'prog' to run the
    'prog.exe' you've just created, you have to do '.\prog' just like you
    have to do './prog' in Linux.

    That's not true for 'prog', but is true for any name of a PS cmdlet or
    alias. In that case you must use a pathname (which can be EITHER
    .\name OR ./name) OR name.exe.

    Actually, on linux, one can just do 'prog', if PATH includes
    an path element containing a single dot. One generally doesn't
    do this, but it is certainly possible.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Tim Rentsch@21:1/5 to All on Thu Mar 14 23:12:31 2024
    Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:

    [...]

    P.S. My apologies for not responding to your posting earlier.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Tim Rentsch@21:1/5 to Keith Thompson on Thu Mar 14 23:11:27 2024
    Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:

    Tim Rentsch <tr.17687@z991.linuxsc.com> writes:

    Kaz Kylheku <433-929-6894@kylheku.com> writes:
    It's important to understand what constitutes an extension. And
    there are different kinds of extensions.

    If an implementation chooses, say, to evaluate function arguments
    always left-to-right, that is perfectly okay (and need not be an
    extension). But if an there is to be a /guarantee/ that function
    arguments will always be evaluated left-to-right, that means
    providing a guarantee that the C standard does not; thus we have an
    extension (to the requirements in the C standard), and the extension
    must be documented. This case is one kind of extension.

    If an implementation chooses to guarantee left-to-right evaluation, I
    don't see anything in the standard that requires that guarantee to be documented. (Of course it can and should be.)

    It's not entirely clear to me exactly what must or must not be
    considered to be an "extension" as the C standard uses the term. The standard doesn't show the word "extension" in italics or provide a
    definition in section 3.

    Unless your point is that it's not a guarantee unless it's documented?
    But I don't see that that documentation is required *by the standard*.

    It seems likely that we have different notions of what "a guarantee"
    is. As I am using the term it does not mean a statement of what
    does happen but a promise for what will happen. Consulting some
    online dictionaries, I find the following definitions (among others
    that for the most part are not relevant). Note that all of these
    are for "guarantee" used as a noun (presented in the order of
    shortest first):

    an assurance for the fulfillment of a condition

    Something that assures a particular outcome or condition.

    A pledge that something will be performed in a specified
    manner.

    an unconditional commitment that something will happen or
    that something is true

    a written assurance that some product or service will be
    provided or will meet certain specifications

    A promise or assurance, especially one given in writing, that
    attests to the quality or durability of a product or service.

    a promise or assurance, especially one in writing, that
    something is of specified quality, content, benefit, etc.,
    or that it will perform satisfactorily for a given length of
    time: "a money-back guarantee".

    In the context of a C implementation, for some condition to be a
    guarantee I would say it must be written down (which is to say,
    documented), as otherwise it doesn't qualify as a guarantee. In
    any case that is how I am using the term.

    Incidentally, as best I can determine the C standard uses the
    term "guarantee" as a noun in only one place in normative text
    (6.5.2.3 p6) and in two places in informative text (5.1.2.4 p23
    and a footnote to 7.22.2.1 p2). Various forms of "guarantee"
    used as a verb appear in the C standard in lots of places. I am
    specifically using the noun form in my comments here.

    I think most people would agree that any documented guarantee of
    behavior beyond what the C standard requires of all implementations
    (and is not part of the documentation for implementation-defined
    behaviors) qualifies as an extension to the C language, and thus
    should be considered an extension as the ISO C standard uses the
    term, given that the C standard itself does not define the term
    but (one assumes) uses the word in its ordinary English sense.


    [...]

    If an implementation chooses to define, for example, __uint128_t to
    be the name of an integer type, that represents a third kind of
    extension. (Annex J.5 also mentions such definitions as an example
    on its list of "Common extensions".) Such cases must be documented
    as extensions because, one, the expected default is that there be no
    definition (which normally would cause a diagnostic because of some
    constraint violation), and two, only the implementation is in a
    position to know if the symbol in question was defined by the
    implementation or by something else. Clearly any definition the
    implementation provides signals a change from the expected default,
    which by any reasonable definition of the word constitutes an
    extension, and hence must be documented.

    Again, that can and should be documented, but since any use of the
    identifer __uint128_t has undefined behavior,

    That isn't right. The C standard says that declaring or defining a
    reserved identifier is undefined behavior. The C standard does NOT
    say that using a reserved identifier is undefined behavior. If an implementation does not define an identifier like __uint128_t, then
    any use in ordinary code (that is, not part of a pre-processor
    directive) should produce a diagnostic due to a syntax or constraint
    violation. If we don't get a diagnostic we know that the identifier
    /must/ have been defined by the implementation, and because it is a
    change in behavior over standard C it must be documented as an
    extension.

    (Note added during editing: "any use in ordinary code" is meant in
    the sense of "a use that is not a declaration or definition".)

    I don't see any
    requirement in the standard that it must be documented.

    It must be documented because it represents a change in behavior
    over what the C standard requires, and any such change constitutes
    an extension to the language, and the C standard requires extensions
    to be documented.

    (Aside: gcc has an extension adding __int128 as a keyword, supporting
    types "__int128" and "unsigned __int128", but only on some target
    systems. These do not qualify as extended integer types. I understand
    that "__uint128_t" was a hypothetical example.)

    On the systems I use the identifiers __int128_t and __uint128_t are
    both predefined type names (and not keywords), under both gcc and
    clang. I see some support for __int128 as a keyword, but the
    predefined type names seem to be more reliable (at least in my
    environments).

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)