Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Compile Seed7 with mcc32 and mcc64

287 views
Skip to first unread message

mertes...@gmail.com

unread,
Aug 29, 2018, 4:20:33 AM8/29/18
to
Hello,

Last year I tried to compile Seed7 with the C compilers mcc32 and
mcc64, provided by Bart. Now, when I look at

https://github.com/bartg/langs/tree/master/bccproj

the compilers mcc32.c and mcc64.c have been gone.
What happend to this C compilers?
Does anybody know more about them?

I try to compile Seed7 with many different C/C++ compilers to improve
Seed7. OTOH it has turned out that Seed7 is a good C compiler (and
run-time library respectively operating system) test suite.

If you have an exotic C compiler / hardware / operating system I would
be interested to see if you succeed in compiling Seed7.

About Seed7 itself: It is not intended to be a replacement language
for C. OTOH Seed7 has been used to write typical systems programming
things. E.g.: Libraries for TLS (used by HTTPS), ZIP compression and
TAR files have been written in Seed7. Contrary to C system properties
like little-/big-endian, the operating system used and many more are
abstracted away. This way Seed7 programs are source code portable.

Regards,
Thomas Mertes

--
Seed7 Homepage: http://seed7.sourceforge.net
Seed7 - The extensible programming language: User defined statements
and operators, abstract data types, templates without special
syntax, OO with interfaces and multiple dispatch, statically typed,
interpreted or compiled, portable, runs under linux/unix/windows.

Bart

unread,
Aug 29, 2018, 7:11:07 AM8/29/18
to
On 29/08/2018 09:20, mertes...@gmail.com wrote:
> Hello,
>
> Last year I tried to compile Seed7 with the C compilers mcc32 and
> mcc64, provided by Bart. Now, when I look at
>
> https://github.com/bartg/langs/tree/master/bccproj
>
> the compilers mcc32.c and mcc64.c have been gone.
> What happend to this C compilers?
> Does anybody know more about them?

They were withdrawn (from that site) for several reasons.

First, the amount of antagonism I received in this group towards me, my
views of the language and its associated tools, my alleged poor
knowledge of the subject, and the quality of my generated sources, put
me off having anything to do with C or making any of my C code public.

Second, my new compiler for mcc's implementation language didn't support
a C target for a few months (now it does, and it can produce the
single-file C distributions instantly; previously it was bit of an ordeal).

Third, there was a bug in the compiler (to do with the unusual (*p).m
construct) that I felt was better dealt with with an overhaul of the
underlying handling. This I am doing now (using another product to test
the changes which will then be ported to the C compiler).

You can find the current version here (this one needs its own assembler
and linker, also here):

https://github.com/sal55/mlang/tree/master/dist

Although I'm not sure how stable that is. I know it may not build and
run itself properly - it uses (*p).m constructs - so from that POV I'm
not happy with it.

I suggest you wait a week or two for a revised version to appear there,
which may also be a little more integrated.

As far as building Seed7, it will still have problems with missing bits
of header files, or missing complete ones like winsock2.h. Those would
have to be dealt with.

(Also it tends to call itself 'bcc' now rather than 'mcc', although that
is not that big a deal. You can name the executable what you like.)

> I try to compile Seed7 with many different C/C++ compilers to improve
> Seed7. OTOH it has turned out that Seed7 is a good C compiler (and
> run-time library respectively operating system) test suite.

It might be, due to its scale. But it's not quite as imaginative in its
use of C (and its macro system) as some programs I've seen, which can
present extra challenges.

--
bart

Keith Thompson

unread,
Aug 29, 2018, 12:35:45 PM8/29/18
to
Bart <b...@freeuk.com> writes:
[...]
> (Also it tends to call itself 'bcc' now rather than 'mcc', although that
> is not that big a deal. You can name the executable what you like.)
[...]

Be aware that there's an existing compiler called "bcc".
https://linux.die.net/man/1/bcc

Do not mistake this information for an opinion about what you should do
about that fact.

--
Keith Thompson (The_Other_Keith) ks...@mib.org <http://www.ghoti.net/~kst>
Working, but not speaking, for JetHead Development, Inc.
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"

BGB

unread,
Aug 30, 2018, 5:31:50 AM8/30/18
to
On 8/29/2018 6:11 AM, Bart wrote:
> On 29/08/2018 09:20, mertes...@gmail.com wrote:
>> Hello,
>>
>> Last year I tried to compile Seed7 with the C compilers mcc32 and
>> mcc64, provided by Bart. Now, when I look at
>>
>>    https://github.com/bartg/langs/tree/master/bccproj
>>
>> the compilers mcc32.c and mcc64.c have been gone.
>> What happend to this C compilers?
>> Does anybody know more about them?
>
> They were withdrawn (from that site) for several reasons.
>
> First, the amount of antagonism I received in this group towards me, my
> views of the language and its associated tools, my alleged poor
> knowledge of the subject, and the quality of my generated sources, put
> me off having anything to do with C or making any of my C code public.
>

In my case, it seems to more be a case of whatever I do, no one cares,
nor even really acknowledges that my efforts exist.

Recently, I don't get that much hostility at least.


> Second, my new compiler for mcc's implementation language didn't support
> a C target for a few months (now it does, and it can produce the
> single-file C distributions instantly; previously it was bit of an ordeal).
>
> Third, there was a bug in the compiler (to do with the unusual (*p).m
> construct) that I felt was better dealt with with an overhaul of the
> underlying handling. This I am doing now (using another product to test
> the changes which will then be ported to the C compiler).
>
> You can find the current version here (this one needs its own assembler
> and linker, also here):
>
>     https://github.com/sal55/mlang/tree/master/dist
>
> Although I'm not sure how stable that is. I know it may not build and
> run itself properly - it uses (*p).m constructs - so from that POV I'm
> not happy with it.
>
> I suggest you wait a week or two for a revised version to appear there,
> which may also be a little more integrated.
>


I also have my own C compiler (BGBCC), which is written in C, and
basically works, though currently only has targets for a few of my own
experimental ISA designs.

It doesn't currently have an x86 or x86-64 backend, mostly as I don't
currently have a compelling use-case to do so (since GCC and MSVC exist
for compiling C on x86; and do pretty well at it).


The C variant itself is otherwise pretty mundane.

Decided to leave off a list of language extensions and function
intrinsics. It has some extensions, but it backtracked some and is now
"more generic" than its earlier form, mostly as it turned out to be more
useful in my case to have something that at least tries to competently
compile normal C code.

Though, there are some pros/cons as the compiler architecture itself is
a bit unusual; largely dispensing with the use of separate compilation
and a traditional linker stage. Effectively, preprocessing and parsing
are separate between translation units, but then the compiler funnels
down into a large shared context for the rest, with things like
libraries handled by the "middle end" emitting or consuming blobs of a
stack-oriented bytecode IR.


The compiler generally supports C well enough to build working versions
of Doom and Quake and similar, along with some other stuff. This at
least puts it past a certain threshold of "basically works".

All bets are off otherwise, as compiler debugging is kinda hard.


For anyone curious, a version is available here, with the compiler in
the "bgbcc22" directory.

https://github.com/cr88192/bgbtech_btsr1arch


For anyone wondering about one of the ISA's it targets:

Larger, Fancier, 64-bit ISA:
https://github.com/cr88192/bgbtech_btsr1arch/wiki/BJX2-ISA
A related, but simpler 32-bit ISA:
https://github.com/cr88192/bgbtech_btsr1arch/wiki/BTSR1B_ISA


Mostly exists for my own hobbyist fiddling for now, even if technically
probably kinda pointless with the existence of ISA's like RISC-V and
similar (which have the advantage of being usefully complete and having
industry support...).

Though, the design and aesthetic is a bit different IMO.


Also note that features which aren't supported directly (such as integer
divide or modulo; 64-bit integer multiply; ...) are implemented in
software via runtime calls (as opposed to relying on "trap and emulate").

Similarly, they are in two different "weight classes", the smaller
intended for a board more like a Mimas, and the larger one for a board
more like the Arty S7.


> As far as building Seed7, it will still have problems with missing bits
> of header files, or missing complete ones like winsock2.h. Those would
> have to be dealt with.
>
> (Also it tends to call itself 'bcc' now rather than 'mcc', although that
> is not that big a deal. You can name the executable what you like.)
>

Though, there are several other compilers that have used the BCC name...


My case, I am mostly building stuff bare metal.

Pretty much no hope ATM of my compiler building something which needs to
be running on an OS.

For Doom and Quake, I am using a modified / hacked-up version of
PDPCLIB, with some glue logic in the back for things like low-level IO
and FAT32 / filesystem support.

For an "OS", there would probably need to be a partial split, with the
program using the C library "DLL style", and the C library making system
calls into a proper "OS kernel". Not done any of this part yet...


>> I try to compile Seed7 with many different C/C++ compilers to improve
>> Seed7. OTOH it has turned out that Seed7 is a good C compiler (and
>> run-time library respectively operating system) test suite.
>
> It might be, due to its scale. But it's not quite as imaginative in its
> use of C (and its macro system) as some programs I've seen, which can
> present extra challenges.
>

FWIW: When I first tried compiling Doom it managed to break my compiler
by stepping on various edge cases.


Some stuff it still don't really support though, and it needed to be
hacked around. Mostly it was stuff having to do with mismatched
declarations between translation units, and some initialization
expressions for global variables which my compiler can't handle.

Partly these are limitations partly due to the compiler architecture.
Some would require tweaking how the compiler handles the global toplevel
to better mimic the behavior of separate compilation and linking (1).

Others would require effectively being able to evaluate expressions at
link-time involving the resolved addresses of global declarations (2).


1: Example:
//TU1
int gblthing[16];
//TU2
gthing_t *gblthing[29];
Traditional behavior: union-style merge to the large size, or use
whichever is initialized.
BGBCC: Currently just sort of freaks out and crashes.
Workaround: make it so that both match and/or split into different
things (if they are seemingly unrelated).

It can be noted that Doom did this sort of thing in a few places, but
Quake did not.


2: Example:
int foo1;
...
int foo2;
...
int foo3 = (&foo2)-(&foo1); //mine can't currently do this.
//workaround: copy this into an Init() function or similar.


There were a few things which broke when porting Doom to Win64 and which
MSVC was not happy with, so had to fix these. This included a few
instances of writing past the end of stack arrays (Quake also did this a
few times, MSVC triggers "security cookie" breakpoint when this
happens). Also some of the usual instances of "this code is not 64-bit
clean".


Probably, compiling other stuff would shake up more bugs.
Also can note that I have a feature to randomly shuffle all the
functions within the program image (as a potential security measure),
but this leads to occasional (also random) compiler crashes and/or
building a broken version of the program.

These are hard to track down, but presumably have to do with some
aspects of the alignment-sensitive instruction coding (it tries to
generate alternate instructions in some cases such that a longer
instruction form never crosses a 32-byte boundary).


So, sadly, bugs remain...

Rick C. Hodgin

unread,
Aug 30, 2018, 8:01:48 AM8/30/18
to
On Thursday, August 30, 2018 at 5:31:50 AM UTC-4, BGB wrote:
> [snip]

I am consistently impressed with your work, BGB. My hat goes
off to you.

--
Rick C. Hodgin

Bart

unread,
Aug 30, 2018, 8:07:38 AM8/30/18
to
On 30/08/2018 10:33, BGB wrote:
> On 8/29/2018 6:11 AM, Bart wrote:

>
> For anyone wondering about one of the ISA's it targets:
>
> Larger, Fancier, 64-bit ISA:
>   https://github.com/cr88192/bgbtech_btsr1arch/wiki/BJX2-ISA
> A related, but simpler 32-bit ISA:
>   https://github.com/cr88192/bgbtech_btsr1arch/wiki/BTSR1B_ISA

You create your own processors? Not something I've tried yet (apart from
on paper years ago, and then it would to make something compiler-friendly).

> FWIW: When I first tried compiling Doom it managed to break my compiler
> by stepping on various edge cases.

Other people's programs are full of such things, almost as if they
delight in pushing the language as far as it will go. They seem to
deliberately track down and use the most obscure library functions they
can find (in standard, POSIX and Windows headers) just so /you/ have to
keep stopping to track them down yourself.

> 1: Example:
>   //TU1
>   int gblthing[16];
>   //TU2
>   gthing_t *gblthing[29];
> Traditional behavior: union-style merge to the large size, or use
> whichever is initialized.

This ought to be an error (1) because two units are exporting the same
name; (2) Even if only one exports it, it is wrong because it is
defining the same object as a different type and (3) as a different size.

> 2: Example:
>   int foo1;
>   ...
>   int foo2;
>   ...
>   int foo3 = (&foo2)-(&foo1);  //mine can't currently do this.
>     //workaround: copy this into an Init() function or similar.

if foo3 is static then gcc can't do this either. If not, then this is a
normal declaration plus initialisation. It should be no different from:

int foo3; foo3 = (&foo2)-(&foo1);

Even if &foo1 and &foo2 are static, their difference can't be reduced to
an integer constant.

> Probably, compiling other stuff would shake up more bugs.

I did maintain a list of issues with my compiler. Then I realised that
producing a C compiler that supported every feature, and that would
compile anything thrown at it, and that came with a complete set of
headers that people expect, would take up the rest of my life. And
that's just for one target.

Tt would also be a poor product because it would need to support every
abused, misused and deprecated feature.

That's what happens with implementing a language with a considerable
amount of baggage, a huge body of source code and myriad existing
implementations all slightly different.

So my little version of it is just a curiosity.

--
bart

BGB

unread,
Aug 30, 2018, 2:46:45 PM8/30/18
to
On 8/30/2018 7:07 AM, Bart wrote:
> On 30/08/2018 10:33, BGB wrote:
>> On 8/29/2018 6:11 AM, Bart wrote:
>
>>
>> For anyone wondering about one of the ISA's it targets:
>>
>> Larger, Fancier, 64-bit ISA:
>>    https://github.com/cr88192/bgbtech_btsr1arch/wiki/BJX2-ISA
>> A related, but simpler 32-bit ISA:
>>    https://github.com/cr88192/bgbtech_btsr1arch/wiki/BTSR1B_ISA
>
> You create your own processors? Not something I've tried yet (apart from
> on paper years ago, and then it would to make something compiler-friendly).
>

I write the Verilog for my own processors at least, though granted the
closest I have gotten to "usable" on this front was BSR1 working in a
Verilog simulation (and running some test programs).

In FPGA synthesis though, it came out as ~ 5 kLUT though, which is a bit
over my target (was hoping more for ~ 3 kLUT).

Then created BSR1B as a further simplification, but not yet got around
to a Verilog implementation. The hope is that the simplifications would
allow reducing the LUT cost.


The BJX2 design is more complex and performance oriented, and thus far
its Verilog impl isn't really complete (and might need some redesign due
to turning into a bit of a mess).

I generally also have emulators though, and most of this stuff works
well enough in emulation. These are generally emulators which run
everything at the level of the instruction set, and count clock-cycles
to try to reflect the expected performance of what the actual hardware
would do.

For now, these emulators are just interpreters as my desktop PC is
*massively* faster than what the FPGA cores would be able to do.

Note that it would be scalar / in-order, and seems to have IPC roughly
comparable to a 486 (and currently the framerates it would give for
Quake at 100MHz fall below what could really be regarded as playable;
though it fares a bit better with Doom).


>> FWIW: When I first tried compiling Doom it managed to break my
>> compiler by stepping on various edge cases.
>
> Other people's programs are full of such things, almost as if they
> delight in pushing the language as far as it will go. They seem to
> deliberately track down and use the most obscure library functions they
> can find (in standard, POSIX and Windows headers) just so /you/ have to
> keep stopping to track them down yourself.
>

Fair enough, though currently all it really has is a basic C library.
No real POSIX support yet.
No support for Windows APIs.


POSIX would probably be done if/when I get around to something more
resembling a proper OS.

No current plans for a Linux port (looks like far too big of a job to be
worthwhile), but a simpler Unix-like OS would probably be doable.

Though, effectively in the sense of a Unix-like OS using a FAT32
filesystem and PE/COFF binaries (for my ISA's I was using PE32 and PE32+
as the canonical format for binaries; mostly for technical reasons).

Similarly, FAT32 is more or less a standard on SD media (nevermind if
some aspects of its design have hair or are sub-optimal), and is what I
am already using here.


>> 1: Example:
>>    //TU1
>>    int gblthing[16];
>>    //TU2
>>    gthing_t *gblthing[29];
>> Traditional behavior: union-style merge to the large size, or use
>> whichever is initialized.
>
> This ought to be an error (1) because two units are exporting the same
> name; (2) Even if only one exports it, it is wrong because it is
> defining the same object as a different type and (3) as a different size.
>

Yes, but it sort of works in traditional compilers, which don't actually
notice or care about types at link time, and when merging
".bss"/"COMDAT" will use whichever is larger.

There were several other cases / varieties of global name clash in Doom,
a few rejected when building with MSVC (which seems to be more strict
about this sort of thing than GCC), but others were a problem for my
compiler.

Another closely related issue was that of having function declarations
and global variables with the same names. GCC seems to quietly allow
these, but MSVC will make an error if both happen within the same
translation unit, but will allow them to happen if in separate
translation units.


Mine, given the way it is implemented, doesn't like name clashes
happening anywhere within a single program image.

For related reasons, the compiler also generally requires that function
prototypes match their corresponding function declarations, ...


Things like "static" work, however, by internally renaming the variables
to essentially "TUID::gblName" (though, it actually uses '/' internally
rather than '::'), where TUID is effectively a hash-value used to
identify the the translation unit (each function implicitly sees
declarations within the "namespace" of its original translation unit).


As noted before, some of this could be addressed (to make it behave more
like a traditional C compiler), but would require a non-trivial amount
of work, for supporting things which are arguably broken anyways.



>> 2: Example:
>>    int foo1;
>>    ...
>>    int foo2;
>>    ...
>>    int foo3 = (&foo2)-(&foo1);  //mine can't currently do this.
>>      //workaround: copy this into an Init() function or similar.
>
> if foo3 is static then gcc can't do this either. If not, then this is a
> normal declaration plus initialisation. It should be no different from:
>
>    int foo3; foo3 = (&foo2)-(&foo1);
>
> Even if &foo1 and &foo2 are static, their difference can't be reduced to
> an integer constant.
>

Because it can't be reduced to a constant during compile time, my
compiler can't currently do it.

It is possible to initialize variables with the address of another
variable, but not using expressions which operate on them.

Granted, this could be handled by passing it off to a sort of "static
initialization" mechanism (ex: code executed to initialize things prior
to calling into "main()"), but my compiler doesn't currently support
this sort of thing (this is apparently a feature in C++ though, but not
part of C proper AFAIK).


I had started some early development of trying to support a small subset
of C++, but probably wont go that far. The language is a bit too
big/hairy for me to realistically support much more than a fairly small
subset.


>> Probably, compiling other stuff would shake up more bugs.
>
> I did maintain a list of issues with my compiler. Then I realised that
> producing a C compiler that supported every feature, and that would
> compile anything thrown at it, and that came with a complete set of
> headers that people expect, would take up the rest of my life. And
> that's just for one target.
>
> Tt would also be a poor product because it would need to support every
> abused, misused and deprecated feature.
>
> That's what happens with implementing a language with a considerable
> amount of baggage, a huge body of source code and myriad existing
> implementations all slightly different.
>
> So my little version of it is just a curiosity.
>

OK.


Mine is basically intended as "compiler for my specific ISA or for
sub-projects".

Its origins were about a decade ago, but it was re-purposed and somewhat
rewritten at various points over the years.

I have debated whether or not to try to make it modular, probably with
back-ends implemented via a plug-in mechanism (similar to how
audio/video codecs work in Windows).


Not meant to try to compete with GCC or Clang or similar.

Personally, I have doubts as to why some huge/massive compiler toolchain
which targets everything is even really seen as desirable or necessary.

So, there is GCC, which is a big mess, MLoc of code, which extending to
support a new target involves dealing with stuff all over the place.


I don't really understand all the hype for Clang, given IME:
It is still a lot of code;
It takes an absurd amount of time and RAM to rebuild;
Build times over 1 hour aren't really acceptable IMO.
It takes a fairly long time to compile anything with it.
GCC is around twice as fast.
MSVC is often around 10x to 12x faster IME.


Some people claim MSVC is really slow vs GCC, but this has not been my
experience with it.

OTOH, in the specific case of "windows.h", it does seem to produce an
order of magnitude more preprocessor output than the MinGW headers,
which could be a factor here.

jacobnavia

unread,
Sep 1, 2018, 4:05:08 AM9/1/18
to
Le 29/08/2018 à 13:11, Bart a écrit :
> First, the amount of antagonism I received in this group towards me, my
> views of the language and its associated tools, my alleged poor
> knowledge of the subject, and the quality of my generated sources, put
> me off having anything to do with C or making any of my C code public.

The reasons for that antagonism are vey clear:

1) You are not GNU
2) You are not gcc
3) You did not learn the c standard by heart
4) You did something useful. That is a NO NO in this group.
5) You do not respect this group hierarchy (thompson et al)


Happily (for you) you do not propose any changes/improvements to the
language, so you can ignore this group. That wasn't my case, and I
endured thompson/heathfield for several years before giving up. They
destroyed my project after succesfully bullying me out of it.

jacob

Ian Collins

unread,
Sep 1, 2018, 6:20:02 AM9/1/18
to
On 01/09/18 20:04, jacobnavia wrote:
>
> Happily (for you) you do not propose any changes/improvements to the
> language, so you can ignore this group. That wasn't my case, and I
> endured thompson/heathfield for several years before giving up. They
> destroyed my project after succesfully bullying me out of it.

Utter bollocks, no one on Usenet can bully anyone out of anything. If
they could, we wouldn't have all of the evangelical loonies posting here!

--
Ian.

Keith Thompson

unread,
Sep 1, 2018, 2:09:52 PM9/1/18
to
jacobnavia <ja...@jacob.remcomp.fr> writes:
[...]
> Happily (for you) you do not propose any changes/improvements to the
> language, so you can ignore this group. That wasn't my case, and I
> endured thompson/heathfield for several years before giving up. They
> destroyed my project after succesfully bullying me out of it.

