Hello Alejandro.

Alejandro Colomar via Mutt-dev wrote in
 <aeKzrBdNqOe06shl@devuan>:
 |On 2026-04-18T00:18:38+0200, Steffen Nurpmeso wrote:
 |> Matthias Andree via Mutt-dev wrote in
 |>  <[email protected]>:
 |>|Am 17.04.26 um 05:26 schrieb Kevin J. McCarthy:
 |>|> On Fri, Apr 17, 2026 at 02:31:19AM +0200, Alejandro Colomar via 
 |>|> Mutt-dev wrote:
 |>|>> Yup, I meant how it can be done, not really an explanation of how the
 |>|>> magic works.  I guess I should have explained it, as it's really not
 |>|>> obvious.
 |>|>
 |>|> I know that explanation was for Steffen, but thank you.  I read your 
 |>|> code and explanation and links for a long while, before it started to 
 |>|> sink in.
 |>|>
 |>|> Steffen is right, it's really cool, but without your explanation I 
 |>|> never would have understood!
 |>|
 |>|The skills required to write library code and skills required to write 
 |>|application code differ by a large stretch. ;-)
 |> 
 |> Well i mean .. i am just a Boche.  (But with a refrigerator
 |> not from Bosch, and unfortunately also not from Foron, but that
 |> aside.)
 |> 
 |> I live in ISO C89 (or C99), and before, and overall mostly even in
 |
 |C89 is too difficult to write correctly.  I bet you're mostly living in
 |C99, maybe avoiding a few features of it sometimes.
 |
 |Also, I suspect you're only really dealing with POSIX (and maybe some
 |other relatively civilized) systems.  Portable ISO C is really painful
 |(one's complement allowed until C23, etc.).

I would not have gone into this detail.
But no, except for some program it really is C89, with certain
(too long) macros to be able to use C99 things like .named-init=X
and such, which are really useful.  Unfortunately initializers
must be in correct order, [1]=X,[0]=Y will not do.
(I really meant K&R with prototypes maybe.  Ie what i really meant
was "let me program what i want to do", and get off my way.)

 |You probably also take advantage of a few common extensions.  -pedantic
 |C89 is really a PITA.

We can -pedantic.

 |BTW, the C Committee that standardized C89 was just as inventive and bad
 |as more recent ones.  They broke malloc(3) et al. by permitting
 |implementations to return NULL on success, which was a terrible mistake
 |that we're dealing with now.  I don't have much sympathy for C89
 |compared to K&R C or C99, TBH.
 |<https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3752.txt>

Ok, .. as above.
I never used the standard library until i took maintainership of
that MUA, really.  (fprintf(3), but only as the last possibility
in case of debug havoc.)  We had (or could) -nostdlib (on Linux;
but needed -ldl), dtors, ctors, (ifuncs), this all not used
(-fno-rtti and all that), so it was wonderful.

 |> C++ '98, and my skills therein are so: i am alive and well.  (Even
 ...
 |> So i am not actually interested in being skilled in almost
 |> anything after ISO C99/C++98, how unprofessional that may be.
 |> But i tell you one thing.  I *loved* the wonderful and more than
 |> only respected Ken Thompson talking in the "Oral History" of the
 |> Computer Museum series, and regarding this thread in particular
 |> this short five minute snippet that is well worth watching or
 |> hearing:
 ...
 |>   $ ytdlf- 91 'https://www.youtube.com/watch?v=NTrAISNdf70'
 |
 |I never liked the Go language, FWIW.  I very much stay with C and the
 |shell.  That's as much as I need for anything.

Me too, and Go is not my thing.  But still, i am very very much
with him.  Of course my skills and also infrastructure does not
allow doing what they then have done.  Well, today one cannot even
avoid the standard libraries, it is all much too inter-dependent,
and the protocols require so many more things, and for these
things you need more support libraries, which need more support
libraries themselves, and they all use the standard libraries...
So the world became a bit sadder in that respect.
(In my opinion, of course.  And there are still people which go
the hard way, the directfb2 Linux layer for example was revived
during Corona, and saw lots of changes until not too long ago; for
example.)

 |> In my opinion the best C would be the one from Plan9.
 |
 |I have mixed opinions about Plan9 C.  The lack of 'const' is really bad.

They have const.  (Ritchie said this in the 80s right; yes,
constant data, in rodata.)

 |And there are some extensions that are dangerous.  But they have other
 |extensions that are useful, and which I want to standardize in C2y.  And
 |the string library has some really good designs but riddled with bugs:
 |strecpy(2) and seprint(2).

That hookable format thing i like.  Other than that i personally
always struggled with C-style strings and their interface.  I was
then completely reeducated by Stroustrup and objects, and i think
objects, mostly, at least.  (Of course this is in parts hard to
believe given my public open source things, but, the thing is,
you land "in the wild", and then have no infrastructure, but still
have to go, or want to, at least.  This does not come together.
And then it also all came out "somehow wrong", and *then*, you
know, it was also interesting to use plain C infrastructure.))

 |>  I do not
 |> need more, but what i need, that we do not have from ISO.
 |
 |I'm working on several things that might be quite interesting.
 |
 |There's the _Countof() operator which I added recently.  Now I want to
 |extend it to array parameters.
 |
 | int
 | func(int n, int a[n])
 | {
 |  return _Countof(a);  // equivalent to 'return n;'
 |}