Bullshit.

Kenny McCormack

unread,
Sep 1, 2018, 2:22:13 PM9/1/18
to
In article <lna7p1h...@kst-u.example.com>,
Keith Thompson <ks...@mib.org> wrote:
>jacobnavia <ja...@jacob.remcomp.fr> writes:
>[...]
>> Happily (for you) you do not propose any changes/improvements to the
>> language, so you can ignore this group. That wasn't my case, and I
>> endured thompson/heathfield for several years before giving up. They
>> destroyed my project after succesfully bullying me out of it.
>
>Bullshit.

thank you for confirming the truth of Jacob's statements.

--
The only thing Trump's made great again is Saturday Night Live.

Bart

unread,
Sep 1, 2018, 9:08:07 PM9/1/18
to
On 30/08/2018 13:07, Bart wrote:

> I did maintain a list of issues with my compiler. Then I realised that
> producing a C compiler that supported every feature, and that would
> compile anything thrown at it, and that came with a complete set of
> headers that people expect, would take up the rest of my life. And
> that's just for one target.

Here's an example of why I don't want to waste too much time on this.

My product until recently didn't properly check that some struct init
data, that using {...}, could be worked out at compile time (as it
doesn't support it otherwise).

Would that check in place, it now reports an error (now fixed with a
one-line mod in which I have low confidence) in line 16213 of SQL.C.
That block includes this interesting comment:

----------------------
** Ticket #3860: The llvm-gcc-4.2 compiler from Apple chokes on
** the ((void*)&((char*)0)[X]) construct. But MSVC chokes on ((void*)(X)).
** So we have to define the macros in different ways depending on the
** compiler.
*/
#if defined(__PTRDIFF_TYPE__) /* This case should work for GCC */
# define SQLITE_INT_TO_PTR(X) ((void*)(__PTRDIFF_TYPE__)(X))
# define SQLITE_PTR_TO_INT(X) ((int)(__PTRDIFF_TYPE__)(X))
#elif !defined(__GNUC__) /* Works for compilers other than LLVM */
# define SQLITE_INT_TO_PTR(X) ((void*)&((char*)0)[X])
# define SQLITE_PTR_TO_INT(X) ((int)(((char*)X)-(char*)0))
#elif defined(HAVE_STDINT_H) /* Use this case if we have ANSI headers */
# define SQLITE_INT_TO_PTR(X) ((void*)(intptr_t)(X))
# define SQLITE_PTR_TO_INT(X) ((int)(intptr_t)(X))
#else /* Generates a warning - but it always
works */
# define SQLITE_INT_TO_PTR(X) ((void*)(X))
# define SQLITE_PTR_TO_INT(X) ((int)(X))
#endif
----------------------

(I've no idea what the context is, or what X is supposed to be, as the
macro system makes tracing line numbers much harder.)

When you have two major compilers 'choking' on variations of the same
fragment of code likes this then you know there is something not right.


--
bart

BGB

unread,
Sep 2, 2018, 6:01:07 AM9/2/18
to
On 9/1/2018 3:04 AM, jacobnavia wrote:
> Le 29/08/2018 à 13:11, Bart a écrit :
>> First, the amount of antagonism I received in this group towards me,
>> my views of the language and its associated tools, my alleged poor
>> knowledge of the subject, and the quality of my generated sources, put
>> me off having anything to do with C or making any of my C code public.
>
> The reasons for that antagonism are vey clear:
>
> 1) You are not GNU
> 2) You are not gcc
> 3) You did not learn the c standard by heart
> 4) You did something useful. That is a NO NO in this group.
> 5) You do not respect this group hierarchy (thompson et al)
>

1/2:
Though not really as much on Usenet, recently there has been a lot of
hype over Clang (to the point of them getting borderline antagonistic to
people who work on compilers other than Clang).

ATM though, Clang is one of the few compilers I like less than GCC,
because ugly as GCC is, at least it can be rebuilt from sources within a
reasonable period of time (and at least they haven't come to quite the
same level of "use standards lawyering to justify compiling code in a
way which breaks existing software for maybe a tiny bit more speed on
some popular benchmarks").

Decided to mostly leave out my thoughts about GCC vs Clang vs MSVC for
compiling code on PCs.

Each has their use-cases I guess.



My own compiler (BGBCC) still exists partly as I wasn't really able to
find anything clearly better for my uses (for oddball custom targets).

But, doesn't itself offer much to make a compelling use-case for using
it on x86.

Generally, when building stuff natively on Windows, I use MSVC, mostly
for the benefit of the VS debugger.

In terms of language dialect, mine is most like MSVC, but with the main
exception of using LP64 on 64-bit targets rather than LLP64.

It also generally uses MSVC style command-line arguments, but can
generally accept either MSVC or GCC style arguments.

It doesn't support object files, so attempting to use it to compile to
an object file will produce RIL3 output (which may be used in a similar
manner). Note that ar is not used, nor is the format supported by the
compiler. While the RIL3 format is not itself target-specific, generally
the emitted RIL3 bytecode is target specific.

Superficially, RIL3 is an abstract-typed stack-machine IR, in a similar
vain to MS's .NET / CIL format, though the way it represents metadata is
rather different (metadata is structured as trees of attributes rather
than as a collection of flat interconnected tables).


3/4:
My case, I think I know C well enough.

Sometimes I come across oddball stuff, and wonder how to best support
it. Like, a while ago coming across constructs like this:
float *(arr[3][4])[9];
This one was sort of a 'fun' hack to the typesystem, and stood out as
previously I hadn't been aware that "such a thing was actually a thing".


I have doubts as to whether any of my stuff is all that useful though.

IRL, also people don't seem to believe I know anything about anything,
and attempts to "actually get a job" have ended up as futility.


>
> Happily (for you) you do not propose any changes/improvements to the
> language, so you can ignore this group. That wasn't my case, and I
> endured thompson/heathfield for several years before giving up. They
> destroyed my project after succesfully bullying me out of it.
>

Not really sure what was going on in this case.


In a basic sense, C seems most useful doing "generic C things", where
one can move code readily from one compiler to another. If all the
compilers involved "more or less" support C99, and the added features
avoid interfering with the compiler working effectively to do its job
and avoid creating portability issues, then things are good.

Ideally, what extensions exist can be done in ways where it is possible
to "fall back" if being built on another compiler, or without the
implied hardware features existing within the instruction set.

In this sense, a set of compiler or hardware features may be exposed in
a form resembling an API, just one where many of the functions may "just
so happen" to compile down to specialized instruction sequences.

Likewise for extension keywords, which are ideally handled in ways which
minimize unnecessary compatibility issues (ex: can be safely macroed
away without breaking the code)


Through, granted, this doesn't really cover typical "syntax sugar" style
language extensions, nor features which have a fundamental effect on the
typesystem (ex: extended numeric types).



I have a few other languages as well in my case:
A sort of very incomplete C++ implementation for my C compiler;
My "BGBScript2" (BS2) language (could use a different name);
...


But, C is as C is.

A partial lazy C++ implementation is, at best, a lazy C++
implementation. I thus far don't really claim "any" C++ support, as
almost invariably I would be faced with people raging about how it
doesn't support templates or similar (or how very few features "actually
work").

Almost need a catchy name for an "almost but not quite" C++. There is
EC++, though the feature-set isn't quite a match (ex: mine has namespaces).


BS2 could probably use a different name, as the language has relatively
little in common semantically with most other "Script" languages, but
this is partly a historical artifact.

BS2 isn't supported yet by my C compiler due to inertia (I have another
VM based implementation though where it is used mostly as an "extension
language" for a program mostly written in C). I am not all that sure it
will offer much advantage to a bare-metal use-case over what I can
already do in C (or could possibly be compared with an "extremely
watered down C++" in this case). In a language design / aesthetic sense,
it is along similar lines to Java and C#, but lacks garbage collection,
and makes object lifetime more explicit as per the semantics (and as
part of the language syntax). So, unlike C# or Java, memory objects
don't just "float off into space and disappear", but rather either need
an explicit static lifetime, or to be created/destroyed manually via
new/delete, or dynamically via constructor/destructor logic (similar to
RAII).


There would be a bit of overlap on the ABI side between BS2 and C++,
though semantics and basic language features differ some between the
languages, so still only a fairly limited set of features could be
shared directly across the language barrier (ex: one language uses
single inheritance + interfaces, whereas the other uses multiple
inheritance, ...). ( On the C++ side, the interfaces could be expressed
as abstract classes. )


There are also some uncertain areas, for example, BS2 arrays are
normally bounds-checked but C-style arrays (and raw pointers) are not,
which creates an awkward edge case (the bounds checks being arguably an
unreasonable use of clock-cycles, and are not terribly easy to optimize
away in many cases). Though, the language still allows C-style pointers,
so it isn't like "all hope is lost" (and since I control the CPU ISA in
this case, adding special instructions for this isn't entirely out of
the question).


The way some parts of the compiler and ABI were being designed as to
support both C++ and BS2, since granted there is still some amount of
overlap in terms of "fundamentals" (ex: both will have objects and name
mangling, ...). Similarly, some C extensions exist partly as a
side-effect of features intended to be able to support BS2.


Also unclear is the tradeoff between:
Put more priority into lazy C++ support;
Put more priority into adding BS2 to this compiler;
Put effort into trying to add both;
Not bother, C is enough.


In any case, I would probably expect C to remain as the dominant
language in this case (C being the language I use the most in-general).


Or such...

Ben Bacarisse

unread,
Sep 2, 2018, 6:23:33 AM9/2/18
to
BGB <cr8...@hotmail.com> writes:
<snip>
> My case, I think I know C well enough.
>
> Sometimes I come across oddball stuff, and wonder how to best support
> it. Like, a while ago coming across constructs like this:
> float *(arr[3][4])[9];
> This one was sort of a 'fun' hack to the typesystem, and stood out as
> previously I hadn't been aware that "such a thing was actually a
> thing".

Is that really the type in question? The ()s are in the "natural" place
so it's just

float *arr[3][4][9];

whereas

float (*arr[3][4])[9];

is a 2D array of pointers to arrays of 9 floats. Pointers whose target
type are arrays are relatively rare "in the wild" and often come as a
surprise even to very experienced C programmers.

<snip>
--
Ben.

Rick C. Hodgin

unread,
Sep 2, 2018, 6:28:59 AM9/2/18
to
On Sunday, September 2, 2018 at 6:01:07 AM UTC-4, BGB wrote:
> I have doubts as to whether any of my stuff is all that useful though.
>
> IRL, also people don't seem to believe I know anything about anything,
> and attempts to "actually get a job" have ended up as futility.

If I ever find funding for my projects, you would be one of
the first people I would offer a job to. You have natural
ability and great talent.

I see potential overflowing in you.

--
Rick C. Hodgin

Bart

unread,
Sep 2, 2018, 7:13:57 AM9/2/18
to
On 02/09/2018 11:02, BGB wrote:

> My case, I think I know C well enough.
>
> Sometimes I come across oddball stuff, and wonder how to best support
> it. Like, a while ago coming across constructs like this:
>   float *(arr[3][4])[9];
> This one was sort of a 'fun' hack to the typesystem, and stood out as
> previously I hadn't been aware that "such a thing was actually a thing".

It is amazing how often that happens with C. Like, for example,
typedefing actual functions, not just pointers to functions:

#include <stdio.h>

typedef int F(int a,int b);

So that you can declare a bunch of functions sharing the same signature:

F add, sub, mul, div;

Or even define such a function:

F add {return a+b;}

But the funny thing is, most C compilers will reject this; even /they/
are saying "WTF?", and they are the experts.

Even funnier is that only my compiler (with Tiny C) will actually
compile and run such a program:

#include <stdio.h>

typedef int F(int a,int b);

F add, sub, mul, div;

F add {return a+b;}

int main(void) {
printf("%d\n",add(3,4));
}

Anyway, once you've got over that surprise, there are plenty more:

typedef int F(int a,int b), i32, int32;

Yes, after you've typedefed a function signature, you can use the same
base type - the 'int' which is the result of that function - to define
more aliases for it.

Or maybe even a different function signature, with the same result type?
Apparently so:

typedef int F(int a,int b), i32, G(double x);

I'd better stop there; this is too much fun. Although I was testing
something with function pointers yesterday, and I think I ended up with
code like this:

int (*fn1)(void);
int (*fn2(void));

Subtly different placement of parentheses, but one is a pointer to a
function, the other is a function returning a pointer a function. Yet,
they can called in the same way:

(fn1)();
(fn2)();

or:

(*fn1)();
(*fn2)();

or, with most compilers, you can go mad with the the derefs:

(**************fn1)();
(**************fn2)();

(Mine however will squawk at more than one extra *.)

All these years and it's still fascinating how crazy this language can
be. I had to use CDECL to figure out which declaration was which.

--
bart

Ben Bacarisse

unread,
Sep 2, 2018, 10:09:53 AM9/2/18
to
Bart <b...@freeuk.com> writes:

> On 02/09/2018 11:02, BGB wrote:
>
>> My case, I think I know C well enough.
>>
>> Sometimes I come across oddball stuff, and wonder how to best support
>> it. Like, a while ago coming across constructs like this:
>>   float *(arr[3][4])[9];
>> This one was sort of a 'fun' hack to the typesystem, and stood out as
>> previously I hadn't been aware that "such a thing was actually a
>> thing".
>
> It is amazing how often that happens with C. Like, for example,
> typedefing actual functions, not just pointers to functions:
>
> #include <stdio.h>
>
> typedef int F(int a,int b);

You sound surprised. Would you not be more surprised if a mechanism to
define types did not let you define function types?

> So that you can declare a bunch of functions sharing the same signature:
>
> F add, sub, mul, div;
>
> Or even define such a function:
>
> F add {return a+b;}

No, that's a constraint violation. The syntax for a function definition
is constrained to include ()s in the declarator by wording in the
standard.

And then, if you use a function-like declarator, the F in that position
denotes the return type.

> But the funny thing is, most C compilers will reject this; even /they/
> are saying "WTF?", and they are the experts.

What's funny about that? Compilers may reject code that violates a
constraint.

> Even funnier is that only my compiler (with Tiny C) will actually
> compile and run such a program:
>
> #include <stdio.h>
>
> typedef int F(int a,int b);
>
> F add, sub, mul, div;
>
> F add {return a+b;}
>
> int main(void) {
> printf("%d\n",add(3,4));
> }

I don't know to what extent tcc tries to be a conforming C compiler, but
if it does intend to conform to the C standard, it should have some
flags that cause it to issue a warning about this. I could not make it
do that.

I think it's neat, rather than funny, that tcc has implemented this
extension, though it should be able to be told to warn about it. But
then I think users of tcc know it's a bit rough and ready.

I'm not sure I like the syntax though. It distances the definitions of
the parameters from their use rather too much for my taste. I would
prefer a syntax that names the parameters locally, though the types need
not be repeated. I suppose

(F add)(a, b) { return a + b; }

would convey the general idea.

--
Ben.

Bart

unread,
Sep 2, 2018, 11:28:10 AM9/2/18
to
On 02/09/2018 15:09, Ben Bacarisse wrote:
> Bart <b...@freeuk.com> writes:

>> It is amazing how often that happens with C. Like, for example,
>> typedefing actual functions, not just pointers to functions:
>>
>> #include <stdio.h>
>>
>> typedef int F(int a,int b);
>
> You sound surprised. Would you not be more surprised if a mechanism to
> define types did not let you define function types?

I don't think of a function as a type. Probably many people don't.

It only gets to be a type when it's the target of a pointer, and even
then it's not a proper type. You can't dereference it, or take its size,
or compare one function to another.

>> So that you can declare a bunch of functions sharing the same signature:
>>
>> F add, sub, mul, div;
>>
>> Or even define such a function:
>>
>> F add {return a+b;}
>
> No, that's a constraint violation. The syntax for a function definition
> is constrained to include ()s in the declarator by wording in the
> standard.

It sounds more like compilers never expected people to write code like
this, and so didn't accept it. So the standard was worded to support
that behaviour. But the actual grammar seems to allow it.

> And then, if you use a function-like declarator, the F in that position
> denotes the return type.

Well, creating a typedef T allows you to use it like this:

T a, b, c;

where T can contain any mix of array and pointer modifies; they don't
have to surround each name.

Using F would be the same - you don't need a parameter list following
the name - except the grammar doesn't allow a list of functions, only one.

>> But the funny thing is, most C compilers will reject this; even /they/
>> are saying "WTF?", and they are the experts.
>
> What's funny about that? Compilers may reject code that violates a
> constraint.

A constraint that they go of their way to detect? That is not typical of
C compilers! Perhaps their implementation simply didn't allow for that
possibility, and the constraint is caught that way, as a syntax error.

That this is invariably an error, and not a warning, makes that a
likelihood. Otherwise just following the grammar, as I do, would pass
that as legal syntax.

There is no reason that I can see why this can't be allowed (other that
it will confuse people reading it). You yourself seemed to think it was
a good idea.

What's funny is that gcc with all its extensions doesn't support but,
but I do, without even trying. It would be more work to detect it I think.

> I think it's neat, rather than funny, that tcc has implemented this
> extension, though it should be able to be told to warn about it. But
> then I think users of tcc know it's a bit rough and ready.

Tiny C is designed to be small. I doubt they would deliberately
implement an extension that no one knows about and no one uses, not if
it takes up code. I suggest it works by accident, like mine.

--
bart

BGB

unread,
Sep 2, 2018, 12:30:06 PM9/2/18
to
Yeah, I meant the latter (ex: pointer to array).
But, it has been a while since I encountered it / dealt with it.

luser droog

unread,
Sep 2, 2018, 1:19:16 PM9/2/18
to
On Sunday, September 2, 2018 at 10:28:10 AM UTC-5, Bart wrote:
> On 02/09/2018 15:09, Ben Bacarisse wrote:
> > Bart <b...@freeuk.com> writes:
>
> >> It is amazing how often that happens with C. Like, for example,
> >> typedefing actual functions, not just pointers to functions:
> >>
> >> #include <stdio.h>
> >>
> >> typedef int F(int a,int b);
> >
> > You sound surprised. Would you not be more surprised if a mechanism to
> > define types did not let you define function types?
>
> I don't think of a function as a type. Probably many people don't.

As usual, you're missing the implicit "modus tollens".

X_pov -> incorrect_result

Therefore X_pov is /wrong/. Continuing to explain/extoll X_pov has no value.

Tim Rentsch

unread,
Sep 2, 2018, 1:20:57 PM9/2/18
to
Ben Bacarisse <ben.u...@bsb.me.uk> writes:

> [ ... considering an extension where
>
> typedef int F(int a,int b);
> F add {return a+b;}
>
> is allowed... ]
>
> I'm not sure I like the syntax though. It distances the definitions of
> the parameters from their use rather too much for my taste. I would
> prefer a syntax that names the parameters locally, though the types need
> not be repeated. I suppose
>
> (F add)(a, b) { return a + b; }
>
> would convey the general idea.

A counter-suggestion, made without much thought behind it:

auto F add(a,b){ return a+b; }

or perhaps even

static F add;

...

auto add(a,b){ return a+b; }

I am not necessarily in favor of this sort of thing. There are
plusses and minusses associated with having the types not be
available at the point of definition, but only elsewhere. But if
it is decided that something along these lines should be allowed,
my first reaction is to like the 'auto' method (or something like
it) more than the (F add) method.

Ben Bacarisse

unread,
Sep 2, 2018, 1:40:40 PM9/2/18
to
Tim Rentsch <t...@alumni.caltech.edu> writes:

> Ben Bacarisse <ben.u...@bsb.me.uk> writes:
>
>> [ ... considering an extension where
>>
>> typedef int F(int a,int b);
>> F add {return a+b;}
>>
>> is allowed... ]
>>
>> I'm not sure I like the syntax though. It distances the definitions of
>> the parameters from their use rather too much for my taste. I would
>> prefer a syntax that names the parameters locally, though the types need
>> not be repeated. I suppose
>>
>> (F add)(a, b) { return a + b; }
>>
>> would convey the general idea.
>
> A counter-suggestion, made without much thought behind it:

Unlikely to have been with less thought than my suggestion!

> auto F add(a,b){ return a+b; }
>
> or perhaps even
>
> static F add;
>
> ...
>
> auto add(a,b){ return a+b; }
>
> I am not necessarily in favor of this sort of thing. There are
> plusses and minusses associated with having the types not be
> available at the point of definition, but only elsewhere. But if
> it is decided that something along these lines should be allowed,
> my first reaction is to like the 'auto' method (or something like
> it) more than the (F add) method.

The syntax already allows for a declaration list to follow the
declarator (a plain identifier in this case) so maybe

F add auto a, b; { ... }

would do.

--
Ben.

Richard Damon