I must say i was running away somewhat from the examples that
cheered "generic programming in C".  After so-and-so-many typeof()
constructs on a single line i get confused, .. which is by the way
a problem i have and had with many especially commercial C++
template-based frameworks, which turned out to be a very hard to
impossible to penetrate djungle of syntax constructs.  And i think
clarity and concise method-on-object is one of the major
advantages of original C++ (for example
g_registered_type_info_get_type_name() is half the width of an 80
column screen, and here indentation is missing, and all that), so
that not here.
I am not a fan.  In general a vector object of integers would be
an interesting thing, then.

 |And there are a few other interesting features that will come in future
 |language revisions.  But I agree there's a lot of unnecessary stuff in
 |recent standards.

I am in the "if it doesn't come naturally leave it" camp.
I think a good thing is self-describing, with only minimal
possible exceptions.
And i think the "supreme discipline" is to have this straight and
clean approach over decades.  Without getting bored, without
detoriating or micro-optimizing.

 |Things I find essential from modern dialects include static_assert(3),

And that was just unusable from ISO C when it came, for example.
It was a brainfart.  They should simply have taken the exact
variant that C++ had introduced, at first.

 |_Generic(3), and a few others.  They allow writing const-preserving
 |string APIs, for example, as we've seen in this thread.  That's an
 |important improvement over C99.

I am not of this opinion.  I would think they buy nothing.
I really tried hard to think if i ever encountered a problem in
the few years i do such things (it was always C++, with a nice
string object here, otherwise), and i could not remember one.
I think you use these functions pretty locally, and if you really
leave that place with a type-qualifier-borked memory buffer,
especially when passing it around to interfaces that you do not
control, you should not program in C.  Such things just do not
happen in C++; even if only have one for example find()
implementation internally, i can multiplex many variants on top
(string&,string&), const.., (string&,char*), const..,
(string&,char*,size_t), const.., all with one implementation, and
the callees can take appropriate steps to ensure "buffer safety".
This is why i do not like the C interface at all.

 |You may also be interested in the magic from this macro:
 |
 | #define typeas(T)         typeof((T){0})
 | #define mallocarray(...)  reallocarray(NULL, __VA_ARGS__)
 | #define malloc_T(n, T)    malloc_T_(n, typeas(T))
 | #define malloc_T_(n, T)  
 | (                                                             \
 |  (void)0,                                              \
 |  (T *){mallocarray(n, sizeof(T))}                      \
 | )
 |
 |which is used as:
 |
 | int  *p;
 |
 | p = malloc_T(42, int);
 | if (p == NULL)
 |  goto fail;
 |
 |This vastly improves the safety of malloc(3) calls compared to anything
 |you can do with the raw function.