unread,
Sep 2, 2018, 2:30:54 PM9/2/18
to
Looking at that example, it looks that that code is wanting to make the
very unportable assumption that an int can be used to store the value of
a pointer, and wanting to make the code work on ancient, and/or
non-conforming compilers (and even have extra casts so if the assumption
isn't true, the code will likely quietly compile and fail to work)

This code almost certainly will misbehave on systems with pointers
bigger than int, like most 64 bit systems (and some systems with 16 bit
ints, but 32 bit pointers). They seem to be ignoring that case.

The standard solution, assuming at least C99 would be something close to
the HAVE_STDINT_H case, using intptr_t, but you need to store the int
side of the conversion in an intptr_t variable, not an int.

Ike Naar

unread,
Sep 2, 2018, 2:55:21 PM9/2/18
to
On 2018-09-02, Bart <b...@freeuk.com> wrote:
> Even funnier is that only my compiler (with Tiny C) will actually
> compile and run such a program:
>
> #include <stdio.h>
>
> typedef int F(int a,int b);

The parameter names 'a' and 'b' are optional so

typedef int F(int a, int b)

is the same as

typedef int F(int foo, int bar)

or

typedef int F(int,int)

> [...]
> F add {return a+b;}

so in this context 'a' and 'b' are unknown.

> Anyway, once you've got over that surprise, there are plenty more:
>
> typedef int F(int a,int b), i32, int32;
>
> Yes, after you've typedefed a function signature, you can use the same
> base type - the 'int' which is the result of that function - to define
> more aliases for it.
>
> Or maybe even a different function signature, with the same result type?
> Apparently so:
>
> typedef int F(int a,int b), i32, G(double x);
>
> I'd better stop there; this is too much fun. Although I was testing
> something with function pointers yesterday, and I think I ended up with
> code like this:
>
> int (*fn1)(void);
> int (*fn2(void));
>
> Subtly different placement of parentheses, but one is a pointer to a
> function, the other is a function returning a pointer a function. Yet,
> they can called in the same way:
>
> (fn1)();
> (fn2)();
>
> or:
>
> (*fn1)();
> (*fn2)();
>
> or, with most compilers, you can go mad with the the derefs:
>
> (**************fn1)();
> (**************fn2)();

Would your compiler allow me to write

int forty_two = 41;

?

BGB

unread,
Sep 2, 2018, 3:47:05 PM9/2/18
to
On 9/2/2018 6:13 AM, Bart wrote:
> On 02/09/2018 11:02, BGB wrote:
>
>> My case, I think I know C well enough.
>>
>> Sometimes I come across oddball stuff, and wonder how to best support
>> it. Like, a while ago coming across constructs like this:
>>    float *(arr[3][4])[9];
>> This one was sort of a 'fun' hack to the typesystem, and stood out as
>> previously I hadn't been aware that "such a thing was actually a thing".
>

Apparently I put the '*' in the wrong place on this one.
So goes for posts being written at 3AM.

This case involved a hack to have a type use another type as if it were
a structure (to allow the type to be expressed).


> It is amazing how often that happens with C. Like, for example,
> typedefing actual functions, not just pointers to functions:
>
>     #include <stdio.h>
>
>      typedef int F(int a,int b);
>

Yep.


> So that you can declare a bunch of functions sharing the same signature:
>
>     F add, sub, mul, div;
>
> Or even define such a function:
>
>     F add {return a+b;}
>
> But the funny thing is, most C compilers will reject this; even /they/
> are saying "WTF?", and they are the experts.
>

Mine does not allow this.

Until recently, it didn't allow K&R declarations either, but I ended up
adding them as there was a non-zero amount of code I was encountering
using this.


> Even funnier is that only my compiler (with Tiny C) will actually
> compile and run such a program:
>
>     #include <stdio.h>
>
>     typedef int F(int a,int b);
>
>     F add, sub, mul, div;
>
>     F add {return a+b;}
>
>     int main(void) {
>         printf("%d\n",add(3,4));
>     }
>

OK.


> Anyway, once you've got over that surprise, there are plenty more:
>
>     typedef int F(int a,int b), i32, int32;
>
> Yes, after you've typedefed a function signature, you can use the same
> base type - the 'int' which is the result of that function - to define
> more aliases for it.
>
> Or maybe even a different function signature, with the same result type?
> Apparently so:
>
>     typedef int F(int a,int b), i32, G(double x);
>
> I'd better stop there; this is too much fun.

Yeah, basically works in mine, typedef is parsed basically the same as a
variable declaration, except that each variable goes into a "literal
table" as a type rather than a variable. When handling variables, the
blob of variables essentially decomposes into individual variable
declarations internally.


There is a separate globals table for everything else (holds all the
global variables and functions).

In my BS2 compiler and VM (which had its origins later), there was only
a single giant "constant pool", rather than having separate "literal"
and "global" tables.


Other random stuff:
Types, in simple cases, are essentially bit-twiddled into a 32-bit
integer. Types which don't fit into this, overflow into an "type
overflow" structure (the type integer then encodes an index into this
table).

There are then several subcases, IIRC:
A: 1D arrays max 4095 entries, 0-7 '*', Types 0-4095.
Generic case.
B: 1D arrays max 1M entires, 0/1 '*', Types 0..63
Larger arrays of primitive types.
C: 16 array max 63 entries, 0/1 '*', Types 0..1M
... Someone just included "windows.h" or similar ...

Generally, type numbers 0..255 are reserved for primitive types, with
types over 256 encoding an index into the literal table. The entry in
this table then encodes what it is.

A: A 4 bit field is used for pointers, but may do other stuff:
0: plain value.
1-7: 'T*' to 'T*******'.
8: 'T&' (N/A for C)
9-11: "T[]" to "T[][][]" (N/A for C or C++)
12-15: "T*[]" .. "T*[][][]" (N/A for C or C++)

Cases B/C use a 2-bit field:
0: plain value.
1: "T*"
2: "T&"
3: "T[]"


Often, types are generally serialized into an ASCII string format, ex:
"i": int x;
"l": long x;
"x": long long x;
"n": __int128 x;
"f": float x;
...
"Pi": int *x;
"A256;Pi": int *x[256];
"(dd)i": int x(double, double);
"PXfoo_s;": foo *x; //depends on usage
"PX269": foo *x; //typical if used internally.

This format is fairly common between my compilers/VMs, but with
variations depending on the typesystem specifics, ex:
"Ri": int &x; //type reference (C++, BS2).
"LFoo;": Foo x; //ref-type class (BS2)
"QQi": int[][] x; //ref-type array (BS2)
"r": var x; //tagged variant type (BS2, C Ext)
"Cs": string s; //string (BS2)
...

"LFoo;" is conceptually similar to "PXFoo;" or "RXFoo;", but represent
different semantics (with an otherwise equivalent in-memory
representation for the subset of classes where "LFoo;" is valid). They
have no direct equivalent in C mode (and would generally be handled by
casting them to something resembling a COM object and/or accessing them
via an API).

The variant type currently exists as an extension ("__variant") in C and
C++ modes, but isn't really intended for general use in these languages
(also doing pretty much anything with this type results in runtime calls
being generated). In terms of code-generation, it is basically otherwise
handled as equivalent to "long long".

OTOH, "Pi" and "Qi" would be quite different, in that "Pi" is a raw
pointer, whereas (in this implementation at least), "Qi" would
essentially be a "fat pointer" (lots of pros/cons here). There isn't
currently a syntax to express these in C mode (but doing so could be
useful for interop).

Strings are a more complex matter, decided to leave out going into the
specifics of C vs BS2 string representation and behavior. In a
fundamental sense, they have in common that they both use a raw pointer
to the start of the string's characters, but differ notably in regards
to semantics (ex: a BS2 "string" type isn't otherwise all that much
similar to a "char*" in C).

...


> Although I was testing
> something with function pointers yesterday, and I think I ended up with
> code like this:
>
>     int (*fn1)(void);
>     int (*fn2(void));
>
> Subtly different placement of parentheses, but one is a pointer to a
> function, the other is a function returning a pointer a function. Yet,
> they can called in the same way:
>
>     (fn1)();
>     (fn2)();
>
> or:
>
>     (*fn1)();
>     (*fn2)();
>

Or, you know:
fn1();


> or, with most compilers, you can go mad with the the derefs:
>
>     (**************fn1)();
>     (**************fn2)();
>
> (Mine however will squawk at more than one extra *.)
>

OK.


> All these years and it's still fascinating how crazy this language can
> be. I had to use CDECL to figure out which declaration was which.
>

The C style declarations aren't too bad for the most part.

My BS2 language constrains things somewhat here (only directly parses
"simple cases"), meaning that to get overly fancy with type declarations
it is necessary to use typedef's.

For example, function returning a function pointer would require using a
typedef.

Bart

unread,
Sep 2, 2018, 3:52:21 PM9/2/18
to
On 02/09/2018 19:55, Ike Naar wrote:
> On 2018-09-02, Bart <b...@freeuk.com> wrote:
>> Even funnier is that only my compiler (with Tiny C) will actually
>> compile and run such a program:
>>
>> #include <stdio.h>
>>
>> typedef int F(int a,int b);
>
> The parameter names 'a' and 'b' are optional so
>
> typedef int F(int a, int b)
>
> is the same as
>
> typedef int F(int foo, int bar)
>
> or
>
> typedef int F(int,int)
>
>> [...]
>> F add {return a+b;}
>
> so in this context 'a' and 'b' are unknown.


Yes, that's why they have to be part of the typedef. Which also ensures
consistently named parameters across all functions using F.

(I'm not advocating using this, but if you do use, this is how it can work.)

>> or, with most compilers, you can go mad with the the derefs:
>>
>> (**************fn1)();
>> (**************fn2)();
>
> Would your compiler allow me to write
>
> int forty_two = 41;
>
> ?

Yes. But you have to access it as:

forty_two;

not:

******forty_two;

Apart from that, I don't see your point, other than that people can make
mistakes in programs. Above, the mistake is in the language in being
unable to detect a mistake in the program, namely too many dereferences
for the function pointer, and too many plus one for the function.

--
bart

Ike Naar

unread,
Sep 2, 2018, 3:59:49 PM9/2/18
to
On 2018-09-02, BGB <cr8...@hotmail.com> wrote:
> On 9/2/2018 6:13 AM, Bart wrote:
>> On 02/09/2018 11:02, BGB wrote:
>>
>>> My case, I think I know C well enough.
>>>
>>> Sometimes I come across oddball stuff, and wonder how to best support
>>> it. Like, a while ago coming across constructs like this:
>>> ?? float *(arr[3][4])[9];
>>> This one was sort of a 'fun' hack to the typesystem, and stood out as
>>> previously I hadn't been aware that "such a thing was actually a thing".
>
> Apparently I put the '*' in the wrong place on this one.
> So goes for posts being written at 3AM.

The 'Or such...' at the end of that post already revealed it wasn't meant
to be taken seriously.

Keith Thompson

unread,
Sep 2, 2018, 4:18:24 PM9/2/18
to
Bart <b...@freeuk.com> writes:
> On 02/09/2018 15:09, Ben Bacarisse wrote:
>> Bart <b...@freeuk.com> writes:
>
>>> It is amazing how often that happens with C. Like, for example,
>>> typedefing actual functions, not just pointers to functions:
>>>
>>> #include <stdio.h>
>>>
>>> typedef int F(int a,int b);
>>
>> You sound surprised. Would you not be more surprised if a mechanism to
>> define types did not let you define function types?
>
> I don't think of a function as a type. Probably many people don't.

A function is not a type. A function type is a type. Every function
has a type, and that type is a function type. Function types are
described in N1570 6.5.2p20, 4th bullet.

It's likely that many people don't think of function has having types.
They're wrong. You could *help* people understand C rather than
reinforcing their lack of knowledge. Or at least get out of the way.

> It only gets to be a type when it's the target of a pointer, and even
> then it's not a proper type.

Completely incorrect. A function type is a type regardless of the
context in which it appears.

> You can't dereference it, or take its size,
> or compare one function to another.

The standard doesn't define the term "proper type". Apparently you have
a meaning in mind, according to which a function type is not a "proper
type". I suggest you look up the well defined term "object type" in the
standard; you might find it more suitable for what you're talking about.
(FYI, the precise meaning of "object type" changed slightly in C11; it
now includes the void type. Some descriptions turned out to be simpler
that way.)

[...]

>> What's funny about that? Compilers may reject code that violates a
>> constraint.
>
> A constraint that they go of their way to detect? That is not typical of
> C compilers! Perhaps their implementation simply didn't allow for that
> possibility, and the constraint is caught that way, as a syntax error.

Every conforming C compiler detects all constraint violations.

[...]

Ike Naar

unread,
Sep 2, 2018, 4:47:34 PM9/2/18
to
On 2018-09-02, Bart <b...@freeuk.com> wrote:
> On 02/09/2018 19:55, Ike Naar wrote:
>> Would your compiler allow me to write
>>
>> int forty_two = 41;
>>
>> ?
>
> Yes. But you have to access it as:
>
> forty_two;
>
> not:
>
> ******forty_two;

Since forty_two is an int, not a pointer, any positive number of
dereference operators must be a mistake, and a compiler would have
to reject this.

> Apart from that, I don't see your point, other than that people can make
> mistakes in programs. Above, the mistake is in the language in being
> unable to detect a mistake in the program, namely too many dereferences
> for the function pointer, and too many plus one for the function.

My point was that it's infeasable for a compiler to catch all silly things
a programmer can write.

If f is a pointer-to-function, then

********f

is a bit silly, but legal, and a compiler must accept it.
Just like, for int i,

i+0+0+0+0+0+0+0+0

is silly, but legal.
Would you want a compiler to reject i+0+0+0+0+0+0+0+0 ?

Ben Bacarisse

unread,
Sep 2, 2018, 4:50:49 PM9/2/18
to
Bart <b...@freeuk.com> writes:

> On 02/09/2018 19:55, Ike Naar wrote:
>> On 2018-09-02, Bart <b...@freeuk.com> wrote:

>>> or, with most compilers, you can go mad with the the derefs:
>>>
>>> (**************fn1)();
>>> (**************fn2)();
>>
>> Would your compiler allow me to write
>>
>> int forty_two = 41;
>>
>> ?
>
> Yes. But you have to access it as:
>
> forty_two;
>
> not:
>
> ******forty_two;

Do you allow +++++++++++forty_two? That's another thing C allows you to
go crazy with.

--
Ben.

Bart

unread,
Sep 2, 2018, 4:57:53 PM9/2/18
to
On 02/09/2018 21:18, Keith Thompson wrote:
> Bart <b...@freeuk.com> writes:
>> On 02/09/2018 15:09, Ben Bacarisse wrote:
>>> Bart <b...@freeuk.com> writes:
>>
>>>> It is amazing how often that happens with C. Like, for example,
>>>> typedefing actual functions, not just pointers to functions:
>>>>
>>>> #include <stdio.h>
>>>>
>>>> typedef int F(int a,int b);
>>>
>>> You sound surprised. Would you not be more surprised if a mechanism to
>>> define types did not let you define function types?
>>
>> I don't think of a function as a type. Probably many people don't.
>
> A function is not a type. A function type is a type. Every function
> has a type, and that type is a function type. Function types are
> described in N1570 6.5.2p20, 4th bullet.
>
> It's likely that many people don't think of function has having types.
> They're wrong. You could *help* people understand C rather than
> reinforcing their lack of knowledge. Or at least get out of the way.

I disagree. You don't help beginners learn English by ramming split
infinitives and transverbial clauses down their throats. You keep things
colloquial and introduce everyday stuff that will be immediately useful.

Very many languages have a keyword to introduce functions, and and quite
a few new ones derived from C have added such a keyword (proc,
procedure, func, def, etc).

That suggests it is very useful to think of a function as a rather
special language construct. All the stuff at the top defines the
interface to a function. It's not that useful to think of that as a type.

Only C (and that ones closely derived from it) doesn't have such a
keyword. I think that was a mistake.


> The standard doesn't define the term "proper type". Apparently you have
> a meaning in mind, according to which a function type is not a "proper
> type". I suggest you look up the well defined term "object type" in the
> standard; you might find it more suitable for what you're talking about.
> (FYI, the precise meaning of "object type" changed slightly in C11; it
> now includes the void type. Some descriptions turned out to be simpler
> that way.)

The people who wrote that may or may not have done a good job of it.

But it is not a good informal reference for people who know how to
program, and and want to know how to express the code in C.

>> A constraint that they go of their way to detect? That is not typical of
>> C compilers! Perhaps their implementation simply didn't allow for that
>> possibility, and the constraint is caught that way, as a syntax error.
>
> Every conforming C compiler detects all constraint violations.

It's usually a hell of a job getting them to report them though.

Except for this one.

--
bart

Keith Thompson

unread,
Sep 2, 2018, 5:05:10 PM9/2/18
to
Bart <b...@freeuk.com> writes:
> On 02/09/2018 19:55, Ike Naar wrote:
>> On 2018-09-02, Bart <b...@freeuk.com> wrote:
>>> Even funnier is that only my compiler (with Tiny C) will actually
>>> compile and run such a program:
>>>
>>> #include <stdio.h>
>>>
>>> typedef int F(int a,int b);
>>
>> The parameter names 'a' and 'b' are optional so
>>
>> typedef int F(int a, int b)
>>
>> is the same as
>>
>> typedef int F(int foo, int bar)
>>
>> or
>>
>> typedef int F(int,int)
>>
>>> [...]
>>> F add {return a+b;}
>>
>> so in this context 'a' and 'b' are unknown.
>
>
> Yes, that's why they have to be part of the typedef. Which also ensures
> consistently named parameters across all functions using F.
>
> (I'm not advocating using this, but if you do use, this is how it can work.)

The parameter names in a function declaration (that's not part of a
function definition) are essentially ignored. This:

int func(int x);
int func(int y);
int func(int z) { return z; }

is perfectly legal, and does not require a diagnostic. Personally I
like to keep parameter names consistent, but the compiler won't enforce
that consistency. Similar examples using typedefs are equally legal.

I mention this because others might not be aware of it.

(blah blah, C sucks even worse than you thought it did, blah blah)

Richard Damon

unread,
Sep 2, 2018, 5:44:25 PM9/2/18
to
On 9/2/18 4:47 PM, Ike Naar wrote:
> If f is a pointer-to-function, then
>
> ********f
>
> is a bit silly, but legal, and a compiler must accept it.

Yes, the issue is that something of function type automatically decays
into a pointer to that function.

C actually doesn't have an operation to directly call a function, it
can only make a call through a pointer to function, it is just that
identifiers that name a function automatically decay into the needed
pointer.

Given: int (*f)(); /* f being a pointer to function */

the 'proper' syntax to call the function pointed to by f is just f(),
adding the * just creates a value of function type which needs to decay
into a pointer to function.

Yes, sometimes coding conventions say to put the * there so people don't
go looking for the definition of a function f(), but the language itself
prefers not to have the * there (in that the expression has a simpler
parse).

You can also put the * in front of a real function name, and the name
decays, you indirect on that pointer getting back the function type,
which decays again.

Bart

unread,
Sep 2, 2018, 6:24:01 PM9/2/18
to
Assuming you mean unary plus, that takes a numeric type T as input, and
delivers the same type T. So you can stack as many as you like.

That doesn't abuse the language's type system so it is not a problem;
just silly code, but code that is not misleading.

If the case of * for dereference, that is applied to a pointer type T*,
and delivers a type T as a result (with the usual rules applying when T
is an array type).

You can't apply * again unless T (or the result of the first *) is
another pointer type, such as U*.

So you have to stay strictly within the rules of the type system and
it's somewhat harder to write silly code like above.

Until you come to function pointers, where C allows some quirky
goings-on which means that each time you dereference a function pointer,
it yields the same function pointer. So you can apply as many extra *
derefs as you like.

This is where it can get misleading: given F() and (*G)(), which one is
the function, and which one the function pointer? Actually both can be
functions, both can be pointers, or one can be a pointer and the other a
function, or vice versa.

There is somewhere a logical explanation for what is going on and why
that is allowed, but that doesn't stop this from being some pretty weird
shit.

(And actually, I'm pleased that my C compiler doesn't go fully along
with this. Although it still has some of the F/G/*F/*G ambuiguity, it is
limited to at most one extra deref, and only for actual functions.

The reason is, if F is an actual function, and G a pointer to a
function, then:

F; // becomes a pointer to function F; you don't need &F
G; // /is/ a pointer to a function (we don't know which one)

Since they are both pointers, it is possible to dereference them, but
because the result would be a function, it is necessary to call the result:

(*F)();
(*G)();

These work because both are pointers. (Although *F is a constant
pointer, G a variable pointer. Like the difference, given 'int a, *b',
between &a and b.)

This however doesn't work:

(**F)();
(**G)();

which is where it diverges from conforming compilers. Because it's run
out of pointer levels.

The discrepancy between F and G above can only be removed (maintain the
difference in rank) if this is made illegal:

F;

you have to write:

&F;

But the former is used extensively in C code, so has to be allowed.
Alternatively, it is possible to think of F as a named constant pointer
to function, and G as a regular pointer.)

--
bart

Ben Bacarisse

unread,
Sep 2, 2018, 6:39:05 PM9/2/18
to
Bart <b...@freeuk.com> writes:

> On 02/09/2018 15:09, Ben Bacarisse wrote:
>> Bart <b...@freeuk.com> writes:
>
>>> It is amazing how often that happens with C. Like, for example,
>>> typedefing actual functions, not just pointers to functions:
>>>
>>> #include <stdio.h>
>>>
>>> typedef int F(int a,int b);
>>
>> You sound surprised. Would you not be more surprised if a mechanism to
>> define types did not let you define function types?
>
> I don't think of a function as a type. Probably many people don't.

I think you mean you don't consider function types to be types.
Functions obviously aren't types, just as integers aren't types.
Anyway, functions have a type -- a function type.

> It only gets to be a type when it's the target of a pointer, and even
> then it's not a proper type. You can't dereference it, or take its
> size, or compare one function to another.

One role of a type is to determine what can be done with instances of
that type, and instances of function types are rather limited in C (
much less so in some other languages) but they are still instances of a
function type.

Types (in C) also have a role in and of themselves. One type may or
may not be compatible with another and this applies to function types as
much as to any other. If functions did not have types, there would have
to be lots of special wording in the standard to cover cases that are
automatically covered now.

>>> So that you can declare a bunch of functions sharing the same signature:
>>>
>>> F add, sub, mul, div;
>>>
>>> Or even define such a function:
>>>
>>> F add {return a+b;}
>>
>> No, that's a constraint violation. The syntax for a function definition
>> is constrained to include ()s in the declarator by wording in the
>> standard.
>
> It sounds more like compilers never expected people to write code like
> this, and so didn't accept it. So the standard was worded to support
> that behaviour. But the actual grammar seems to allow it.

The grammar does allow it. The grammar allows all sorts of constructs
that are not valid C.

Your claim that the constraint was retrospective seems very unlikely to
me for reasons that I could expand on if anyone wants me to.

> What's funny is that gcc with all its extensions doesn't support but,
> but I do, without even trying. It would be more work to detect it I
> think.

But very valuable for a compiler that wants to be conforming.

I don't find it funny that gcc does not support this. There's no need
for a compiler to note the names used as parameters in a typdefed
function type, and since the gcc authors will have known that (a) C's
grammar explicitly used for forbid this form, and (b) it's now a
constraint, they probably decided not to bother noting them.

>> I think it's neat, rather than funny, that tcc has implemented this
>> extension, though it should be able to be told to warn about it. But
>> then I think users of tcc know it's a bit rough and ready.
>
> Tiny C is designed to be small. I doubt they would deliberately
> implement an extension that no one knows about and no one uses, not if
> it takes up code. I suggest it works by accident, like mine.

I think it's likely to be an accident too. If it were deliberate, I
suspect someone would have put in a diagnostic to make the compiler
conforming. It looks like it "just works" by attaching the prototype to
the declared name.

--
Ben.

Chris M. Thomasson

unread,
Sep 2, 2018, 6:44:34 PM9/2/18
to
On 9/2/2018 3:38 PM, Ben Bacarisse wrote:
> Bart <b...@freeuk.com> writes:
>
>> On 02/09/2018 15:09, Ben Bacarisse wrote:
>>> Bart <b...@freeuk.com> writes:
>>
>>>> It is amazing how often that happens with C. Like, for example,
>>>> typedefing actual functions, not just pointers to functions:
>>>>
>>>> #include <stdio.h>
>>>>
>>>> typedef int F(int a,int b);
>>>
>>> You sound surprised. Would you not be more surprised if a mechanism to
>>> define types did not let you define function types?
>>
>> I don't think of a function as a type. Probably many people don't.
>
> I think you mean you don't consider function types to be types.
> Functions obviously aren't types, just as integers aren't types.
> Anyway, functions have a type -- a function type.

Fwiw, I always thought of int as an integer data "type" with a range of
two or four bytes?

[...]

David Brown

unread,
Sep 2, 2018, 6:50:49 PM9/2/18
to
On 02/09/18 22:57, Bart wrote:
> On 02/09/2018 21:18, Keith Thompson wrote:
>> Bart <b...@freeuk.com> writes:
>>> On 02/09/2018 15:09, Ben Bacarisse wrote:
>>>> Bart <b...@freeuk.com> writes:
>>>
>>>>> It is amazing how often that happens with C. Like, for example,
>>>>> typedefing actual functions, not just pointers to functions:
>>>>>
>>>>>       #include <stdio.h>
>>>>>
>>>>>        typedef int F(int a,int b);
>>>>
>>>> You sound surprised.  Would you not be more surprised if a mechanism to
>>>> define types did not let you define function types?
>>>
>>> I don't think of a function as a type. Probably many people don't.
>>
>> A function is not a type.  A function type is a type.  Every function
>> has a type, and that type is a function type.  Function types are
>> described in N1570 6.5.2p20, 4th bullet.
>>
>> It's likely that many people don't think of function has having types.
>> They're wrong.  You could *help* people understand C rather than
>> reinforcing their lack of knowledge.  Or at least get out of the way.
>
> I disagree. You don't help beginners learn English by ramming split
> infinitives and transverbial clauses down their throats. You keep things
> colloquial and introduce everyday stuff that will be immediately useful.

You'll help them even less by telling the beginners that the verb "to
go" isn't really a proper verb, because it has a funny past tense that
no one can understand properly, and that English would be so much better
if it was spelt like German.

I don't think anyone will argue that function types are something that
beginners need to know much about - but they certainly don't need to be
told things that are wrong and confusing.

>
> Very many languages have a keyword to introduce functions, and and quite
> a few new ones derived from C have added such a keyword (proc,
> procedure, func, def, etc).
>
> That suggests it is very useful to think of a function as a rather
> special language construct. All the stuff at the top defines the
> interface to a function. It's not that useful to think of that as a type.

Functions are different to objects in C, and in other imperative
languages (in functional languages, they are objects as much as anything
else).

That does not mean that functions do not have types. In C, "Types are
partitioned into objects types (types that describe objects) and
function types (types that describe functions)". Function types and
object types share some characteristics, but are different in others.

>
> Only C (and that ones closely derived from it) doesn't have such a
> keyword. I think that was a mistake.
>

Perhaps, perhaps not. I agree it was a mistake to allow a single
declaration to include declarations of objects and functions of
different (but related) types. But even if C had a "func" or "def"
keyword, functions would still have types. Other languages usually have
function types.

>
>> The standard doesn't define the term "proper type".  Apparently you have
>> a meaning in mind, according to which a function type is not a "proper
>> type".  I suggest you look up the well defined term "object type" in the
>> standard; you might find it more suitable for what you're talking about.
>> (FYI, the precise meaning of "object type" changed slightly in C11; it
>> now includes the void type.  Some descriptions turned out to be simpler
>> that way.)
>
> The people who wrote that may or may not have done a good job of it.
>
> But it is not a good informal reference for people who know how to
> program, and and want to know how to express the code in C.

The standard was never intended to be an information reference - it was
intended to be a /formal/ reference. And it was certainly never
intended to be a tutorial - it says as much on the very first page!

>
>>> A constraint that they go of their way to detect? That is not typical of
>>> C compilers! Perhaps their implementation simply didn't allow for that
>>> possibility, and the constraint is caught that way, as a syntax error.
>>
>> Every conforming C compiler detects all constraint violations.
>
> It's usually a hell of a job getting them to report them though.
>

No, unless they allow a constraint violation as an extension, compilers
will give a diagnostic (error or warning) on any constraint violation.
(It is unfortunate that sometimes this will be a warning, rather than a
hard error.)

You are probably confusing "constraints" with restrictions given in the
"semantics" sections in the standard - which do not require a
diagnostic. I know I have confused these things.

> Except for this one.
>

Keith Thompson

unread,
Sep 2, 2018, 6:53:12 PM9/2/18
to
Richard Damon <Ric...@Damon-Family.org> writes:
[...]
> Given: int (*f)(); /* f being a pointer to function */
>
> the 'proper' syntax to call the function pointed to by f is just f(),
> adding the * just creates a value of function type which needs to decay
> into a pointer to function.
>
> Yes, sometimes coding conventions say to put the * there so people don't
> go looking for the definition of a function f(), but the language itself
> prefers not to have the * there (in that the expression has a simpler
> parse).

The language itself has no preference, and the implicit conversion(s)
are not part of parsing. It's true that (*fptr)() invokes more
operations in the abstract machine that fptr(). That might involve a
few extra cycles at compile time, but there's no reason to expect any
difference in the generated code (and you didn't say there was).

> You can also put the * in front of a real function name, and the name
> decays, you indirect on that pointer getting back the function type,
> which decays again.

Keith Thompson

unread,
Sep 2, 2018, 6:54:02 PM9/2/18
to
Ben Bacarisse <ben.u...@bsb.me.uk> writes:
[...]
> Do you allow +++++++++++forty_two? That's another thing C allows you to
> go crazy with.

Only if you insert spaces between the + symbols.

Keith Thompson

unread,
Sep 2, 2018, 6:56:52 PM9/2/18
to
Bart <b...@freeuk.com> writes:
> On 02/09/2018 21:18, Keith Thompson wrote:
>> Bart <b...@freeuk.com> writes:
[...]
>>> I don't think of a function as a type. Probably many people don't.
>>
>> A function is not a type. A function type is a type. Every function
>> has a type, and that type is a function type. Function types are
>> described in N1570 6.5.2p20, 4th bullet.
>>
>> It's likely that many people don't think of function has having types.
>> They're wrong. You could *help* people understand C rather than
>> reinforcing their lack of knowledge. Or at least get out of the way.
>
> I disagree. You don't help beginners learn English by ramming split
> infinitives and transverbial clauses down their throats. You keep things
> colloquial and introduce everyday stuff that will be immediately useful.

Nor do you help beginners learn English by giving them misleading
information, as you do for C.

You've said you intend to leave this newsgroup. I seriously recommend
cold turkey.

David Brown

unread,
Sep 2, 2018, 7:18:56 PM9/2/18
to
"int" is a "standard signed integer type" - that means it is also an
"signed integer type", a "standard integer type", an "arithmetic type"
and a "basic type" - as well as an "object type". There are many
classifications - some overlapping, some subsets/supersets, some
non-overlapping.

"int" is /usually/ either 16-bit or 32-bit, and a C byte is /usually/
8-bit. But those are not requirements of the standards - there are
real, current systems where this is not the case.

Bart

unread,
Sep 2, 2018, 8:16:29 PM9/2/18
to
On 02/09/2018 23:56, Keith Thompson wrote:
> Bart <b...@freeuk.com> writes:
>> On 02/09/2018 21:18, Keith Thompson wrote:

> Nor do you help beginners learn English by giving them misleading
> information, as you do for C.

I said I don't think of a function as a type (or having a type). You
agreed that many people don't.

Where's the misleading information?

You think people trying to learn the language for practical reasons are
going to be better off with a diet of pure standardese full of
references and quotations as though this was some sort of Holy book?

They'd do better learning to read between the lines, if they are going
to go anywhere near the standard at all. And learn to have an opinion of
their own rather than what has been Written.

(And above all, question what people say here. 20-40x speed-up merely by
turning on -O3, really?)

Meanwhile my toy C compiler can build and run applications like the Lua
interpreter and the Pico C interpreter despite several other products
failing to do so.

And, when necessary, I can generate pure C code (at some half million
lines per second), that will build on half a dozen targets.

That's despite my alleged poor knowledge of, and disdain for, the C
standard.

How about that?

Bart

unread,
Sep 2, 2018, 8:56:06 PM9/2/18
to
I have to say it looks pretty complicated.

I use a uniform system across projects now, where a type, any type, is
represented by an integer, an index into tables that give more
information about the type.

(This came from using that on interpreters where the type indicator had
to be very simple and very quick to access.)

The standard types (and generic placeholder types for arrays, structs,
pointers and functions) have the first 20 or so codes.

So if a pointer type has the index m, then what it points to has the
index tttarget[m]. The size of a type m is ttsize[m]. So the tables are
just a bunch of parallel arrays.

>> Subtly different placement of parentheses, but one is a pointer to a
>> function, the other is a function returning a pointer a function. Yet,
>> they can called in the same way:
>>
>>      (fn1)();
>>      (fn2)();

> Or, you know:
>   fn1();

Yeah...

> The C style declarations aren't too bad for the most part.

They're OK as far as doing:

int a,b,c;

In my opinion. But the type doesn't have to get much more complicated
than that before you run into stuff like this:

int long unsigned long const const typedef const const*const const a;

Which is the point you need to stop and question whether you really want
to use C's type syntax as a model for any new language.

--
bartc

Ben Bacarisse

unread,
Sep 2, 2018, 9:47:20 PM9/2/18
to
Bart <b...@freeuk.com> writes:

> On 02/09/2018 21:50, Ben Bacarisse wrote:
>> Bart <b...@freeuk.com> writes:
>>
>>> On 02/09/2018 19:55, Ike Naar wrote:
>>>> On 2018-09-02, Bart <b...@freeuk.com> wrote:
>>
>>>>> or, with most compilers, you can go mad with the the derefs:
>>>>>
>>>>> (**************fn1)();
>>>>> (**************fn2)();
>>>>
>>>> Would your compiler allow me to write
>>>>
>>>> int forty_two = 41;
>>>>
>>>> ?
>>>
>>> Yes. But you have to access it as:
>>>
>>> forty_two;
>>>
>>> not:
>>>
>>> ******forty_two;
>>
>> Do you allow +++++++++++forty_two? That's another thing C allows you to
>> go crazy with.
>
> Assuming you mean unary plus,

Yes, I should have put spaces between them.

> that takes a numeric type T as input, and delivers the same type T. So
> you can stack as many as you like.
>
> That doesn't abuse the language's type system so it is not a problem;
> just silly code, but code that is not misleading.

Your example is only misleading to people who don't know what it means.
That is simply inevitable in an artificial notation like a programming
language. If there are parts that someone does not know too well,
confusion results.

I have never seen anyone write (*****f)() just as I have never seen
anyone write + + + + + + +x. And even those the latter might be
considered less "mysterious" I suspect many readers would feel the need
to check with some reference or other just be be sure that it does not
have a special meaning.

> If the case of * for dereference, that is applied to a pointer type
> T*, and delivers a type T as a result (with the usual rules applying
> when T is an array type).

...and when T is a function type. Surely this is not a surprise
anymore. I remember exactly this conversation from a while back.

--
Ben.

Ben Bacarisse

unread,
Sep 2, 2018, 9:52:58 PM9/2/18
to
Did you think I said something that is at odds with that? There may
have been some confusion because I switched to the plural. A function is
not a type just as an integer is not a type.

--
Ben.

BGB

unread,
Sep 3, 2018, 12:27:20 AM9/3/18
to
If you mean the integer type signature, most predicates can be resolved
within a few branches, so it isn't too bad. Similarly, a majority of the
variable types can be expressed directly within the packed 32-bit
representation (without needing to fall back to the
somewhat-more-expensive type-overflow structures).


The ASCII serialization has its own uses, mostly used in bytecode
serialization (ex: RIL3 or BS2VM bytecode); Or within the name mangling
scheme (originally it was inspired by both the JVM and IA64 C++ ABI).

Its encoding is also a little more general in that it is mostly
independent of how how types are represented within the particular
compiler or VM. It is also human-readable, unlike the type-signature
encoding used in .NET, ...


It is worth noting that this is a typesystem which is trying to
simultaneously deal with C, C++ (subset), and BS2, so some additional
complexity is inherent.


> The standard types (and generic placeholder types for arrays, structs,
> pointers and functions) have the first 20 or so codes.
>

For the numeric scheme, I currently have 22 base types, beyond this,
types are generally an index into the literal table. Then the entry in
the table has an entry that says what it is (function, struct, union,
class, ...).

There are also typedef nodes, but these are not generally used "as" a
type except for in the construction of more complex types (usually, a
typedef's type is merged into whatever references it).


> So if a pointer type has the index m, then what it points to has the
> index tttarget[m]. The size of a type m is ttsize[m]. So the tables are
> just a bunch of parallel arrays.
>

OK.

As can be noted, there are different ways to do things.

I suspect probably MSVC uses parallel tables (as implied by the .NET
metadata), but mine has typically used one or two giant tables
(interconnected into a sort of graph structure).

Conceptually, it can be compared with the structure used in JVM ".class"
objects, just covering an entire program image (rather than a single class).


>>> Subtly different placement of parentheses, but one is a pointer to a
>>> function, the other is a function returning a pointer a function.
>>> Yet, they can called in the same way:
>>>
>>>      (fn1)();
>>>      (fn2)();
>
>> Or, you know:
>>    fn1();
>
> Yeah...
>
>> The C style declarations aren't too bad for the most part.
>
> They're OK as far as doing:
>
>   int a,b,c;
>
> In my opinion. But the type doesn't have to get much more complicated
> than that before you run into stuff like this:
>
>  int long unsigned long const const typedef const const*const const a;
>
> Which is the point you need to stop and question whether you really want
> to use C's type syntax as a model for any new language.
>

There are limits.


The actual first real implementation of BS2 was in 2015/2016. For this
language, I focused more on making it fast and trying to avoid needless
complexity (vs its predecessor, the original BGBScript, which mostly
spanned ~ 2004 to ~ 2012).

Actually, my C compiler had its origins in ~ 2008 originally as a fork
off of the BGBScript VM, and some experience from the C compiler effort
was fed back into the design for BS2.

In its VM-based form, it was used for writing parts of my "BGBTech2" 3D
engine (though, the majority of the engine is still C).

Videos kinda suck, but here is a partial example of the 3D engine:
https://www.youtube.com/watch?v=Kzvp3XZIcrI

For example, the renderer and voxel terrain code and similar is C, but a
lot of the parts involving player state/movement, NPC interaction, ...
was written in BS2.

Though, this 3D-engine project mostly fizzled out on the "no one really
seems to care" front, and I sort of ran short on ideas/motivation for
working on it not that long afterwards (after I started going down the
CPU arch rabbit hole).



I would be doing a semi-new implementation in the sense of compiling it
directly to machine-code, but plans weren't really to change the
language design all that drastically (though there will likely be some
minor differences).


BS2 declaration syntax is more constrained than C syntax in some ways:
Only a single token may give the type name;
Many modifiers have an implied order;
Modifiers may not generally appear more than once;
...

This is, partly, because the parser design does not rely on visible type
definitions, so it needs to be possible to unambiguously parse
declarations. Generally, what is valid for a declaration is invalid for
a statement or expression.

Some other differences:
The 'unsigned' modifier isn't used;
'uint' or 'ulong' or similar are used instead;
The use of 'const' is split into 'const' and 'final'.
The 'const' modifier means the object is immutable.
The 'final' modifier means the variable is immutable.
...

As noted, the language's syntax design is somewhat influenced by C# and
Java (though, semantically, it also borrows a fair bit from C and C++).


Then again, it probably still wont be terribly relevant (to PCs) as it
would mostly target BJX2. If I wanted this to be more immediately
relevant (on PC), would probably need an x86-64 backend.

But, then again, there is already the VM, and no one really seemed to
give a crap, so alas...

David Brown

unread,
Sep 3, 2018, 2:17:03 AM9/3/18
to
On 03/09/18 02:16, Bart wrote:
> On 02/09/2018 23:56, Keith Thompson wrote:
>> Bart <b...@freeuk.com> writes:
>>> On 02/09/2018 21:18, Keith Thompson wrote:
>
>> Nor do you help beginners learn English by giving them misleading
>> information, as you do for C.
>
> I said I don't think of a function as a type (or having a type). You
> agreed that many people don't.
>
> Where's the misleading information?

"I don't think of a function as a type." That's fair enough.

"Probably many people don't." I'd agree. It's not often that you think
much about the fact that functions have types in C.

"It only gets to be a type when it's the target of a pointer"

That is incorrect, and highly misleading. Functions do not get "to be"
types - they /have/ a type. They always have a type, and they never
/are/ a type.

"You can't dereference it" - a you can't dereference "int" either.

"or take its size" - you are correct, you can't get the size of a
function in C. That is rather obvious. There are other things you
can't take the "sizeof" in C.

"or compare one function to another." - also obvious.

While true, none of these are requirements to be "a type", none are
special to function types, and none are relevant.


You could reasonably say that function types play little role in C
programming except when you are using function pointers. You could
reasonably say that unless a programmer is looking at the standard, they
could pretty much ignore the the fact that functions have types - it's
usually enough to know about pointers to function types.

But saying things that are wrong, mixed-up, and confusing does not help
anyone.


>
> You think people trying to learn the language for practical reasons are
> going to be better off with a diet of pure standardese full of
> references and quotations as though this was some sort of Holy book?
>

I don't think anyone considers the standard to be a way to learn C. And
most people avoid quoting the standards at newbies, at least without
being sure they get a "plain English" explanation too. But I thought
you were supposed to be an experienced C programmer who wrote his own
compiler - surely /you/ can handle quotations from the standard?

Still, the main issue is that it is directly counterproductive to give
people incorrect, confused and misleading information. A simplified
version of the real rules is absolutely fine - incorrect ones are not.

Tim Rentsch

unread,
Sep 3, 2018, 2:47:41 AM9/3/18
to
Ben Bacarisse <ben.u...@bsb.me.uk> writes:

> Tim Rentsch <t...@alumni.caltech.edu> writes:
>
>> Ben Bacarisse <ben.u...@bsb.me.uk> writes:
>>
>>> [ ... considering an extension where
>>>
>>> typedef int F(int a,int b);
>>> F add {return a+b;}
>>>
>>> is allowed... ]
>>>
>>> I'm not sure I like the syntax though. It distances the definitions of
>>> the parameters from their use rather too much for my taste. I would
>>> prefer a syntax that names the parameters locally, though the types need
>>> not be repeated. I suppose
>>>
>>> (F add)(a, b) { return a + b; }
>>>
>>> would convey the general idea.
>>
>> A counter-suggestion, made without much thought behind it:
>
> Unlikely to have been with less thought than my suggestion!
>
>> auto F add(a,b){ return a+b; }
>>
>> or perhaps even
>>
>> static F add;
>>
>> ...
>>
>> auto add(a,b){ return a+b; }
>>
>> I am not necessarily in favor of this sort of thing. There are
>> plusses and minusses associated with having the types not be
>> available at the point of definition, but only elsewhere. But if
>> it is decided that something along these lines should be allowed,
>> my first reaction is to like the 'auto' method (or something like
>> it) more than the (F add) method.
>
> The syntax already allows for a declaration list to follow the
> declarator (a plain identifier in this case) so maybe
>
> F add auto a, b; { ... }
>
> would do.

I was surprised to learn that the official syntax does in fact
accept this syntactically.

I can't help feeling that a better alternative is available,
but for both of our sakes I'm going to stop now.

mark.b...@gmail.com

unread,
Sep 3, 2018, 3:38:08 AM9/3/18
to
On Monday, 3 September 2018 07:17:03 UTC+1, David Brown wrote:
(To Bart)
> I thought
> you were supposed to be an experienced C programmer who wrote his own
> compiler - surely /you/ can handle quotations from the standard?

I think it's clear by now that Bart's compiler is not a compliant C
compiler. I imagine it is "sort of" a C compiler, compiling C written
in compliance with Bart's prejudices and best guesses about what C
is. The standard has very little to do with Bart's ideas about C.

I don't doubt that Bart is a very experienced and in his way, competent
programmer but I can't fathom his unwillingness to fully and accurately
understand the language that he claims to compile and that he chooses to
use as an intermediate language for handling his own language which has
different syntax and (crucially) semantics. As I've pointed out before,
if he doesn't clearly understand C semantics (such as for example signed
overflow) how can he confidently assert that his generated C code is
a valid reflection of his source code?

Ike Naar

unread,
Sep 3, 2018, 4:02:06 AM9/3/18
to
On 2018-09-03, Bart <b...@freeuk.com> wrote:
> On 02/09/2018 23:56, Keith Thompson wrote:
>> Bart <b...@freeuk.com> writes:
>>> On 02/09/2018 21:18, Keith Thompson wrote:
>
>> Nor do you help beginners learn English by giving them misleading
>> information, as you do for C.
>
> I said I don't think of a function as a type (or having a type). You
> agreed that many people don't.

Nobody thinks of a function as a type. A function isn't a type;
a function *has* a type.

Compare this to persons and lengths. Nobody thinks of a person as
a length; but every person has a length.

> Where's the misleading information?
>
> You think people trying to learn the language for practical reasons are
> going to be better off with a diet of pure standardese full of
> references and quotations as though this was some sort of Holy book?
>
> They'd do better learning to read between the lines, if they are going
> to go anywhere near the standard at all. And learn to have an opinion of
> their own rather than what has been Written.
>
> (And above all, question what people say here. 20-40x speed-up merely by
> turning on -O3, really?)
>
> Meanwhile my toy C compiler can build and run applications like the Lua
> interpreter and the Pico C interpreter despite several other products
> failing to do so.
>
> And, when necessary, I can generate pure C code (at some half million
> lines per second), that will build on half a dozen targets.

It won't build on more than half a dozen targets, alas.

David Brown

unread,
Sep 3, 2018, 4:34:55 AM9/3/18
to
On 03/09/18 09:37, mark.b...@gmail.com wrote:
> On Monday, 3 September 2018 07:17:03 UTC+1, David Brown wrote:
> (To Bart)
>> I thought
>> you were supposed to be an experienced C programmer who wrote his own
>> compiler - surely /you/ can handle quotations from the standard?
>
> I think it's clear by now that Bart's compiler is not a compliant C
> compiler. I imagine it is "sort of" a C compiler, compiling C written
> in compliance with Bart's prejudices and best guesses about what C
> is. The standard has very little to do with Bart's ideas about C.

That's fine - there is no requirement for Bart to make a compliant C
compiler. He made the compiler primarily to handle the C code he
generates himself. Most other C compilers are not fully compliant - at
least, not in their default modes.

All I ask of him here is that he tries to write accurately when posting
here about C, and avoids knowingly writing things that are wrong and
potentially confusing to others. (Everyone can make mistakes, of course
- but that's different from doing so intentionally.)


Bart

unread,
Sep 3, 2018, 6:31:54 AM9/3/18
to
Half a dozen that I've actually tested at some point.

Namely, Win32, Win64, Linux32, Linux64 on Intel and Linux32 on ARM.
That's five (although the ARM was tested on two different machines with
different OSes, and Win32/64 on up to three different Windows versions).

But, if the OS is part of the target, there are also the OS-neutral
Nos32 and Nos64, which ought to be compilable on anything with 32/64-bit
processors, although with some reduced functionality for the application
if there are some OS-features it can't use.

Plus one more if you count the primary target, which is ASM on Win64.

How many actual targets do you routinely test your programs on?

The first programs I used to write were tied to the machine I happened
to be using, either because it used special compilers (for DEC, ICL
etc), or because the programs were written specially for the
architecture I was developing, or because it was what clients were using
(ie. what was commercially available to small businesses).

The sort of portability you're talking about is simply wasn't important.

If an application only does file I/O and it's in C, then that's good
enough. What more can I do?

--
bart

Bart

unread,
Sep 3, 2018, 6:56:37 AM9/3/18
to
On 03/09/2018 02:47, Ben Bacarisse wrote:
> Bart <b...@freeuk.com> writes:

>> That doesn't abuse the language's type system so it is not a problem;
>> just silly code, but code that is not misleading.
>
> Your example is only misleading to people who don't know what it means.
> That is simply inevitable in an artificial notation like a programming
> language. If there are parts that someone does not know too well,
> confusion results.

This is the usual spiel. Yes, whenever one highlights a bit of code
here, and says that it is confusing, then by the time a dozen experts
here have taken it apart, consulted the standard, looked in detail at
where and how all the various parts are defined, then in the end there
will always be a completely logical explanation for how it behaves.

That it is not very useful, because then it is impossible for any bit of
code to be said to be confusing. But of course we all know that many,
many things can be, especially in the context of a large program and
given that we will have hundreds of other things on our mind.

>
> I have never seen anyone write (*****f)() just as I have never seen
> anyone write + + + + + + +x.

Obviously, to you those superfluous *s don't matter at all. I guess that
if the language also allowed this:

const *const s, *const t;

*****s = *****t;

that wouldn't matter either. That is, if the language discarded extra
deref operations as harmless noise. Because you would rarely come across it.

Actually, the bigger problem is not the extra *s, but being able to use
fewer *s. Given a function pointer F, you can write is as:

F();

as well the correct:

(*F)(); // and the incorrect (***F)()

Now F is masquerading as an ordinary function. That, of course, is not
misleading at all, since you simply have to look at the type of F
(wherever that happens to be lurking when the app uses dozens of modules
and uses a hundred function names that are very similar to F).

However, whatever happened to that maxim of the C designers,
'declaration mirrors use' or some such thing. What was that all about,
if everyone ignores it?

And going back to my char* example, suppose the language also allowed this:

s = t;

to mean *s = *t? With s being const, it makes no sense to write to it,
so it must be *s, and the RHS would only then make sense as *t.

So no confusion whatsoever, it you consult the definitions of s and t,
and the language rules.

This is nonsense of course, but that's exactly what happens when someone
says a fragment of C can be confusing.

--
bart

Bart

unread,
Sep 3, 2018, 7:08:04 AM9/3/18
to
On 03/09/2018 11:56, Bart wrote:

> ... Given a function pointer F, you can write is as:
>
>    F();

...

> Now F is masquerading as an ordinary function. That, of course, is not
> misleading at all, since you simply have to look at the type of F
> (wherever that happens to be lurking when the app uses dozens of modules
> and uses a hundred function names that are very similar to F).

Of course, we don't know at this point what F is. There might well be a
function called F that we know about. But if F could be a function
pointer, then we also need to search all nested scopes, working outwards
looking for possible variables called F, on the off-chance that there
will be a variable F that is a function pointer, before it's possible to
be sure what's being called here.

Now some people will chime in and say, use adequate tools where you just
click on F and it will tell you what it is.

But you shouldn't really need to do that. It should be obvious.

And you shouldn't need to rely on GUI-based tools to write and
understand code. Imagine looking at a printout, for example.

--
bart

David Brown

unread,
Sep 3, 2018, 7:28:34 AM9/3/18
to
On 03/09/18 12:56, Bart wrote:
> On 03/09/2018 02:47, Ben Bacarisse wrote:
>> Bart <b...@freeuk.com> writes:
>
>>> That doesn't abuse the language's type system so it is not a problem;
>>> just silly code, but code that is not misleading.
>>
>> Your example is only misleading to people who don't know what it means.
>> That is simply inevitable in an artificial notation like a programming
>> language. If there are parts that someone does not know too well,
>> confusion results.
>
> This is the usual spiel. Yes, whenever one highlights a bit of code
> here, and says that it is confusing, then by the time a dozen experts
> here have taken it apart, consulted the standard, looked in detail at
> where and how all the various parts are defined, then in the end there
> will always be a completely logical explanation for how it behaves.
>
> That it is not very useful, because then it is impossible for any bit of
> code to be said to be confusing. But of course we all know that many,
> many things can be, especially in the context of a large program and
> given that we will have hundreds of other things on our mind.
>
>>
>> I have never seen anyone write (*****f)() just as I have never seen
>> anyone write + + + + + + +x.
>
> Obviously, to you those superfluous *s don't matter at all.

I have never written multiple *'s in such a context, nor ever seen them
written. I can't imagine why anyone /would/ write it. So no, it really
doesn't matter at all.

I think the fact that function types are converted to pointer to
function types in most cases is an oddity of the C language. I presume
this oddity exists for a reason - maybe it makes the grammar simpler to
describe in some way. However, it really is not a problem unless you
intentionally write obfuscated code - and if you want to do that, there
are plenty of other ways to write a mess (as there are in all
programming languages).

>
> Actually, the bigger problem is not the extra *s, but being able to use
> fewer *s. Given a function pointer F, you can write is as:
>
> F();
>
> as well the correct:
>
> (*F)(); // and the incorrect (***F)()
>
> Now F is masquerading as an ordinary function. That, of course, is not
> misleading at all, since you simply have to look at the type of F
> (wherever that happens to be lurking when the app uses dozens of modules
> and uses a hundred function names that are very similar to F).
>

That is not a problem. Both F() and (*F)() are correct - one is not
more correct than the other. The point here is that you /can/ use a
function pointer just like an ordinary function for making a call - so
the grammar of C lets you write it in the same way.

Whether you think this is a nice way to write your code or not, is up to
you - that's a stylistic choice. It lets you write code that is sort-of
object oriented if that is what you want - structs with function
pointers are a bit like object methods in other languages. On the other
hand, if you want to be very clear on the difference between function
pointers and "normal" functions, you can choose a naming convention and
usage to make the code clear - differentiate between "foo();" and
"(*fp_bar)();".

A programming language only gives you the tools - it's up to /you/ to
write clear code.


In your follow-up, you wrote "Of course, we don't know at this point
what F is". If you don't know what it is, don't call it - program by
design, not trial and error. And if you are reading the code and it is
not clear what F is, blame the guy that wrote unclear code, not the
language.


Bart

unread,
Sep 3, 2018, 7:41:16 AM9/3/18
to
On 03/09/2018 07:16, David Brown wrote:
> On 03/09/18 02:16, Bart wrote:


> "It only gets to be a type when it's the target of a pointer"
>
> That is incorrect, and highly misleading.  Functions do not get "to be"
> types - they /have/ a type.  They always have a type, and they never
> /are/ a type.

OK. Here's a variable:

int *X;

The type of X can be expressed in C syntax as int*. That is, just the
type and not the associated name.

Now here's a function:

int F(float,float);

How do you express purely its type using C syntax in the same way?

The fact is, you can't, not in a way that can ever be used in a C
program (eg. as a cast, or an unnamed parameter type, or a function
return type).

But a pointer to such a thing, that would be 'int (*)(float,float)'. Hence:

"It only gets to be a type when it's the target of a pointer"


Feel free to show me where I'm wrong. Unless it's just 'because the
standard says so'. Sometimes common sense transcends the standard.


--
bart

David Brown

unread,
Sep 3, 2018, 9:37:56 AM9/3/18
to
On 03/09/18 13:41, Bart wrote:
> On 03/09/2018 07:16, David Brown wrote:
>> On 03/09/18 02:16, Bart wrote:
>
>
>> "It only gets to be a type when it's the target of a pointer"
>>
>> That is incorrect, and highly misleading. Functions do not get "to
>> be" types - they /have/ a type. They always have a type, and they
>> never /are/ a type.
>
> OK. Here's a variable:
>
> int *X;
>
> The type of X can be expressed in C syntax as int*. That is, just the
> type and not the associated name.
>
> Now here's a function:
>
> int F(float,float);
>
> How do you express purely its type using C syntax in the same way?
>

I honestly don't know of a way to do that - I have never needed it or
thought about it. Maybe others know a way - or maybe there is no way.

But does that mean that the function F does not have a type? No, of
course it doesn't mean that. The function F has various characteristics
- you can define it, declare it, take its address and call it. That
means it has a type.

Think about what you can do with a type in C. You can use it for
declarations, for typedefs, or for making derived types. You can do all
that with function types - within the limitations and restrictions that
are natural for functions (unlike some languages, functions are not
first-class objects in C - you can't return them, manipulate them, etc.)
You can use function types in a typedef, you can use them for declaring
functions (it's rare to do it that way, but possible), and you can
derive pointers and pointer types for them.

No one is suggesting that function types are equivalent to object types
in C - they are classified separately, and have a number of differences
in what you can do with them. But they are both kinds of "type".

> The fact is, you can't, not in a way that can ever be used in a C
> program (eg. as a cast, or an unnamed parameter type, or a function
> return type).
>
> But a pointer to such a thing, that would be 'int (*)(float,float)'. Hence:
>
> "It only gets to be a type when it's the target of a pointer"
>
>
> Feel free to show me where I'm wrong. Unless it's just 'because the
> standard says so'. Sometimes common sense transcends the standard.
>

See above. You apparently believe that only object types are "proper
types" - therefore, in your mind, function types are not "proper types".
That is where you are wrong. Function types are not object types, but
they are still types.

Some languages may have different terminology, equating "type" with C's
meaning of "object type". When the C standard's terminology differs
from common usage, standard English language usage, or the term's use in
other programming languages or computing in general, it is unfortunate.
It can be for historic reasons - such as C's definition of "byte" being
older than the common modern usage of the term. If things are unclear,
be more precise - talk about "C function types" if you prefer. That
applies to all terms in the C standards.

When there is a difference, in the context of C, the standard transcends
common usage - not the other way round.

Bart

unread,
Sep 3, 2018, 10:39:55 AM9/3/18
to
On 03/09/2018 14:37, David Brown wrote:
> On 03/09/18 13:41, Bart wrote:

> See above. You apparently believe that only object types are "proper
> types" - therefore, in your mind, function types are not "proper types".
> That is where you are wrong. Function types are not object types, but
> they are still types.

I use actual function types (and also label types) within my
implementations. But there, mainly it's so I can have something that can
be the target of a pointer (actually there is a comment to that effect).

When I store a symbol entry for variable X, I also store its type, which
might be the internal code for 'int*' if its type is 'int*'.

With the symbol table entry for function F, I also store a type, but
it's the return of the function ('int' in my example).

I don't bother to construct an actual type for a function signature,
because they can be complicated, and the type system would be swamped
with thousands of the things. Most of the time they are not needed.

That is only done when there is a need to have an actual pointer to the
type. So here:

F(10.0, 20.0)

this is given special treatment when "(" follows "F", so that such a
type is not constructed (it will use a separate method to access
parameter info). But here:

F;
(F)(10.0, 20.0);

it will waste some time creating a pointer to a filled-in function type.

Summary: a function type of sorts exists inside the implementation. It
doesn't really exist in a user's program except for use with pointers.

--
bart

Richard Damon

unread,
Sep 3, 2018, 11:10:28 AM9/3/18
to
The type of F is (as I understand it) int ()(float, float)
You can create a typedef funtype for it by

typedef int (funtype)(float, float);

Given that you have done that, you can define a pointer to that type as

funtype *funptr;

I believe you can even declare F as

funtype F;

What you can't do it try to define F using that type (using an old-style
declaration for instance) by

funtype F float x, y; { }

is a syntax error.

Ben Bacarisse

unread,
Sep 3, 2018, 11:24:07 AM9/3/18
to
Bart <b...@freeuk.com> writes:

> On 03/09/2018 07:16, David Brown wrote:
>> On 03/09/18 02:16, Bart wrote:
>
>
>> "It only gets to be a type when it's the target of a pointer"
>>
>> That is incorrect, and highly misleading.  Functions do not get "to
>> be" types - they /have/ a type.  They always have a type, and they
>> never /are/ a type.
>
> OK. Here's a variable:
>
> int *X;
>
> The type of X can be expressed in C syntax as int*. That is, just the
> type and not the associated name.
>
> Now here's a function:
>
> int F(float,float);
>
> How do you express purely its type using C syntax in the same way?

int (float, float)

> The fact is, you can't, not in a way that can ever be used in a C
> program (eg. as a cast, or an unnamed parameter type, or a function
> return type).

You can use that in a cast. A conforming compiler will tell you that
you can't convert to a function type. Ditto in a cast, a _Generic
selection and so on.

By the way, these things are called "type names". It's a type name that
going into ()s in a cast and in a sizeof expression. int (float, float)
is the name of F's type. A type name is a declaration with the declared
identifier omitted.

> But a pointer to such a thing, that would be 'int (*)(float,float)'. Hence:
>
> "It only gets to be a type when it's the target of a pointer"
>
> Feel free to show me where I'm wrong. Unless it's just 'because the
> standard says so'. Sometimes common sense transcends the standard.

Functions have types just like objects do. That's why you can give a
function type a name. What type did you think your original typedef was
naming?

--
Ben.

Ben Bacarisse

unread,
Sep 3, 2018, 11:40:06 AM9/3/18
to
Richard Damon <Ric...@Damon-Family.org> writes:

> On 9/3/18 7:41 AM, Bart wrote:
>> On 03/09/2018 07:16, David Brown wrote:
>>> On 03/09/18 02:16, Bart wrote:
<snip>
>> Now here's a function:
>>
>>    int F(float,float);
>>
>> How do you express purely its type using C syntax in the same way?
<snip>

> The type of F is (as I understand it) int ()(float, float)

That's the type name of a function that returns a function. You can't
write such a function in C, but you can write its type!

There is an ambiguity with empty parentheses -- are they just redundant
parentheses round a missing identifier, or are they the ()s of a
function declarator? The standard says they should be taken to be those
of a function declarator.

The type name of F's type is, as I understand it, int (float, float).

<snip>
--
Ben.

Ben Bacarisse

unread,
Sep 3, 2018, 12:20:23 PM9/3/18
to
Bart <b...@freeuk.com> writes:

> On 03/09/2018 02:47, Ben Bacarisse wrote:
>> Bart <b...@freeuk.com> writes:
>
>>> That doesn't abuse the language's type system so it is not a problem;
>>> just silly code, but code that is not misleading.
>>
>> Your example is only misleading to people who don't know what it means.
>> That is simply inevitable in an artificial notation like a programming
>> language. If there are parts that someone does not know too well,
>> confusion results.
>
> This is the usual spiel. Yes, whenever one highlights a bit of code
> here, and says that it is confusing, then by the time a dozen experts
> here have taken it apart, consulted the standard, looked in detail at
> where and how all the various parts are defined, then in the end there
> will always be a completely logical explanation for how it behaves.

Yes, that's how programming languages work. If something is initially
confusing there should be a way to find out what it means.

Collegiate and helpful posters would explain the confusion, not try to
perpetuate it.

You can add redundant *s to a function call because function-valued
expressions usually "decay" to pointer-valued expressions, just like
array-valued ones usually do.

That's the helpful way to talk about (******f)(). No one would mind if
you also said "look at this crazy C code!". Everyone thinks (******f)()
is crazy code, just as everyone thinks !!!!!!!!!x is crazy code. (This
is a better example since it does not need spaces and it is more likely
to confuse than unary plus).

> That it is not very useful, because then it is impossible for any bit
> of code to be said to be confusing.

Of course it's confusing. It's just not confusing to me because I've
used C for decades and I even remember the crucial change in the
language that meant you no longer need to write (*fp)(...) for a call
through a function pointer. It was much debated at the time.

>> I have never seen anyone write (*****f)() just as I have never seen
>> anyone write + + + + + + +x.
>
> Obviously, to you those superfluous *s don't matter at all.

I have no idea how you can possibly conclude that. (You do this all the
time.)

They bother me a lot. There is no way I would write that, and no way it
pass a code review with me. I might, depending on context, leave a
single * if f is, in fact, a function pointer object rather than a
function designator, but that a different question.

As to whether I am bothered that it can be written in the first place,
only a little. The language is slightly simpler for the change that
permits it, and no less expressive. But it is also a little less
helpful in picking up type errors, so it is a debatable point. I can
honestly say I've flip-flopped on which way the balance goes more than
once.

> I guess that if the language also allowed this:
>
> const *const s, *const t;
>
> *****s = *****t;
>
> that wouldn't matter either. That is, if the language discarded extra
> deref operations as harmless noise. Because you would rarely come
> across it.

I strongly urge you not to try to guess what I think.

> Actually, the bigger problem is not the extra *s, but being able to
> use fewer *s.

Yes, that is more of an issue, but I am not sure, even after all these
years, how much of an issue it really is.

--
Ben.

jacobnavia

unread,
Sep 3, 2018, 12:24:40 PM9/3/18
to

I said, answering to you:

<quote>
Happily (for you) you do not propose any changes/improvements to the
language, so you can ignore this group. That wasn't my case, and I
endured thompson/heathfield for several years before giving up. They
destroyed my project after succesfully bullying me out of it.
<end quote>

Now, thompson writes:

> You've said you intend to leave this newsgroup. I seriously recommend
> cold turkey.

This constant bullying by thompson spanning several years continues and
nobody notices, nt even you, that are asked without any doubts to just
"go away".

And nobody will object to this guy's behavior. Actually nobody even
noticed my message that was immediately drowned in some C trivia.

Bart

unread,
Sep 3, 2018, 12:32:23 PM9/3/18
to
On 03/09/2018 16:23, Ben Bacarisse wrote:
> Bart <b...@freeuk.com> writes:
>
>> On 03/09/2018 07:16, David Brown wrote:
>>> On 03/09/18 02:16, Bart wrote:
>>
>>
>>> "It only gets to be a type when it's the target of a pointer"
>>>
>>> That is incorrect, and highly misleading.  Functions do not get "to
>>> be" types - they /have/ a type.  They always have a type, and they
>>> never /are/ a type.
>>
>> OK. Here's a variable:
>>
>> int *X;
>>
>> The type of X can be expressed in C syntax as int*. That is, just the
>> type and not the associated name.
>>
>> Now here's a function:
>>
>> int F(float,float);
>>
>> How do you express purely its type using C syntax in the same way?
>
> int (float, float)

>
>> The fact is, you can't, not in a way that can ever be used in a C
>> program (eg. as a cast, or an unnamed parameter type, or a function
>> return type).
>
> You can use that in a cast.

OK, I was mistaken.

While a function type is still rather abstract, you can denote such an
anonymous type in C code, even with rather limited use-cases.

But this then is just another thing that you weren't aware 'was a thing'
(to use BGB's expression).

A conforming compiler will tell you that
> you can't convert to a function type. Ditto in a cast, a _Generic
> selection and so on.

My compiler won't support that (and I'm not planning to fix it) so
either it doesn't automatically follow from implementing the grammar, or
my parser does its own thing.

(In my own language, because function types are usually meaningless by
themselves, there is no syntax to denote them, unless following 'ref'.

Syntax for function definitions is separate, and does not involve
creating a type for the whole function. As usually you will never need
such a thing. Functions have signatures, not types.)

--
bart

Bart

unread,
Sep 3, 2018, 12:54:25 PM9/3/18
to
On 03/09/2018 17:20, Ben Bacarisse wrote:
> Bart <b...@freeuk.com> writes:

> Yes, that's how programming languages work. If something is initially
> confusing there should be a way to find out what it means.

Or, for some of us, to try to fix the language or create a new one where
that confusion is less likely.

> As to whether I am bothered that it can be written in the first place,
> only a little. The language is slightly simpler for the change that
> permits it, and no less expressive.

That's what I meant. And I have some doubts about whether it makes the
language simpler. I've battled the last week or two with issues caused
by the decay of arrays, and the equivalence of F and G when only one is
an actual pointer, throwing the type system out of step.

(And apparently I'm still doing something by being too sensible in
throwing out (**********F)().)

Wouldn't it be MUCH easier for learning, teaching, writing, reading,
documenting and implementing the language, if the * of each pointer in
the type was always matched by exactly one * in an expression when you
had to dereference that pointer?

(Hypothetical question since C is what it is.)

But it is also a little less
> helpful in picking up type errors, so it is a debatable point. I can
> honestly say I've flip-flopped on which way the balance goes more than
> once.
>
>> I guess that if the language also allowed this:
>>
>> const *const s, *const t;
>>
>> *****s = *****t;
>>
>> that wouldn't matter either. That is, if the language discarded extra
>> deref operations as harmless noise. Because you would rarely come
>> across it.
>
> I strongly urge you not to try to guess what I think.

You're missing my point, which is being able to question poor aspects of
the language, even if they are perfectly logically set out in the
language reference.

--
bart

Bart

unread,
Sep 3, 2018, 12:57:12 PM9/3/18
to
On 03/09/2018 17:24, jacobnavia wrote:

> Now, thompson writes:
>
> > You've said you intend to leave this newsgroup.  I seriously recommend
> > cold turkey.
>
> This constant bullying by thompson spanning several years continues and
> nobody notices, nt even you, that are asked without any doubts to just
> "go away".

I noticed it. I decided to ignore it.

And actually it may have had the opposite effect.

Tim Rentsch

unread,
Sep 3, 2018, 3:33:59 PM9/3/18
to
Richard Damon <Ric...@Damon-Family.org> writes:

> [A typedef may be used to create a type name for a function type:
>
> typedef int funtype(float, float);
> ]
>
> Given that you have done that, you can define a pointer to that type as
>
> funtype *funptr;
>
> I believe you can even declare F as
>
> funtype F;
>
> What you can't do it try to define F using that type (using an old-style
> declaration for instance) by
>
> funtype F float x, y; { }
>
> is a syntax error.

Strictly speaking, it's a constraint violation rather than a
syntax error, but it does require a diagnostic.

(Credit is due to Ben Bacarisse for educating me on this
aspect of C syntax recently.)

Tim Rentsch

unread,
Sep 3, 2018, 3:57:45 PM9/3/18
to
[My apologies for a small hodgepodge of comments.]

Ben Bacarisse <ben.u...@bsb.me.uk> writes:

> Bart <b...@freeuk.com> writes:
>
>> On 03/09/2018 02:47, Ben Bacarisse wrote:
>>
>>> Bart <b...@freeuk.com> writes:
>>>
>>>> That doesn't abuse the language's type system so it is not a problem;
>>>> just silly code, but code that is not misleading.
>>>
>>> Your example is only misleading to people who don't know what it means.
>>> That is simply inevitable in an artificial notation like a programming
>>> language. If there are parts that someone does not know too well,
>>> confusion results.
>>
>> This is the usual spiel. Yes, whenever one highlights a bit of code
>> here, and says that it is confusing, then by the time a dozen experts
>> here have taken it apart, consulted the standard, looked in detail at
>> where and how all the various parts are defined, then in the end there
>> will always be a completely logical explanation for how it behaves.
>
> Yes, that's how programming languages work. If something is initially
> confusing there should be a way to find out what it means.
>
> Collegiate and helpful posters would explain the confusion, not try to
> perpetuate it.

I think the word you're looking for is collegial. Collegiate
means something somewhat different, at least in American English.
(I did look for a different meaning in British English but didn't
fine one. Of course I may have missed something.)

> You can add redundant *s to a function call because function-valued
> expressions usually "decay" to pointer-valued expressions, just like
> array-valued ones usually do.
>
> That's the helpful way to talk about (******f)(). No one would mind if
> you also said "look at this crazy C code!". Everyone thinks (******f)()
> is crazy code, just as everyone thinks !!!!!!!!!x is crazy code. (This
> is a better example since it does not need spaces and it is more likely
> to confuse than unary plus).
>
>> That it is not very useful, because then it is impossible for any bit
>> of code to be said to be confusing.
>
> Of course its confusing. It's just not confusing to me because I've
> used C for decades and I even remember the crucial change in the
> language that meant you no longer need to write (*fp)(...) for a call
> through a function pointer. It was much debated at the time.
>
>>> I have never seen anyone write (*****f)() just as I have never seen
>>> anyone write + + + + + + +x.
>>
>> Obviously, to you those superfluous *s don't matter at all.
>
> I have no idea how you can possibly conclude that. (You do this all the
> time.) [...]

I noticed something recently about Bart's postings. Every so
often he gets a response that is exactly on point, and helps
answer his question, and only that, with no loose ends. What I
noticed is, he never responds to those postings. He only
responds to postings where there is some sort of loose thread, to
which he redirects his focus to complain about whatever that is.

After seeing this happen several times over the course of a few
weeks, I decided it would be better if I just didn't respond to
him at all, regardless of the content of the posting. I'm not
offering any conclusion or making some sort of statement; I
just thought it might be helpful to give the reason for my
decision. So fwiw that's it.

Keith Thompson

unread,
Sep 3, 2018, 4:31:03 PM9/3/18
to
jacobnavia <ja...@jacob.remcomp.fr> writes:
[...]
> This constant bullying by thompson spanning several years continues
> and nobody notices, nt even you, that are asked without any doubts to
> just "go away".

jacob, you have been bullying me for years. I'd ask you to stop if I
thought it would do any good. Your ability to hold a grudge for
imagined slights astonishes me.

Bart *said* he was going to leave the newsgroup. My suggestion was that
he not draw out the process as he's been doing. He is, of course, under
no obligation to follow my advice.

--
Keith Thompson (The_Other_Keith) ks...@mib.org <http://www.ghoti.net/~kst>
Working, but not speaking, for JetHead Development, Inc.
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"

David Brown

unread,
Sep 3, 2018, 4:55:41 PM9/3/18
to
On 03/09/18 16:39, Bart wrote:
> On 03/09/2018 14:37, David Brown wrote:
>> On 03/09/18 13:41, Bart wrote:
>
>> See above.  You apparently believe that only object types are "proper
>> types" - therefore, in your mind, function types are not "proper types".
>>   That is where you are wrong.  Function types are not object types, but
>> they are still types.
>
> I use actual function types (and also label types) within my
> implementations. But there, mainly it's so I can have something that can
> be the target of a pointer (actually there is a comment to that effect).
>

That is fair enough. The main use of function types (from the user's
viewpoint) is as the target for function pointer types. But your
compiler has function types, and C has function types. Why were you
claiming they don't really exist?

> When I store a symbol entry for variable X, I also store its type, which
> might be the internal code for 'int*' if its type is 'int*'.
>
> With the symbol table entry for function F, I also store a type, but
> it's the return of the function ('int' in my example).
>
> I don't bother to construct an actual type for a function signature,
> because they can be complicated, and the type system would be swamped
> with thousands of the things. Most of the time they are not needed.
>

If you also store the number and types of the function's arguments, you
are effectively also storing the function's type. If you don't store
this information (from a prototype declaration), you won't be able to
check that later calls are correct - and you won't spot such glaring
errors in the code. (They would be constraint violations, so for
conformance you are supposed to issue a diagnostic of some sort.)

> That is only done when there is a need to have an actual pointer to the
> type. So here:
>
>   F(10.0, 20.0)
>
> this is given special treatment when "(" follows "F", so that such a
> type is not constructed (it will use a separate method to access
> parameter info). But here:
>
>   F;
>   (F)(10.0, 20.0);
>
> it will waste some time creating a pointer to a filled-in function type.
>
> Summary: a function type of sorts exists inside the implementation. It
> doesn't really exist in a user's program except for use with pointers.
>

A better summary - function types exist in C, and every function
(declared or defined) has a type. Implementations are free to ignore
this most of the time, as long as they act as though they did consider
functions to have types. Users are also free to ignore this if they
want. But in the definition of the language, functions have types.

Ben Bacarisse

unread,
Sep 3, 2018, 5:34:56 PM9/3/18
to
I wanted to express the notion of us all helping each other to learn,
but looking at the dictionary definition of collegiate, that is probably
not the right word as the "Belonging or relating to a college or its
students" appears to be used only very literally.

In UK English at least collegial does not convey anything to do with
students and learning -- only harmony amongst colleagues.

So I am stuck for a word that refers (positively) to an environment of
mutual education and learning. I thought collegiate could mean that,
but I now suspect not (unless it's being used at least partly
metaphorically).

--
Ben.

Ben Bacarisse

unread,
Sep 3, 2018, 6:38:21 PM9/3/18
to
Bart <b...@freeuk.com> writes:

> On 03/09/2018 16:23, Ben Bacarisse wrote:
>> Bart <b...@freeuk.com> writes:

>>> Now here's a function:
>>>
>>> int F(float,float);
>>>
>>> How do you express purely its type using C syntax in the same way?
>>
>> int (float, float)
>
>>
>>> The fact is, you can't, not in a way that can ever be used in a C
>>> program (eg. as a cast, or an unnamed parameter type, or a function
>>> return type).
>>
>> You can use that in a cast.
>
> OK, I was mistaken.
>
> While a function type is still rather abstract, you can denote such an
> anonymous type in C code, even with rather limited use-cases.

Unnamed function types (ironically called type names) have almost no
uses, except as a vehicle for explaining things. Named function types
(like the original typedef all those posts ago) are used -- I use them
quite a lot.

> But this then is just another thing that you weren't aware 'was a
> thing' (to use BGB's expression).

I'm not sure what the "this" is that I was not aware of

>> A conforming compiler will tell you that
>> you can't convert to a function type. Ditto in a cast, a _Generic
>> selection and so on.
>
> My compiler won't support that (and I'm not planning to fix it) so
> either it doesn't automatically follow from implementing the grammar,
> or my parser does its own thing.

It should fall out of the syntax, except for the ambiguity about empty
()s. Where did you get the syntax from?

By the way, you don't have to implement this other than issuing some
diagnostic. Even "syntax error" or "oops!" would do. I have not
checked thoroughly, but I think everywhere you can use a type name, a
function type is a constraint violation. Provided there is some
diagnostic, your compiler can do anything it likes, including accepting
it, rejecting it, or whistling Dixie. Specifically, you don't have to
parse it and issue a message about how you can't use a function type in
that context. You can just stop compiling at that point.

--
Ben.

Ben Bacarisse

unread,
Sep 3, 2018, 7:09:42 PM9/3/18
to
Bart <b...@freeuk.com> writes:

> On 03/09/2018 17:20, Ben Bacarisse wrote:
>> Bart <b...@freeuk.com> writes:
>
>> Yes, that's how programming languages work. If something is initially
>> confusing there should be a way to find out what it means.
>
> Or, for some of us, to try to fix the language or create a new one
> where that confusion is less likely.

You are not fixing C. You can create a new language which, most likely
will not get used much, but will remain exactly as it was. To change C
you need political muscle on WG14.

>> As to whether I am bothered that it can be written in the first place,
>> only a little. The language is slightly simpler for the change that
>> permits it, and no less expressive.
>
> That's what I meant. And I have some doubts about whether it makes the
> language simpler. I've battled the last week or two with issues caused
> by the decay of arrays, and the equivalence of F and G when only one
> is an actual pointer, throwing the type system out of step.

I can't comment because I don't know what the battles have been.
Function calls are (since ANSI C) expressions that involve a function
pointer. This works for ordinary function names because they decay to a
pointer like arrays do.

> (And apparently I'm still doing something by being too sensible in throwing out (**********F)().)
>
> Wouldn't it be MUCH easier for learning, teaching, writing, reading,
> documenting and implementing the language, if the * of each pointer in
> the type was always matched by exactly one * in an expression when you
> had to dereference that pointer?

I am never confident that I know what such a change would really mean
for the language as a whole. It would certainly be more consistent, but
that boat sailed long ago. Even in pre-K&R C, a function name meant
different things depending on the context. Given

int f();

writing f(42) called f, but writing g(f) passed a pointer. A consistent
approach to addresses and referencing would require g(&f) to get the
address. At the other end, K&R C did require (*p)(42) to call a
function through a pointer.

ANSI C made f(42) consistent with this by making f decay to a pointer in
that context too. Consistency then requires that *f also decay since
it's an expression with function type too. And this permits
(*******f)().

> (Hypothetical question since C is what it is.)
>
> But it is also a little less
>> helpful in picking up type errors, so it is a debatable point. I can
>> honestly say I've flip-flopped on which way the balance goes more than
>> once.
>>
>>> I guess that if the language also allowed this:
>>>
>>> const *const s, *const t;
>>>
>>> *****s = *****t;
>>>
>>> that wouldn't matter either. That is, if the language discarded extra
>>> deref operations as harmless noise. Because you would rarely come
>>> across it.
>>
>> I strongly urge you not to try to guess what I think.
>
> You're missing my point, which is being able to question poor aspects
> of the language, even if they are perfectly logically set out in the
> language reference.

No, I am not missing that point. I understand it perfectly well. Your
example was an attempt to put words into my mouth based on a
misunderstanding both of C and of what I think about it.

--
Ben.

Tim Rentsch

unread,
Sep 3, 2018, 7:16:32 PM9/3/18
to
> function type is a constraint violation. [...]

There is a significant exception to that rule, namely, giving
the type of a parameter in a function declaration that is not
a definition. Thus,

int foo( int () );

is an acceptable declaration for the function foo().

Chris M. Thomasson

unread,
Sep 3, 2018, 7:23:10 PM9/3/18
to
On 9/2/2018 6:52 PM, Ben Bacarisse wrote:
> "Chris M. Thomasson" <invalid_chr...@invalid.invalid> writes:
>
>> On 9/2/2018 3:38 PM, Ben Bacarisse wrote:
>>> Bart <b...@freeuk.com> writes:
>>>
>>>> On 02/09/2018 15:09, Ben Bacarisse wrote:
>>>>> Bart <b...@freeuk.com> writes:
>>>>
>>>>>> It is amazing how often that happens with C. Like, for example,
>>>>>> typedefing actual functions, not just pointers to functions:
>>>>>>
>>>>>> #include <stdio.h>
>>>>>>
>>>>>> typedef int F(int a,int b);
>>>>>
>>>>> You sound surprised. Would you not be more surprised if a mechanism to
>>>>> define types did not let you define function types?
>>>>
>>>> I don't think of a function as a type. Probably many people don't.
>>>
>>> I think you mean you don't consider function types to be types.
>>> Functions obviously aren't types, just as integers aren't types.
>>> Anyway, functions have a type -- a function type.
>>
>> Fwiw, I always thought of int as an integer data "type" with a range
>> of two or four bytes?
>
> Did you think I said something that is at odds with that? There may
> have been some confusion because I switched to the plural. A function is
> not a type just as an integer is not a type.
>

Yeah. I did get a bit confused. Sorry.

Ben Bacarisse

unread,
Sep 3, 2018, 8:51:00 PM9/3/18
to
Tim Rentsch <t...@alumni.caltech.edu> writes:

> Ben Bacarisse <ben.u...@bsb.me.uk> writes:
<snip>
>> ... I have not
>> checked thoroughly, but I think everywhere you can use a type name, a
>> function type is a constraint violation. [...]
>
> There is a significant exception to that rule, namely, giving
> the type of a parameter in a function declaration that is not
> a definition. Thus,
>
> int foo( int () );
>
> is an acceptable declaration for the function foo().

That's not a type name, it just looks like one! The syntax for a type
name is more restrictive than the syntax for an unnamed parameter in a
function declaration.

Mind you, I suppose one could invoke a "duck syntax" rule -- if it looks
like a type name, it is a type name.

--
Ben.

Bart

unread,
Sep 5, 2018, 7:10:23 AM9/5/18
to
On 04/09/2018 00:09, Ben Bacarisse wrote:
> Bart <b...@freeuk.com> writes:

>> Wouldn't it be MUCH easier for learning, teaching, writing, reading,
>> documenting and implementing the language, if the * of each pointer in
>> the type was always matched by exactly one * in an expression when you
>> had to dereference that pointer?
>
> I am never confident that I know what such a change would really mean
> for the language as a whole.

Actually, C /can/ be used consistently. So if A, S and F are pointers to
an array, struct, and function respectively, then you can use them via a
dereference as follows:

(*A)[i]
(*S).m
(*F)(x)

So exactly one * each that corresponds to the pointer * in the type of each.

Except, C isn't used like that. For A, idiomatic C requires A is of type
T* rather than T(*)[]. For S, everyone uses -> instead. And for F, often
the * and () will be left out, because it's possible.

The end result is that the orthogonal examples above are usually written as:

A[i]
S->m
F(x)

The problem being that a non-pointer A and non-function-pointer F would
also be written as:

A[i]
F(x)

Only the pointer to struct is written differently, but there have been
calls to allows S.m to be used for that too, so that pointer and
non-pointer versions are indistinguishable, just like with arrays and
functions.

People who write C do seem keen to eradicate the use of "*" in
expressions as much as possible!

Anyway, while the language is capable of consistency, that is of little
use because it tends to be written differently, and the language allows it.

As it does the use of extra, rather then fewer, *, although as said that
is less of a problem. It's just mildly misleading and somewhat confusing
when an extra * slips through:

void(*F)(void);
void(*G)(void);

(*F)();
(**G)();

(Real examples of course, with much longer names, far more complex
expressions, and spread across 100,000 lines of other code, will not be
so obvious.)

--
bart

Ben Bacarisse

unread,
Sep 5, 2018, 7:23:33 AM9/5/18
to
Bart <b...@freeuk.com> writes:

> On 04/09/2018 00:09, Ben Bacarisse wrote:
>> Bart <b...@freeuk.com> writes:
>
>>> Wouldn't it be MUCH easier for learning, teaching, writing, reading,
>>> documenting and implementing the language, if the * of each pointer in
>>> the type was always matched by exactly one * in an expression when you
>>> had to dereference that pointer?
>>
>> I am never confident that I know what such a change would really mean
>> for the language as a whole.
>
> Actually, C /can/ be used consistently.

I don't know why you say "actually". It's almost as if you think I
think it can't be.

--
Ben.

Richard Damon

unread,
Sep 5, 2018, 7:24:47 AM9/5/18
to
If A is a 'pointer to array, like int (*A)[10] then (*A)[1] and A[1]
return very different locations.

*A is of type int [10], which will decay to int*, and then (*A)[1] will
return the first element of that array. A[1], since A is a pointer will
treat it as a pointer to the first element of an array and take the
second instance of that, which will be the 10 element array, 10 ints
after the int pointed to by A. This array value will then normally decay
into a pointer to the first element of that array.

(*A)[i] will have type int. A[i] will have type int*

Ben Bacarisse

unread,
Sep 5, 2018, 9:01:22 AM9/5/18
to
Richard Damon <Ric...@Damon-Family.org> writes:

> On 9/5/18 7:10 AM, Bart wrote:
>> ... So if A, S and F are pointers to
>> an array, struct, and function respectively, then you can use them via a
>> dereference as follows:
>>
>>    (*A)[i]
>>    (*S).m
>>    (*F)(x)
>>
>> So exactly one * each that corresponds to the pointer * in the type of
>> each.
>>
>> Except, C isn't used like that. For A, idiomatic C requires A is of type
>> T* rather than T(*)[]. For S, everyone uses -> instead. And for F, often
>> the * and () will be left out, because it's possible.
>>
>> The end result is that the orthogonal examples above are usually written
>> as:
>>
>>    A[i]
>>    S->m
>>    F(x)

<snip>
> If A is a 'pointer to array, like int (*A)[10] then (*A)[1] and A[1]
> return very different locations.

Yes, but Bart is doing his best to make C appear as confusing as
possible by changing the type of A between examples: "For A, idiomatic C
requires A is of type T* rather than T(*)[]". He only implies that this
is type you are to use for A in the second example, but that's what you
are supposed to do.

One of his themes is that C uses a pointer to an element as some sort of
"pointer to an array" rather than adopting the most helpful description
that C accesses arrays via a pointer to an element (not even always the
first!). He's not wrong in one thing: many people are a bit lax about
this and talk about "pointing to an array" when they mean a pointer to
an array element (usually the first).

<snip>
--
Ben.

bart...@gmail.com

unread,
Sep 5, 2018, 11:05:13 AM9/5/18
to
BB: "Yes, but Bart is doing his best to make C appear as confusing as
possible by changing the type of A between examples: ...."

What you're supposed to take away from my post is that you don't see those orthogonal forms in C code. When you do see (*F), you can't be sure that F is actually a function pointer with one level of indirection. And you can't be sure that the A in A[I] has T* type or T[] type.

From Richard Damon's post, it appears that A[I] can actually be a pointer to array type, T(*)[], but is selecting the i'th array along. A third source of confusion.

This reinforces my view that strictly enforcing a one * per deref rule would make a significant difference in understanding code.

It doesn't even need a language change. Just a new option. Which it seems is already how many here are moulding the language to suit their own views.

--
Bart

Ben Bacarisse

unread,
Sep 5, 2018, 12:12:53 PM9/5/18
to
bart...@gmail.com writes:

> BB: "Yes, but Bart is doing his best to make C appear as confusing as
> possible by changing the type of A between examples: ...."
>
> What you're supposed to take away from my post is that you don't see
> those orthogonal forms in C code.

I think pretty much everyone here knows that. And I think pretty much
everyone here knows why you don't see them in real code (except for
(*f)(...) which many people, especially old hands, favour). What was
the purpose of saying it again, if not to keep stoking the propaganda
campaign?

> From Richard Damon's post, it appears that A[I] can actually be a
> pointer to array type, T(*)[], but is selecting the i'th array
> along. A third source of confusion.

Since [] is defined in terms of *, why do you find this confusing? And
since you appear to understand the "access an array though pointing to a
member" idiom how do you think 2D array indexing works of not as Richard
explained? I am finding it hard to sustain the idea that your continual
surprise at these things is genuine.

> This reinforces my view that strictly enforcing a one * per deref rule
> would make a significant difference in understanding code.
>
> It doesn't even need a language change. Just a new option. Which it
> seems is already how many here are moulding the language to suit their
> own views.

The standard library does not use this convention, so a change there
would be needed. How, for example do you pass and array pointer to
strncpy? And I don't think you've tried using this strategy much or you
would find all sorts of problems. For example, how would you re-write
bsearch using this consistent use of *?

--
Ben.

bart...@gmail.com

unread,
Sep 5, 2018, 2:58:43 PM9/5/18
to
I'll reply in more detail when I can use a proper newsreader and proper keyboard.

--
Bart

Tim Rentsch

unread,
Sep 5, 2018, 8:17:42 PM9/5/18
to
Ben Bacarisse <ben.u...@bsb.me.uk> writes:

> Tim Rentsch <t...@alumni.caltech.edu> writes:
>
>> Ben Bacarisse <ben.u...@bsb.me.uk> writes:
>
> <snip>
>
>>> ... I have not
>>> checked thoroughly, but I think everywhere you can use a type name, a
>>> function type is a constraint violation. [...]
>>
>> There is a significant exception to that rule, namely, giving
>> the type of a parameter in a function declaration that is not
>> a definition. Thus,
>>
>> int foo( int () );
>>
>> is an acceptable declaration for the function foo().
>
> That's not a type name, it just looks like one!

Oh yeah. I was wondering if there might be something like that
but I didn't double check it. I should have known you would be
on top of that.

> The syntax for a type name is more restrictive than the syntax for
> an unnamed parameter in a function declaration.
>
> Mind you, I suppose one could invoke a "duck syntax" rule -- if it
> looks like a type name, it is a type name.

After looking up the difference, I feel better about my mistake.
Although the syntax for an unnamed parameter accepts a larger set
of strings than the syntax for a type name, after semantic
constraints are added there is very little difference between the
two. AFAICT (and please correct me if I've missed something),
the only difference is that unnamed parameters can have a storage
class of 'register', which doesn't even affect the type in
question. So this looks pretty duck-like.

Bart

unread,
Sep 6, 2018, 8:19:44 AM9/6/18
to
On 05/09/2018 17:12, Ben Bacarisse wrote:
> bart...@gmail.com writes:
>
>> BB: "Yes, but Bart is doing his best to make C appear as confusing as
>> possible by changing the type of A between examples: ...."
>>
>> What you're supposed to take away from my post is that you don't see
>> those orthogonal forms in C code.
>
> I think pretty much everyone here knows that.

You don't know that. They could well be lurkers here who think they know
C but would be surprised you can use (*A)[i], (*S).m and (*F)(x) forms,
especially the first two.

>> From Richard Damon's post, it appears that A[I] can actually be a
>> pointer to array type, T(*)[], but is selecting the i'th array
>> along. A third source of confusion.
>
> Since [] is defined in terms of *, why do you find this confusing?

It just is. Everyone already knows that then you see A[i], then A can
have either T[] type, or T*. A few might be aware of actual
pointers-to-arrays of type T(*)[] that are normally accessed as (*A)[i].

But they might be mildly surprised that a T(*)[] can also be accessed as
A[i]! (With a different meaning.)

You are suggesting it should not be a surprise since every [object and
non-empty array] type that starts with 'pointer-to' can be accessed as
[i], yet it is.

> I am finding it hard to sustain the idea that your continual
> surprise at these things is genuine.

It is genuine. I should no longer be surprised at anything in C but I am.

>> It doesn't even need a language change. Just a new option. Which it
>> seems is already how many here are moulding the language to suit their
>> own views.
>
> The standard library does not use this convention, so a change there
> would be needed. How, for example do you pass and array pointer to
> strncpy? And I don't think you've tried using this strategy much or you
> would find all sorts of problems.

I've been using the C runtime, and other libraries using a C API, since
1992, in a language that strictly enforces my (*A)[i], (*S).m and
(*F)(x) rule. It seems to work.

(TBF that language was lax in checking pointer compatibility. Now it's
much stricter, needing a few tweaks, but it's still eminently doable.)

> For example, how would you re-write
> bsearch using this consistent use of *?

No need to change existing libraries. If they use T* parameters rather
than T(*)[], then it's easy to convert. bsearch() also uses void*
parameters; even simpler.

'Arrays' represented with T* types /are/ much simpler to manipulate in
some ways, for example in working with slices. So tail(A) might just be
A+1 [given enough elements in A]. A version based on T(*)[] is fiddly
and will involve casting.

A proper solution would involve supporting slices in the language, but
that would step the level of the language up a notch.

Another way is just to fall back to using pointers to blocks, with
pointer dereferencing done with *(p+i) if the (*A)[i] rules are enforced.

Of course, enforcing such rules in C would mean that 99.9% of existing
programs would no longer work.

But remember I was talking about how much easier the language would be
if they were in place:

bart:
> Wouldn't it be MUCH easier for learning, teaching, writing, reading,
> documenting and implementing the language, if the * of each pointer in
> the type was always matched by exactly one * in an expression when you
> had to dereference that pointer?
>
> (Hypothetical question since C is what it is.)

(Implementing this stuff I notice that, in C, A[i] results in one of
three possible AST forms depending on what A is. In my language, there
is only one possible AST form. C is more complex.)

-------------------------


(At the moment I have some 100,000 lines of generated C code of active
programs, all exclusively using the (*A)[i], (*S).m and (*F)(x) forms
I've talked about.

The syntax is rather ugly, even after allowing for code generation
artefacts, but that's C's fault. It is workable. In such source code, if
you ever see A[i], then you KNOW that A has type T[], never T*; and you
KNOW that the F in F(x) is an actual function.)


--
bart

Ben Bacarisse

unread,
Sep 6, 2018, 5:00:43 PM9/6/18
to
Bart <b...@freeuk.com> writes:

> On 05/09/2018 17:12, Ben Bacarisse wrote:
>> bart...@gmail.com writes:
>>
>>> BB: "Yes, but Bart is doing his best to make C appear as confusing as
>>> possible by changing the type of A between examples: ...."
>>>
>>> What you're supposed to take away from my post is that you don't see
>>> those orthogonal forms in C code.
>>
>> I think pretty much everyone here knows that.
>
> You don't know that. They could well be lurkers here who think they
> know C but would be surprised you can use (*A)[i], (*S).m and (*F)(x)
> forms, especially the first two.

You appear to be supporting my claim. What I claimed "pretty much
everyone here knows" is that "you don't see those orthogonal forms in C
code". All those lurkers are surely in the category who know you don't
see those form in C code.

>>> From Richard Damon's post, it appears that A[I] can actually be a
>>> pointer to array type, T(*)[], but is selecting the i'th array
>>> along. A third source of confusion.
>>
>> Since [] is defined in terms of *, why do you find this confusing?
>
> It just is.

I see. Nothing to see here... move on.

> Everyone already knows that then you see A[i], then A can have either
> T[] type, or T*. A few might be aware of actual pointers-to-arrays of
> type T(*)[] that are normally accessed as (*A)[i].

They should know quite the opposite. In idiomatic C, a pointer to an
array is usually accessed A[i]. (*A)[i] does occur, very rarely, but
indexing an array pointer is everyday fare in some kinds of program.

I maybe biased by having seen lots of non-representative C the code, but
I've seen a lot of C over the years, and I think it's a typical mix.

> But they might be mildly surprised that a T(*)[] can also be accessed
> as A[i]! (With a different meaning.)

They should expect that a pointer /can/ be indexed. If they don't they
need to read a good tutorial and fewer of your posts. And I would be
very alarmed by any C programmer who was even a tiny bit surprised that
two indirections have a different meaning as one.

> You are suggesting it should not be a surprise since every [object and
> non-empty array] type that starts with 'pointer-to' can be accessed as
> [i],

Yes. Someone who is surprised by any pointer being indexed has been let
down by their C education. Your posts don't help. Beginners might
actually think that it's all as complicated as you say it is.

You seem to consider array pointers to be special, but they arise
naturally in passing 2D arrays to functions. Just as you typically pass
a pointer to the first int element of an int array, you pass a pointer
to the first array element in an array array:

int calculate(int m, int n, int (*array)[n]) ...

Inside the function you index the pointer to get a row:

array[i]

which, since that expression decays to a pointer to the first int in row
i, could be passed to a function that processes an int array:

sum_row(n, array[i]);

or that pointer (note pointer, not array) could be further indexed to
get an element: array[i][j].

At the other end, so to speak, a function like the one above is simply
called like this

int a[5][6];
...
calculate(5, 6, a);

because the type of the expression a (in that position) is "pointer to
array of 6 ints". Now C allows you to hide the pointer syntax in this
case like so:

int calculate(int m, int n, int array[][n]) ...

or even using the more self-documenting form:

int calculate(int m, int n, int array[m][n]) ...

but a C programmer who has to work with multi-dimensional arrays should
know that 'array' is really a pointer to array parameter in both of
these. Any good explanation of arrays and functions should cover all
this (not for beginners, of course).

>> I am finding it hard to sustain the idea that your continual
>> surprise at these things is genuine.
>
> It is genuine.

I have to take your word for it but I would have expected something of
the previous discussions of this very topic to have stuck in your mind.

Anyway, at the risk of failing yet again to prevent further surprise:

. [E] simply means add and apply *. It is a dereferencing operation.
. P[E] is /only/ valid if P is a pointer.
. You /can't/ use [] on an array.

>>> It doesn't even need a language change. Just a new option. Which it
>>> seems is already how many here are moulding the language to suit their
>>> own views.
>>
>> The standard library does not use this convention, so a change there
>> would be needed. How, for example do you pass and array pointer to
>> strncpy? And I don't think you've tried using this strategy much or you
>> would find all sorts of problems.
>
> I've been using the C runtime, and other libraries using a C API,
> since 1992, in a language that strictly enforces my (*A)[i], (*S).m
> and (*F)(x) rule. It seems to work.

Yes, but your point was that strict enforcement would make things
consistent. The C library does not use this convention so code that
calls standard C function has to be inconsistent at the boundary.
For example, fgets does not take a pointer-to-array argument.

I presume, though I can't be sure, that you'd just make almost every T *
into a T (*)[]. Presumably you would you do the same with return types?

>> For example, how would you re-write
>> bsearch using this consistent use of *?
>
> No need to change existing libraries. If they use T* parameters rather
> than T(*)[], then it's easy to convert. bsearch() also uses void*
> parameters; even simpler.

Conversions are not the point. I was inviting you to show some code
written this way so that readers can compare styles. It would also help
pin down what you are actually suggesting.

As far as I can tell, the main change is lots of clutter to get a false
sense of significantly greater consistency. I say it's false because I
think you are over stating the inconsistency of C as it is usually
written. The cost in terms of clutter is significant.

I suspect your experience is mainly in your source language, and you
find generating C in this style to be a better fit when converting from
that source. But what matters in generated C is not what matters in
human written C.

> But remember I was talking about how much easier the language would be
> if they were in place:

Yes, I know. And, if you remember, I said I don't know what the full
consequences would be, but one at least would be a new standard library
that used whatever style it is you are suggesting. I know that this new
library could no longer use C's idiomatic "point to the start and pass a
length if needed" style, but I don't really know what you are proposing
in its place.

> (Implementing this stuff I notice that, in C, A[i] results in one of
> three possible AST forms depending on what A is. In my language, there
> is only one possible AST form. C is more complex.)

Do you mean that in your C compiler you generate three form of syntax
tree from one syntactic construct? If so, that does not seem right. My
first guess would be that E1[E2] results in something like

*
|
+
/ \
/ \
E1 E2

What three ASTs do you generate? Maybe there is some type analysis
being folded into the parse?

--
Ben.

Bart

unread,
Sep 6, 2018, 5:41:29 PM9/6/18
to
On 06/09/2018 22:00, Ben Bacarisse wrote:
> Bart <b...@freeuk.com> writes:

> You seem to consider array pointers to be special, but they arise
> naturally in passing 2D arrays to functions. Just as you typically pass
> a pointer to the first int element of an int array, you pass a pointer
> to the first array element in an array array:
>
> int calculate(int m, int n, int (*array)[n]) ...
>
> Inside the function you index the pointer to get a row:
>
> array[i]

This is one of the things that I find very confusing. I see (*A)[n] and
I think, ah, a pointer to a 1D array.

But apparently you mean this to be a 2D array.

Well, similarly to how a T* parameter might be intended to be either a
reference to a single T, or a pointer to the start of an array of T,
then this might be my pointer to 1D array, or a pointer to the first row
of a 2D array (that first row then being the 1D array I would assume it
was).

It seems as though C just doesn't like actual pointers to arrays!

> Conversions are not the point. I was inviting you to show some code
> written this way so that readers can compare styles. It would also help
> pin down what you are actually suggesting.

> As far as I can tell, the main change is lots of clutter to get a false
> sense of significantly greater consistency.

That's the snag. In C, you have to use ugly syntax like (*A)[i], (*S).m,
(*F)(x) because of its design (compare with the slightly cleaner A^[i],
S^.m, F^(x) I normally write).

So it seems that consistency and transparency are thrown out and various
hacks and special rules added to allow you to write A[i], S->m, F(x).

>> (Implementing this stuff I notice that, in C, A[i] results in one of
>> three possible AST forms depending on what A is. In my language, there
>> is only one possible AST form. C is more complex.)
>
> Do you mean that in your C compiler you generate three form of syntax
> tree from one syntactic construct? If so, that does not seem right. My
> first guess would be that E1[E2] results in something like
>
> *
> |
> +
> / \
> / \
> E1 E2
>
> What three ASTs do you generate? Maybe there is some type analysis
> being folded into the parse?

My C compiler combines a parser and type analyser into one pass. But
even as separate passes, the result will be a modified AST with type
info filled in.

The three types of nodes are like this when adjusted according to types:

In C, AST for:

A[i] when A is int[10] ('ptr' means deref or * operation):

00011 int------------|---1 ptr:
00011 ref int--------|---|---1 addptr: Ptrscale: 4
00011 ref int--------|---|---|---1 addrof &:
00011 [10]int--------|---|---|---|---1 name: A
00011 llong----------|---|---|---2 name: i

A[i] when A is int*:

00012 int------------|---1 ptr:
00012 ref int--------|---|---1 addptr: Ptrscale: 4
00012 ref int--------|---|---|---1 name: A
00012 llong----------|---|---|---2 name: i

A[i] when A is int(*)[10] (incorporates array decay):

00013 ref int--------|---1 addptr: Ptrscale: 40
00013 ref [10]int----|---|---1 name: A
00013 llong----------|---|---2 name: i


In my language, AST after type analysis just like above:

A[i] when A is int[10]:

0013 i32------------- - 1 index:
0013 [1..10]i32------ - - 1 name: a
0013 i64------------- - - 2 name: i

A[i] when A is array 10 of int[3]:

0014 [1..3]i32------- - - 1 index:
0014 [1..10][1..3]i32 - - - 1 name: a
0014 i64------------- - - - 2 name: i

A[i] when A is array 10 of struct of (int,int,int):

0015 trecord(i32 d,i3 - - 1 index:
0015 [1..10]date----- - - - 1 name: a
0015 i64------------- - - - 2 name: i

Etc. It will always be the same. (It's not possible to use A[i] when A
is any sort of pointer. You have to use A^[i] provided A^ is an array.)


--
bart

Bart

unread,
Sep 6, 2018, 7:43:50 PM9/6/18
to
On 06/09/2018 22:41, Bart wrote:
> On 06/09/2018 22:00, Ben Bacarisse wrote:

>> What three ASTs do you generate?  Maybe there is some type analysis

> In C, AST for:
>
> A[i] when A is int[10] ('ptr' means deref or * operation):
>
>  00011 int------------|---1 ptr:
>  00011 ref int--------|---|---1 addptr: Ptrscale: 4
>  00011 ref int--------|---|---|---1 addrof &:
>  00011 [10]int--------|---|---|---|---1 name: A
>  00011 llong----------|---|---|---2 name: i
>
> A[i] when A is int*:
>
>  00012 int------------|---1 ptr:
>  00012 ref int--------|---|---1 addptr: Ptrscale: 4
>  00012 ref int--------|---|---|---1 name: A
>  00012 llong----------|---|---|---2 name: i
>
> A[i] when A is int(*)[10] (incorporates array decay):
>
>  00013 ref int--------|---1 addptr: Ptrscale: 40
>  00013 ref [10]int----|---|---1 name: A
>  00013 llong----------|---|---2 name: i

There appears to be a fourth type of node for A[i] when A is int[10][3]:

00015 ref int----------|---1 addptr: Ptrscale: 12
00015 ref [3]int-------|---|---1 addrof &:
00015 [10][3]int-------|---|---|---1 name: A
00015 llong------------|---|---2 name: i

So four different 'shapes' all for A[i], compared to the one shape I
use, no matter what the types involved, even after any transformations
due to type manipulations.

This is partly due to C allowing [] on pointer types, and because
attempts to access array types cause a decay to a pointer.


--
bart

Ben Bacarisse

unread,
Sep 6, 2018, 9:54:02 PM9/6/18
to
Bart <b...@freeuk.com> writes:

> On 06/09/2018 22:00, Ben Bacarisse wrote:
>> Bart <b...@freeuk.com> writes:
>
>> You seem to consider array pointers to be special, but they arise
>> naturally in passing 2D arrays to functions. Just as you typically pass
>> a pointer to the first int element of an int array, you pass a pointer
>> to the first array element in an array array:
>>
>> int calculate(int m, int n, int (*array)[n]) ...
>>
>> Inside the function you index the pointer to get a row:
>>
>> array[i]
>
> This is one of the things that I find very confusing. I see (*A)[n]
> and I think, ah, a pointer to a 1D array.

You are correct. That is what int (*A)[n] is.

> But apparently you mean this to be a 2D array.

No. I mean it to be a pointer to a 1D array. Similarly, when I write

int sum(int n, int *nums);

I mean that 'nums' is a pointer to an int. That is literally what each
type says. Arrays in C, as you know, are usually accessed via a pointer
to an element -- often, but not always, the first.

> Well, similarly to how a T* parameter might be intended to be either a
> reference to a single T, or a pointer to the start of an array of T,
> then this might be my pointer to 1D array, or a pointer to the first
> row of a 2D array (that first row then being the 1D array I would
> assume it was).

Yes, though it's a little more general than that because a T * might
point to no T object.

Give some T *p, the range of i for which p[i] is valid can, in general,
only be determined by understanding the code -- you certainly can't tell
from the declaration. Even p[0] may be invalid. And p[0] may be
invalid, but p[-1] might me fine. Or maybe p[0] to p[10] might all be
valid.

> It seems as though C just doesn't like actual pointers to arrays!

It loves them deeply.

>> Conversions are not the point. I was inviting you to show some code
>> written this way so that readers can compare styles. It would also help
>> pin down what you are actually suggesting.
>
>> As far as I can tell, the main change is lots of clutter to get a false
>> sense of significantly greater consistency.
>
> That's the snag. In C, you have to use ugly syntax like (*A)[i],
> (*S).m, (*F)(x) because of its design (compare with the slightly
> cleaner A^[i], S^.m, F^(x) I normally write).
>
> So it seems that consistency and transparency are thrown out and
> various hacks and special rules added to allow you to write A[i],
> S->m, F(x).

Well, that's polemic again. Consistency is not "thrown out" because
there are consistent rules. It's possible to argue about whether one
scheme is more or less consistent, or whether one set of rules is
simpler. It would take a full description of what you propose to
evaluate both the increase in consistency and any possible costs.

>>> (Implementing this stuff I notice that, in C, A[i] results in one of
>>> three possible AST forms depending on what A is. In my language, there
>>> is only one possible AST form. C is more complex.)
>>
>> Do you mean that in your C compiler you generate three form of syntax
>> tree from one syntactic construct? If so, that does not seem right. My
>> first guess would be that E1[E2] results in something like
>>
>> *
>> |
>> +
>> / \
>> / \
>> E1 E2
>>
>> What three ASTs do you generate? Maybe there is some type analysis
>> being folded into the parse?
>
> My C compiler combines a parser and type analyser into one pass. But
> even as separate passes, the result will be a modified AST with type
> info filled in.

And from your examples I think you factor in the context of the
expression as well. That's not usually what I think of as a syntax tree
but the term is not well-defined.

--
Ben.

Richard Damon

unread,
Sep 6, 2018, 11:30:53 PM9/6/18
to
On 9/6/18 8:19 AM, Bart wrote:
>
> It just is. Everyone already knows that then you see A[i], then A can
> have either T[] type, or T*. A few might be aware of actual
> pointers-to-arrays of type T(*)[] that are normally accessed as (*A)[i].
>
> But they might be mildly surprised that a T(*)[] can also be accessed as
> A[i]! (With a different meaning.)

Why should it be surprising. You said that people know that a T* can be
accessed by [] with a know meaning. The fact that in this case the type
of T is R[] (to change the name to avoid confusion). Just as a T* and
T[m] address similarly so do R(*)[n] and R[n][m]

Bart

unread,
Sep 7, 2018, 6:18:39 AM9/7/18
to
You yourself just mentioned the word confusion. If R is meant to be T(*)
then the type 'starts with' * not [].

I was speculating on how much simpler C might be to put across if,
effectively, applying []-indexing to types starting with * was banned.

At present, nobody can tell if a T*, say as a parameter, is intended as
a pointer to a single T, or to an indexable block (a 1D array)

And similarly, they can't tell if T(*)[] is a pointer to a single 1D
array, or a block of such arrays (ie. a 2D array). But since I recognise
this type pattern as one I use for the former and tend to assume that's
what it is, it's a surprise to find out it's meant to be a 2D array.

I really don't think I'm the only one.

(I'm not going to get into the confusion by whether your R is literally
T(*), then R[] would be T(*)[]; or whether R is meant to be a typedef
T*, then R[] would be T*[], a different beast.)

The only way the confusion can be mitigated in the 1D/2D case, would be
to assume that pointers to 1D arrays are never used to merely pass a
reference to a 1D arrray; they will always be 2D in the T(*)[] case.

However you can't really make the assumption for the T* case, unless you
see the parameter or variable used with [].

Phew. I'm just glad I'm not the one who's required to actually code in
this language. Or to teach it.

--
bart

Bart

unread,
Sep 7, 2018, 7:11:14 AM9/7/18
to
On 07/09/2018 11:18, Bart wrote:

> And similarly, they can't tell if T(*)[] is a pointer to a single 1D
> array, or a block of such arrays (ie. a 2D array). But since I recognise
> this type pattern as one I use for the former and tend to assume that's
> what it is, it's a surprise to find out it's meant to be a 2D array.
>
> I really don't think I'm the only one.

I think it was a mistake to try it, and I risk get accused of sowing
confusion, but I wanted to try a T(*)[] type and see how many ways there
were of legally, if not correctly, accessing it:

typedef int T;
long long int i,j;

T (*A)[10];

A[i]; // int*
*A; // int*
**A; // int
A[i][j]; // int
(*A)[i]; // int
*(A[i]); // int

j[i[A]]; // int
....

6 different ways (before you start with the weird rearrangements).

Which of these makes sense is another question.

If I try the same type and same accesses in my language, which has short
shrift with this kind of nonsense, then here are the results expressed
in C. The lines commented out were rejected outright, only the remaining
two lines were possible:


// A[i]; // (A is not an array)

*A; // int[10]

// **A; // (*A is not a pointer)
// A[i][j]; // (A is not an array)

(*A)[i]; // int

// *(A[i]); // (A is not an array)
// j[i[A]]; // (j is not an array)

Since the type is a pointer to array, the only access possibilities are
to dereference the pointer, or dereference the pointer then index the array.

You might also notice the result of dereferencing a pointer-to-array, is
an array (not a pointer to int as in C).

Now everybody is going to tell me that there is nothing wrong with C, I
just don't understand it (frankly, I don't want to).

I understand why it works as it does above (I have to to implement it);
I just disagree strongly that any language should work like this.

What I don't understand is why people defend the language and accuse
anyone who criticises such aspects of not understanding it.

Again, I feel sorry for anyone who has to teach it.

Good luck with that, and good luck to everyone who mistakenly gets it
mixed up as the language will not help you.

--
bart

Richard Damon

unread,
Sep 7, 2018, 10:04:40 AM9/7/18
to
On 9/7/18 6:18 AM, Bart wrote:
> On 07/09/2018 04:30, Richard Damon wrote:
>> On 9/6/18 8:19 AM, Bart wrote:
>>>
>>> It just is. Everyone already knows that then you see A[i], then A can
>>> have either T[] type, or T*. A few might be aware of actual
>>> pointers-to-arrays of type T(*)[] that are normally accessed as (*A)[i].
>>>
>>> But they might be mildly surprised that a T(*)[] can also be accessed as
>>> A[i]! (With a different meaning.)
>>
>> Why should it be surprising. You said that people know that a T* can be
>> accessed by [] with a know meaning. The fact that in this case the type
>> of T is R[] (to change the name to avoid confusion). Just as a T* and
>> T[m] address similarly so do R(*)[n] and R[n][m]
>>
>
> You yourself just mentioned the word confusion. If R is meant to be T(*)
> then the type 'starts with' * not [].

The confusion was in consecutive examples of using the same symbol to
represent different type, and not having names for those types.
>
> I was speculating on how much simpler C might be to put across if,
> effectively, applying []-indexing to types starting with * was banned.
>
> At present, nobody can tell if a T*, say as a parameter, is intended as
> a pointer to a single T, or to an indexable block (a 1D array)
>
> And similarly, they can't tell if T(*)[] is a pointer to a single 1D
> array, or a block of such arrays (ie. a 2D array). But since I recognise
> this type pattern as one I use for the former and tend to assume that's
> what it is, it's a surprise to find out it's meant to be a 2D array.
>
> I really don't think I'm the only one.
>
> (I'm not going to get into the confusion by whether your R is literally
> T(*), then R[] would be T(*)[]; or whether R is meant to be a typedef
> T*, then R[] would be T*[], a different beast.)
>
> The only way the confusion can be mitigated in the 1D/2D case, would be
> to assume that pointers to 1D arrays are never used to merely pass a
> reference to a 1D arrray; they will always be 2D in the T(*)[] case.
>
> However you can't really make the assumption for the T* case, unless you
> see the parameter or variable used with [].
>
> Phew. I'm just glad I'm not the one who's required to actually code in
> this language. Or to teach it.
>

Yes, one weakness with C is that there is no way to declare that a
pointer is to a single item, and not into an element of an array. All
pointer all there value to be adjusted with +, - and []. There could be
some advantages to being able to indicate the size of the array (or
bounds of access), including that a given pointer points to exactly one
object, and not an array. You can't just make this the default for a
pointer without breaking a LOT of code.

Note, that in C there is a BIG difference in context between a pointer
to an element of an array, and a pointer to an array itself (even though
people are sometime sloppy and call a pointer to an element as a pointer
to an array). The types int* foop, and int (*foopa)[], are very different.

int* is NEVER a pointer to an array, but only a pointer to an element
(perhaps only conceptually of size 1). The Standard even has words to
the effect that pointers to non-array objects can be thought of as
pointers to arrays of size 1 of that object.

Perhaps some of the confusion comes because when we declare a variable like:

int foo[10];

The identifier foo has a type that is an array, but for most usages of
it, that identifier decays int a value of type int* (a pointer to an
element of the array, or a pointer to an int object), so to the unwary
it can act like it has two different types.

My understanding is that much of this behavior has been inherited as an
evolution from the proto-C era when the language was largely typeless. A
variable foo might be considered an integer value for doing arithmetic
on it, but you could also treat it as a pointer to the location
represented by that value doing things like *foo or foo[n].

Yes, perhaps C is an old crotchety language, but with that age also
comes much wisdom when used right. It is perhaps not the best language
for a neophyte programmer, as it comes optimized for performance and not
safety. One of its big advantages is that since it has been around
'forever', it has become a core interconnection technology.

Ben Bacarisse

unread,
Sep 7, 2018, 11:56:07 AM9/7/18
to
Bart <b...@freeuk.com> writes:

> I think it was a mistake to try it, and I risk get accused of sowing
> confusion, but I wanted to try a T(*)[] type and see how many ways
> there were of legally,

But then you went and used a different type! Maybe that'll sow another
small seed of confusion in the minds of some readers.

> if not correctly, accessing it:
>
> typedef int T;
> long long int i,j;
>
> T (*A)[10];
>
> A[i]; // int*
> *A; // int*
> **A; // int
> A[i][j]; // int
> (*A)[i]; // int
> *(A[i]); // int
>
> j[i[A]]; // int
> ....
>
> 6 different ways (before you start with the weird rearrangements).

Curiously you have avoided most of the really odd cases by writing full
expressions. I hate to think what you'd have written if you'd been
really trying to show the complications.

C has some pretty simple rules that explain what these expressions mean,
and since I prefer to clarify C than point and gasp at it, the main one
is that

E1[E2]

means add and indirect: *(E1 + E2) so of course it's commutative (E2[E1]
means the same thing as E1[E2]) because + is. Thus you can write all of
them in terms indexing:

A[i] A[i]
*A A[0]
**A A[0][0]
A[i][j] A[i][j]
(*A)[i] A[0][i]
*(A[i]) A[i][0]

or in terms of arithmetic and indirection with a third column showing
where E + 0 can be simplified:

A[i] *(A + i)
*A *(A + 0) *A
**A *(*(A + 0) + 0) **A
A[i][j] *(*(A + i) + j)
(*A)[i] *(*(A + 0) + i) *(*A + i)
*(A[i]) *(*(A + i) + 0) **(A + i)

This is particularly scary because you need to know the second rule:
array valued expressions like *(A + i) are converted to pointer
expressions -- specifically to a pointer to the first element of the
array.

> If I try the same type and same accesses in my language, which has
> short shrift with this kind of nonsense,

I am at a disadvantage here since without a reasonably complete public
specification, I have no idea what the costs of any of your language
design decisions were. You (quite understandably) only ever post about
what you see as the benefits.

> You might also notice the result of dereferencing a pointer-to-array,
> is an array (not a pointer to int as in C).

No, it's the same in C. The type of the indirection expression *A is
int[10] in C. You know this. You also know if and when (as the lawyers
say) that value is converted to a pointer.

> Now everybody is going to tell me that there is nothing wrong with C,

I doubt anyone has ever said that about C. Who has said it here?

--
Ben.

Bart

unread,
Sep 7, 2018, 1:24:27 PM9/7/18
to
On 07/09/2018 16:55, Ben Bacarisse wrote:
> Bart <b...@freeuk.com> writes:

> But then you went and used a different type! Maybe that'll sow another
> small seed of confusion in the minds of some readers.

Do you mean using [10] instead of []? Apparently it makes a difference
in C; not so much in my language, since the examples fail for other
reasons before it gets to where it needs the array to be bounded to do
certain things with it.

>> 6 different ways (before you start with the weird rearrangements).
>
> Curiously you have avoided most of the really odd cases by writing full
> expressions. I hate to think what you'd have written if you'd been
> really trying to show the complications.

I don't know what you mean here. Are you saying those access can get
even more elaborate?

> This is particularly scary because you need to know the second rule:
> array valued expressions like *(A + i) are converted to pointer
> expressions -- specifically to a pointer to the first element of the
> array.

What's scary is that if A is only ever intended to point to one array,
then it still allows A[i] and *(A+i). [This is where the target of A
needs to be bounded.]

>> If I try the same type and same accesses in my language, which has
>> short shrift with this kind of nonsense,
>
> I am at a disadvantage here since without a reasonably complete public
> specification,

You just need to know that arrays are arrays, and pointers are pointers.
Very different types. You index one and dereference the other, with or
without an offset.


I have no idea what the costs of any of your language
> design decisions were. You (quite understandably) only ever post about
> what you see as the benefits.

That's not true. I do mention drawbacks sometimes, like yesterday when I
said that slice-handling was harder with a strict array model. I also
showed that I have to use syntax like A^[i], where idiomatic C would end
up using A[i] even it helps hide the true nature of A.

>> You might also notice the result of dereferencing a pointer-to-array,
>> is an array (not a pointer to int as in C).
>
> No, it's the same in C. The type of the indirection expression *A is
> int[10] in C.

My AST says that the result is int*. (Although some indication that it
came from an array needs to be retained so that sizeof and addrof can be
applied later with the correct results.)

Perhaps you mean the type is int[10] unless you try and use the result
or even just try and observe it.

You know this. You also know if and when (as the lawyers
> say) that value is converted to a pointer.

The conversion is done at compile-time. In my case, during type analysis.

--
bart

Bart

unread,
Sep 7, 2018, 3:53:30 PM9/7/18
to
On 29/08/2018 12:11, Bart wrote:
> On 29/08/2018 09:20, mertes...@gmail.com wrote:
>> Hello,
>>
>> Last year I tried to compile Seed7 with the C compilers mcc32 and
>> mcc64, provided by Bart. Now, when I look at
>>
>>    https://github.com/bartg/langs/tree/master/bccproj
>>
>> the compilers mcc32.c and mcc64.c have been gone.
>> What happend to this C compilers?
>> Does anybody know more about them?
>
> They were withdrawn (from that site) for several reasons.

After doing a bit more work on it, I can add one more reason for why it
was withdrawn, probably the main one. Having to provide the full working
set of headers that people expect, would be a life sentence.

I've just added several dozen more missing definitions, but have made
very little progress (in compiling the modules of Seed7).

In fact, looking at gcc's bundled headers on Windows, there are some
1400 .h files totalling 55MB. Others are not much smaller.

Even Tiny C bundles 82 .h files that come to 1.2MB.

My compiler's bundled headers comprise 43 files totalling 84KB, 1/600th
the size of gcc's. So it would be a long haul.

After having to track down yet another missing *stat* function (just MS
provides 12 variations of stat(), and 6 versions of struct stat), I
decided to throw in the towel.

I don't anyway think it is the job of a language implementation to also
have to provide headers for hundreds of other libraries and applications.

>     https://github.com/sal55/mlang/tree/master/dist

(The last version, with its minimal headers, is at the same link. This
one at least will compile and link itself. I call it a toy compiler for
a C dialect.

Building Seed7 successfully seems unlikely. Out of the 157 .c files that
I'd earmarked last year for reasons I forget, gcc currently compiles 137
of them. My bcc compiles 126.

This suggests the gap is smaller than it really is, because there are
significant files missing such as sql*.h and winsock2.h. Imports of
those from other systems don't work. And at least 90% of windows.h is
still missing, even if most of Seed7's demands there are satisfied.

But even if I could temporarily work around the modules with missing
headers, I'm not able to build and test the application to resolve the
inevitable problems, which I normally do incrementally.)

--
bart

Ben Bacarisse

unread,
Sep 7, 2018, 5:47:58 PM9/7/18
to
Bart <b...@freeuk.com> writes:

> On 07/09/2018 16:55, Ben Bacarisse wrote:
>> Bart <b...@freeuk.com> writes:

>>> If I try the same type and same accesses in my language, which has
>>> short shrift with this kind of nonsense,
>>
>> I am at a disadvantage here since without a reasonably complete public
>> specification,
>
> You just need to know that arrays are arrays, and pointers are
> pointers. Very different types. You index one and dereference the
> other, with or without an offset.

That's an unhelpful statement. Did you separate out the first part of
my remark just so you could say it?

In case there is any doubt about what I meant, I was saying that I would
need a specification in order to assess the consequences of arrays being
arrays and pointers being pointers. That they are what they are does
not tell me what I need to know about the overall design.

Pascal (the original Pascal) had that property, and its arrays were
almost unusable. Algol 68 had it, but it had very powerful array
handling.

>> I have no idea what the costs of any of your language
>> design decisions were. You (quite understandably) only ever post about
>> what you see as the benefits.
>
> That's not true. I do mention drawbacks sometimes, like yesterday when
> I said that slice-handling was harder with a strict array model.

This was in the context of using a restricted C, and here is what you
said about it:

'Arrays' represented with T* types /are/ much simpler to manipulate in
some ways, for example in working with slices. So tail(A) might just
be A+1 [given enough elements in A]. A version based on T(*)[] is
fiddly and will involve casting.

That looks like C to me -- the syntax, the talk of casting -- it all
appears to be about the restricted C we had been discussing. How could
I know this was a criticism about your language? For all I know (and
here we get back to the point I made above) your language gets round the
fact that you can't so certain slices with more restricted pointers in
some other way -- I don't have the specification.

Given the restricted nature of pointers, and your use of Algol 68-like
terminology, I had always assumed you had some sort of sub-array syntax.
If there were a public specification, I could have found that out for
myself.

> I also showed that I have to use syntax like A^[i], where idiomatic C
> would end up using A[i] even it helps hide the true nature of A.

The whole sub-thread has been about how the explicit syntax is an
advantage! Why would I take your saying that you have to be explicit as
some sort of criticism of you design?

Anyway, if you don't like it, you can very simply get rid of it. If and
expression E is type error in some context where E^ would not be, you
can add an implicit indirection. A[i] would still mean A^[i] but there
would be some syntactic sugar for it. You could write 'a + pb' rather
than 'a + pb^' and 'ps.m' rather than 'ps^.m'.

Of course, I don't have the language specification (much less any
experience using it) so I don't know what the full consequences of this
rule might be in terms of readability, errors and so on.

>>> You might also notice the result of dereferencing a pointer-to-array,
>>> is an array (not a pointer to int as in C).
>>
>> No, it's the same in C. The type of the indirection expression *A is
>> int[10] in C.uo
>
> My AST says that the result is int*. (Although some indication that it
> came from an array needs to be retained so that sizeof and addrof can
> be applied later with the correct results.)

I can't comment on how you implement this -- if it works it works -- but
I'd have the type as int[10] (since that's what it is) and I'd add a
node to represent the conversion (if needed) when building the next
level up.

Of course you'd end up with the type being int * (in this case) if you
are passing the context /down/. You'd make an int[10] node if the
parser is in a "non-declaying" expression (you are parsing &... or
sizeof ... for example), and you'd make an int * node otherwise.
However, your description of saving the array type "for later" makes me
think you are not doing it this way.

> Perhaps you mean the type is int[10] unless you try and use the result
> or even just try and observe it.

No, I mean int[10]. And you can use it as an array, for example in a
sizeof expression. I know you know this but I don't know why you want
to complicate matters by denying what the type really is.

--
Ben.

Ian Collins

unread,
Sep 7, 2018, 5:52:25 PM9/7/18
to
On 08/09/18 07:53, Bart wrote:
>
> I don't anyway think it is the job of a language implementation to also
> have to provide headers for hundreds of other libraries and applications.

It isn't and none that I know do...

For standard library header, just use an appropriate third party library
if you are using an unfriendly platform that doesn't provide them.

--
Ian.

Bart

unread,
Sep 7, 2018, 8:17:16 PM9/7/18
to
On 07/09/2018 22:47, Ben Bacarisse wrote:
> Bart <b...@freeuk.com> writes:

>> You just need to know that arrays are arrays, and pointers are
>> pointers. Very different types. You index one and dereference the
>> other, with or without an offset.

> Pascal (the original Pascal) had that property, and its arrays were
> almost unusable. Algol 68 had it, but it had very powerful array
> handling.

The model I use is necessarily simple (I had a compiler for it on Z80)
but I'm not good at describing such things. Think of how C deals with
arrays, but without this business of them morphing to and from pointers
that I find objectionable.

> That looks like C to me -- the syntax, the talk of casting -- it all
> appears to be about the restricted C we had been discussing.

Both languages have the capability of manipulating pointers by adding
offsets. Both have the ability to construct genuine pointers to actual
arrays. But only C spoils it (IMO) by taking the pretence of pointers
being arrays so far (by being able to index them for example).

> Given the restricted nature of pointers, and your use of Algol 68-like
> terminology, I had always assumed you had some sort of sub-array syntax.

Slices you mean? That's always been on the cards but I've never got
round to it. My next language up has that stuff. The low-level I prefer
to keep low-level. Slices wouldn't affect that that much, but it's a
fiddly addition.


>> I also showed that I have to use syntax like A^[i], where idiomatic C
>> would end up using A[i] even it helps hide the true nature of A.
>
> The whole sub-thread has been about how the explicit syntax is an
> advantage! Why would I take your saying that you have to be explicit as
> some sort of criticism of you design?

People will be critical if you suggest a way of writing C that involves
even more punctuation than it already has.

> Anyway, if you don't like it, you can very simply get rid of it. If and
> expression E is type error in some context where E^ would not be, you
> can add an implicit indirection. A[i] would still mean A^[i] but there
> would be some syntactic sugar for it. You could write 'a + pb' rather
> than 'a + pb^' and 'ps.m' rather than 'ps^.m'.

This is stuff I've considered, but decided to keep the indirections
explicit (I don't mind them myself).

(And sometimes the ^ can be made to disappear in other ways; there's an
example below. Long ago I had auto-dereference control on pointer types.
I've dropped that, but it was rather interesting how it worked.)


Below are examples of a very simple array-scanning function in C using
T* in the first example and T(*)[] in the second (consider 'i' to be a
global):
--------------------------

int sumarray1(int* a, int n) {
int sum=0;
for (i=0; i<n; ++i)
sum += a[i];
return sum;
}

int sumarray2(int(*a)[], int n) {
int sum=0;
for (i=0; i<n; ++i)
sum += (*a)[i];
return sum;
}

int main(void){
int data[]={10,20,30,40};
int length=sizeof(data)/sizeof(data[0]);

printf("%d\n",sumarray1(data,length));
printf("%d\n",sumarray2(&data,length));
}

--------------------------
On the face of it, summarray1 looks cleaner. But you can also call it
like this:

int x;
sumarray1(&x,4);

You can't call the other in the same way:

sumarray2(&x,4); // error (or probably warning for most compilers)

Here are the same two examples in my language (using the same interface
anyway), plus a bonus one using a reference parameter. (The first does
not attempt to 'index' 'a' using (a+i)^; it will just step along it).
Note that array bounds here can be anything but default to 1:

----------------------
function sumarray1(ref int a, int n)int =
int sum:=0
to n do
sum +:= a++^
od
return sum
end

function sumarray2(ref[]int a, int n)int =
int sum:=0
for i to n do
sum +:= a^[i]
od
return sum
end

function sumarray3([]int &a, int n)int = # &a is reference parameter
int sum:=0
for i to n do
sum +:= a[i]
od
return sum
end

proc start=
[]int data = (10,20,30,40)

println sumarray1(&.data,data.len) # &.data same as &data[1]
println sumarray2(&data,data.len)
println sumarray3(data,data.len)

end

----------------------

sumarray3 is interesting, as it achieves the same clean code as C's
sumarray1, but with fewer ways of passing the wrong type: you can't pass
a pointer to an int, and you can't pass a null value (it has to be an
lvalue).

But all functions except either of the sumarray1s will have trouble
passing a slice of data without lots of fiddly casts.

(Bigger example of this language here: https://pastebin.com/5PxrSP5q;
raw: https://pastebin.com/raw/5PxrSP5q. Lovely examples of casting for
slices c. line 282. I can't find the original C but that part was likely
simpler.)

--
bart

jacobnavia

unread,
Sep 8, 2018, 2:18:35 PM9/8/18
to
Le 07/09/2018 à 21:53, Bart a écrit :

> After doing a bit more work on it, I can add one more reason for why it
> was withdrawn, probably the main one. Having to provide the full working
> set of headers that people expect, would be a life sentence.
>

And why don't you use lcc-win header files for windows?

I have done all that work already...

It is loading more messages.
0 new messages