Oh, i have similar ones but without typeof() magic.  (sizeof(),
though.  But that is very old.)  Of course, this is a longer road
to go to use them, but so it is.

 |I'll paste here some links to commits that added those macros in the
 |shadow project, which explain some of the reasons for them:
 |
 |-  <https://github.com/shadow-maint/shadow/commit/97d3c09a3d357bf4ade04b\
 |24afeb8d2e5ec6bf73>
 |-  <https://github.com/shadow-maint/shadow/commit/6e58c1275252f3314d1aa5\
 |cc4d7e7f9068e3a902>
 |-  <https://github.com/shadow-maint/shadow/commit/09775d3718df216c75b62d\
 |73bbcc5aa060e0fc94>
 |-  <https://github.com/shadow-maint/shadow/commit/0afe2169537de5459f5129\
 |d2f7af68f675c6b27a>
 |-  <https://github.com/shadow-maint/shadow/commit/9705effba5a0af3d87848b\
 |ee0a434ff4c2520aef>
 |
 |To be fair, the malloc_T() macro from above only needs C99 compound
 |literals, plus typeof.  However, there's realloc_T(), which comes with
 |even more magic, and needs C11.
 |
 | #define reallocarray_(p, n, z)  reallocarray(p, (n)?:1, (z)?:1)
 | #define realloc_T(p, n, T)      realloc_T_(p, n, typeas(T))
 | #define realloc_T_(p, n, T)                                   \
 | (                                                             \
 |  _Generic(p, T *: (void)0),                            \
 |  (T *){reallocarray_(p, n, sizeof(T))}                 \
 | )
 |
 |This guarantees that the type of the input pointer is the same as the
 |type used for the allocation and is also the same type as the type used
 |for the assignment of the return value.  This prevents an entire class
 |of stupid mistakes, and makes realloc_T() quite fool-proof.

Too late to think now, but it sounds good.
(Of course, i just go a longer, more explicit road.  But that
works with C89/C++98.)

 |-  <https://github.com/shadow-maint/shadow/commit/afc4b574b7372e72cffb69\
 |5214bc08e1a6a86a75>
 |-  <https://github.com/shadow-maint/shadow/commit/26c9dd37157353092e8709\
 |255a6a601915057c05>
 |
 |>  (In C++
 |> maybe, but then i never liked most aspects of this, there.)
 |> 
 |>   http://www.rundkuehlschrank.de/
 |>   https://www.mdr.de/geschichte/ddr/wirtschaft/ddr-elektrogeraete-funktio\
 |>   nieren-laenger-langlebig-garantie-gesetzliche-zuverlaessigkeit-100.html
 |> 
 |> Btw i already saw Alejandro's message (i am still digesting), and
 |> now that we talk compiling C with C++, as you do (if i recall
 |> correctly)
 |
 |I don't.  I used C++ in the past because I needed to interact with some
 |library written only in C++, but I hate that language, find it terrible,
 |and don't need that library anymore, so I don't use any C++ anymore.
 |Compiling C as C++ is usually a bad idea, IMO.

I love C++.  Or loved it, i would love to have a C-with-objects.
Ie "current ISO C" plus certain extensions.

 |> and i do, too, and then, there was a {0} "pointer", was
 |> it.
 |
 |Yeah, that's a compound literal, which doesn't exist in C++, so this
 |can't be used in C++ mode.  (This reminds me that I need to wrap the
 |musl code in !defined(__cplusplus).)  That's a bad language; I recommend
 |not using it: if you want the good things from C++, you need to use
 |modern C++, which strays away from C too much.  If you stay with the old

Definetely not :)

 |C++, what you get is some terrible dialect that doesn't get the benefits
 |of modern C nor of modern C++.
 |
 |Have a lovely night!

You too, dear Alejandro.

--steffen
|
|Der Kragenbaer,                The moon bear,
|der holt sich munter           he cheerfully and one by one
|einen nach dem anderen runter  wa.ks himself off
|(By Robert Gernhardt)

Reply via email to