Discussion:
C23 thoughts and opinions
(too old to reply)
David Brown
2024-05-22 16:55:36 UTC
Permalink
In an attempt to bring some topicality to the group, has anyone started
using, or considering, C23 ? There's quite a lot of change in it,
especially compared to the minor changes in C17.

<https://open-std.org/JTC1/SC22/WG14/www/docs/n3220.pdf>
<https://en.wikipedia.org/wiki/C23_(C_standard_revision)>
<https://en.cppreference.com/w/c/23>

I like that it tidies up a lot of old stuff - it is neater to have
things like "bool", "static_assert", etc., as part of the language
rather than needing a half-dozen includes for such basic stuff.

I like that it standardises a several useful extensions that have been
in gcc and clang (and possibly other compilers) for many years.

I'm not sure it will make a big difference to my own programming - when
I want "typeof" or "chk_add()", I already use them in gcc. But for
people restricted to standard C, there's more new to enjoy. And I
prefer to use standard syntax when possible.

"constexpr" is something I think I will find helpful, in at least some
circumstances.
Thiago Adams
2024-05-22 17:42:58 UTC
Permalink
Post by David Brown
In an attempt to bring some topicality to the group, has anyone started
using, or considering, C23 ?  There's quite a lot of change in it,
especially compared to the minor changes in C17.
<https://open-std.org/JTC1/SC22/WG14/www/docs/n3220.pdf>
<https://en.wikipedia.org/wiki/C23_(C_standard_revision)>
<https://en.cppreference.com/w/c/23>
I like that it tidies up a lot of old stuff - it is neater to have
things like "bool", "static_assert", etc., as part of the language
rather than needing a half-dozen includes for such basic stuff.
I like that it standardises a several useful extensions that have been
in gcc and clang (and possibly other compilers) for many years.
I'm not sure it will make a big difference to my own programming - when
I want "typeof" or "chk_add()", I already use them in gcc.  But for
people restricted to standard C, there's more new to enjoy.  And I
prefer to use standard syntax when possible.
"constexpr" is something I think I will find helpful, in at least some
circumstances.
I am waiting MSVC support. There are a lot of simple features MSVC could
implement and deliver in small increments. But it is very slow.

I am would use today if I had.

- #warning
- [[nodiscard]]
- typeof
- digit separators
- bool true, false

I am not planning to use:

- enum with specific types.
- #elifdef
- nullptr
- auto
- constexpr

Not sure
- empty initializer
David Brown
2024-05-22 20:11:44 UTC
Permalink
Post by Thiago Adams
Post by David Brown
In an attempt to bring some topicality to the group, has anyone
started using, or considering, C23 ?  There's quite a lot of change in
it, especially compared to the minor changes in C17.
<https://open-std.org/JTC1/SC22/WG14/www/docs/n3220.pdf>
<https://en.wikipedia.org/wiki/C23_(C_standard_revision)>
<https://en.cppreference.com/w/c/23>
I like that it tidies up a lot of old stuff - it is neater to have
things like "bool", "static_assert", etc., as part of the language
rather than needing a half-dozen includes for such basic stuff.
I like that it standardises a several useful extensions that have been
in gcc and clang (and possibly other compilers) for many years.
I'm not sure it will make a big difference to my own programming -
when I want "typeof" or "chk_add()", I already use them in gcc.  But
for people restricted to standard C, there's more new to enjoy.  And I
prefer to use standard syntax when possible.
"constexpr" is something I think I will find helpful, in at least some
circumstances.
I am waiting MSVC support. There are a lot of simple features MSVC could
implement and deliver in small increments. But it is very slow.
MSVC is primarily a C++ compiler - the C support is more of a leftover
from the previous century, with a few post-C90 features as an
afterthought. Surely for C development on Windows, rather than C++,
you'd look for something better?
Post by Thiago Adams
I am would use today if I had.
 - #warning
 - [[nodiscard]]
 - typeof
 - digit separators
 - bool true, false
I use these today in C, except the digit separators (I use them in C++).
But as I say, it's nice to see them as standard rather than just
common extensions.
Post by Thiago Adams
 - enum with specific types.
I haven't found a use for these in C++, and I'm not sure I'll need them
in C either. I sometimes have ordinary enum types in bitfields for
specific sizes.
Post by Thiago Adams
 - #elifdef
The will slightly neaten some of my pre-processor handling. My strong
preference for preprocessor symbols for conditional compilation and the
like is to have symbols that are always defined, but to different
values, and use "#if" checks rather than "#ifdef" - when combined with
gcc warnings, it makes it far easier to catch spelling mistakes, and it
makes it easy to jump in the code to where the symbol is defined. But
#ifdef checks do turn up, and this will give marginally neater code.
Post by Thiago Adams
 - nullptr
I am fond of nullptr in C++, and will use it in C. Like most of the C23
changes, it's not a big issue - after all, you get a lot of the same
effect with "#define nullptr (void*)(0)" or similar. But it means your
code has a visual distinction between the integer 0 and a null pointer,
and also lets the compiler or other static checking system check better
than using NULL would. (And I don't like NULL - I dislike all-caps
identifiers in general.)
Post by Thiago Adams
 - auto
I use that occasionally in gcc, as __auto_type. It can be helpful in
macros. I might use it more when it is standardised. (I use auto in
C++ a bit more often.)
Post by Thiago Adams
 - constexpr
I will definitely use that. Sometimes I want a constant expression for
things like array sizes or static initialisers, and want to calculate
it. constexpr gives you that without having to resort to macros. (I'd
perhaps be even happier if I could just use const, as I can in C++.)
Post by Thiago Adams
Not sure
 - empty initializer
I don't see that one being a big hit, at least for me. But I see little
benefit in /not/ allowing it in the language, so it seems a sensible
addition.
Thiago Adams
2024-05-22 20:26:20 UTC
Permalink
Post by David Brown
Post by Thiago Adams
Post by David Brown
In an attempt to bring some topicality to the group, has anyone
started using, or considering, C23 ?  There's quite a lot of change
in it, especially compared to the minor changes in C17.
<https://open-std.org/JTC1/SC22/WG14/www/docs/n3220.pdf>
<https://en.wikipedia.org/wiki/C23_(C_standard_revision)>
<https://en.cppreference.com/w/c/23>
I like that it tidies up a lot of old stuff - it is neater to have
things like "bool", "static_assert", etc., as part of the language
rather than needing a half-dozen includes for such basic stuff.
I like that it standardises a several useful extensions that have
been in gcc and clang (and possibly other compilers) for many years.
I'm not sure it will make a big difference to my own programming -
when I want "typeof" or "chk_add()", I already use them in gcc.  But
for people restricted to standard C, there's more new to enjoy.  And
I prefer to use standard syntax when possible.
"constexpr" is something I think I will find helpful, in at least
some circumstances.
I am waiting MSVC support. There are a lot of simple features MSVC
could implement and deliver in small increments. But it is very slow.
MSVC is primarily a C++ compiler - the C support is more of a leftover
from the previous century, with a few post-C90 features as an
afterthought.  Surely for C development on Windows, rather than C++,
you'd look for something better?
MSVC has support for C11 features like _Generic etc.. even threads.
I am used to MSVC. For some type of programs that needs resources or a
lot of windows api I think MSVC is a good choice.
Post by David Brown
Post by Thiago Adams
I am would use today if I had.
  - #warning
  - [[nodiscard]]
  - typeof
  - digit separators
  - bool true, false
I use these today in C, except the digit separators (I use them in C++).
 But as I say, it's nice to see them as standard rather than just
common extensions.
Post by Thiago Adams
  - enum with specific types.
I haven't found a use for these in C++, and I'm not sure I'll need them
in C either.  I sometimes have ordinary enum types in bitfields for
specific sizes.
Post by Thiago Adams
  - #elifdef
The will slightly neaten some of my pre-processor handling.  My strong
preference for preprocessor symbols for conditional compilation and the
like is to have symbols that are always defined, but to different
values, and use "#if" checks rather than "#ifdef" - when combined with
gcc warnings, it makes it far easier to catch spelling mistakes, and it
makes it easy to jump in the code to where the symbol is defined.  But
#ifdef checks do turn up, and this will give marginally neater code.
Post by Thiago Adams
  - nullptr
I am fond of nullptr in C++, and will use it in C.  Like most of the C23
changes, it's not a big issue - after all, you get a lot of the same
effect with "#define nullptr (void*)(0)" or similar.  But it means your
code has a visual distinction between the integer 0 and a null pointer,
and also lets the compiler or other static checking system check better
than using NULL would.  (And I don't like NULL - I dislike all-caps
identifiers in general.)
Post by Thiago Adams
  - auto
I use that occasionally in gcc, as __auto_type.  It can be helpful in
macros.  I might use it more when it is standardised.  (I use auto in
C++ a bit more often.)
Post by Thiago Adams
  - constexpr
I will definitely use that.  Sometimes I want a constant expression for
things like array sizes or static initialisers, and want to calculate
it.  constexpr gives you that without having to resort to macros.  (I'd
perhaps be even happier if I could just use const, as I can in C++.)
I am curious for that. Do you have a sample?
Post by David Brown
Post by Thiago Adams
Not sure
  - empty initializer
I don't see that one being a big hit, at least for me.  But I see little
benefit in /not/ allowing it in the language, so it seems a sensible
addition.
This is what I use
struct X x = {0};
But I can do a find-replace and change everything to {}

When I create samples, I use new feature like nullptr and {}.
The problem I see is to use these features in real code, and create a
mess of styles.
David Brown
2024-05-23 12:17:53 UTC
Permalink
Post by Thiago Adams
Post by David Brown
Post by Thiago Adams
Post by David Brown
In an attempt to bring some topicality to the group, has anyone
started using, or considering, C23 ?  There's quite a lot of change
in it, especially compared to the minor changes in C17.
<https://open-std.org/JTC1/SC22/WG14/www/docs/n3220.pdf>
<https://en.wikipedia.org/wiki/C23_(C_standard_revision)>
<https://en.cppreference.com/w/c/23>
  - constexpr
I will definitely use that.  Sometimes I want a constant expression
for things like array sizes or static initialisers, and want to
calculate it.  constexpr gives you that without having to resort to
macros.  (I'd perhaps be even happier if I could just use const, as I
can in C++.)
I am curious for that. Do you have a sample?
If I try to be precise about the terms "constant expression", "integer
constant expression", etc., I suspect I will get the details wrong
unless I spend a lot of time checking carefully. So I hope it is good
enough for me to be a bit lazy and quote the error messages from gcc
(with "-std=c23 -Wpedantic").


With this code, compilation fails "initialiser element is not a
constant" for y.

int x = 100;
int y = x / 20;
int zs[y];


With this code, compilation fails because the zs is actually a VLA, and
"variably modified 'zs' at file scope" is not allowed.

const int x = 100;
const int y = x / 20;
int zs[y];


This code, however, is fine:

constexpr int x = 100;
constexpr int y = x / 20;
int zs[y];


This also works, even for older standards:

enum { x = 100 };
enum { y = x / 20 };
int zs[y];


But constexpr works for other types, not just "int" which is the type of
all enumeration constants. (And "enum" constants are a somewhat weird
way to get this effect - "constexpr" looks neater.)

And in general, I like to be able to say, to the compiler and to people
reading the code, "this thing is really fixed and constant, and stop
compiling if you think I am wrong" rather than just "I promise I won't
change this thing - or if I do, I don't mind the nasal daemons".
Post by Thiago Adams
Post by David Brown
Post by Thiago Adams
Not sure
  - empty initializer
I don't see that one being a big hit, at least for me.  But I see
little benefit in /not/ allowing it in the language, so it seems a
sensible addition.
This is what I use
struct X x = {0};
But I can do a find-replace and change everything to {}
You could, but I don't really see the point of such a change. But in
new code it would be fine to write "= {}" rather than "= { 0 }".
Post by Thiago Adams
When I create samples, I use new feature like nullptr and {}.
The problem I see is to use these features in real code, and create a
mess of styles.
I think you need significant motivation to justify changing style in
existing code, and I don't see anything here that would make me want to
change existing C17 code to C23 code. But when writing new code, I'd
use the new features.
Thiago Adams
2024-05-23 12:38:10 UTC
Permalink
Post by David Brown
If I try to be precise about the terms "constant expression", "integer
constant expression", etc., I suspect I will get the details wrong
unless I spend a lot of time checking carefully.  So I hope it is good
enough for me to be a bit lazy and quote the error messages from gcc
(with "-std=c23 -Wpedantic").
With this code, compilation fails "initialiser element is not a
constant" for y.
    int x = 100;
    int y = x / 20;
    int zs[y];
With this code, compilation fails because the zs is actually a VLA, and
"variably modified 'zs' at file scope" is not allowed.
    const int x = 100;
    const int y = x / 20;
    int zs[y];
    constexpr int x = 100;
    constexpr int y = x / 20;
    int zs[y];
    enum { x = 100 };
    enum { y = x / 20 };
    int zs[y];
But constexpr works for other types, not just "int" which is the type of
all enumeration constants.  (And "enum" constants are a somewhat weird
way to get this effect - "constexpr" looks neater.)
And in general, I like to be able to say, to the compiler and to people
reading the code, "this thing is really fixed and constant, and stop
compiling if you think I am wrong" rather than just "I promise I won't
change this thing - or if I do, I don't mind the nasal daemons".
We can write:

#define X 100
#define Y ((X) / 20)
int zs[Y];

I cannot see a good justification for constexpr.
I already see bad usages of constexpr in C++ code. It was used in cases
where we know for sure that is NOT compile time. This just make review
harder "why did someone put this here?" conclusion was it was totally
unnecessary and ignored by the compiler. The programmer was trying to
add something extra, like "magic" hoping for something that would never
happen.
David Brown
2024-05-23 13:11:45 UTC
Permalink
Post by Thiago Adams
Post by David Brown
If I try to be precise about the terms "constant expression", "integer
constant expression", etc., I suspect I will get the details wrong
unless I spend a lot of time checking carefully.  So I hope it is good
enough for me to be a bit lazy and quote the error messages from gcc
(with "-std=c23 -Wpedantic").
With this code, compilation fails "initialiser element is not a
constant" for y.
     int x = 100;
     int y = x / 20;
     int zs[y];
With this code, compilation fails because the zs is actually a VLA,
and "variably modified 'zs' at file scope" is not allowed.
     const int x = 100;
     const int y = x / 20;
     int zs[y];
     constexpr int x = 100;
     constexpr int y = x / 20;
     int zs[y];
     enum { x = 100 };
     enum { y = x / 20 };
     int zs[y];
But constexpr works for other types, not just "int" which is the type
of all enumeration constants.  (And "enum" constants are a somewhat
weird way to get this effect - "constexpr" looks neater.)
And in general, I like to be able to say, to the compiler and to
people reading the code, "this thing is really fixed and constant, and
stop compiling if you think I am wrong" rather than just "I promise I
won't change this thing - or if I do, I don't mind the nasal daemons".
#define X 100
#define Y ((X) / 20)
int zs[Y];
I cannot see a good justification for constexpr.
Clearer code, better checking along the way, better typing. I don't
think constexpr lets you do things you couldn't do before, but it lets
you do those things in a neater way. (IMHO.)
Post by Thiago Adams
I already see bad usages of constexpr in C++ code. It was used in cases
where we know for sure that is NOT compile time. This just make review
harder "why did someone put this here?" conclusion was it was totally
unnecessary and ignored by the compiler. The programmer was trying to
add something extra, like "magic" hoping for something that would never
happen.
IME poor or confusing uses of "constexpr" are for functions, not
objects, and C23 does not support "constexpr" for functions.

I think it is better to think of constexpr functions in C++ as "pure"
functions - confusingly called __attribute__((const)) functions in gcc
and [[unsequenced] functions in C23. That is, functions that don't
affect anything around them, are not affected by anything external, have
no side effects, always give the same results for the same parameters,
and can be called more or fewer times without affecting the program's
observable behaviour. (It's not exactly like that in C++ - a
"constexpr" function is implicitly inline and needs a local definition.
But I think that is how it could have been handled.)

The whole thing - in C and C++ - suffers somewhat from being addons over
time rather than part of the original design of the language. But
that's inevitable as an old language evolves.
Thiago Adams
2024-05-23 16:21:45 UTC
Permalink
Post by Thiago Adams
Post by David Brown
If I try to be precise about the terms "constant expression",
"integer constant expression", etc., I suspect I will get the details
wrong unless I spend a lot of time checking carefully.  So I hope it
is good enough for me to be a bit lazy and quote the error messages
from gcc (with "-std=c23 -Wpedantic").
With this code, compilation fails "initialiser element is not a
constant" for y.
     int x = 100;
     int y = x / 20;
     int zs[y];
With this code, compilation fails because the zs is actually a VLA,
and "variably modified 'zs' at file scope" is not allowed.
     const int x = 100;
     const int y = x / 20;
     int zs[y];
     constexpr int x = 100;
     constexpr int y = x / 20;
     int zs[y];
     enum { x = 100 };
     enum { y = x / 20 };
     int zs[y];
But constexpr works for other types, not just "int" which is the type
of all enumeration constants.  (And "enum" constants are a somewhat
weird way to get this effect - "constexpr" looks neater.)
And in general, I like to be able to say, to the compiler and to
people reading the code, "this thing is really fixed and constant,
and stop compiling if you think I am wrong" rather than just "I
promise I won't change this thing - or if I do, I don't mind the
nasal daemons".
#define X 100
#define Y ((X) / 20)
int zs[Y];
I cannot see a good justification for constexpr.
Clearer code, better checking along the way, better typing.  I don't
think constexpr lets you do things you couldn't do before, but it lets
you do those things in a neater way.  (IMHO.)
Post by Thiago Adams
I already see bad usages of constexpr in C++ code. It was used in
cases where we know for sure that is NOT compile time. This just make
review harder "why did someone put this here?" conclusion was it was
totally unnecessary and ignored by the compiler. The programmer was
trying to add something extra, like "magic" hoping for something that
would never happen.
IME poor or confusing uses of "constexpr" are for functions, not
objects, and C23 does not support "constexpr" for functions.
The sample C++ was something like

constexpr char * s[] = {"a", "b"};
for (int i = 0; i < sizeof(s); i++)
{
//using s[i]
}

I checked in C, it is an error.

error: 'constexpr' pointer initializer is not null
5 | constexpr char * s[] = {"a", "b"};


Then we were asking why constexpr was used in that case.
Keith Thompson
2024-05-23 21:49:24 UTC
Permalink
[...]
Post by Thiago Adams
Post by David Brown
Post by Thiago Adams
I already see bad usages of constexpr in C++ code. It was used in
cases where we know for sure that is NOT compile time. This just
make review harder "why did someone put this here?" conclusion was
it was totally unnecessary and ignored by the compiler. The
programmer was trying to add something extra, like "magic" hoping
for something that would never happen.
IME poor or confusing uses of "constexpr" are for functions, not
objects, and C23 does not support "constexpr" for functions.
The sample C++ was something like
constexpr char * s[] = {"a", "b"};
for (int i = 0; i < sizeof(s); i++)
{
//using s[i]
}
I checked in C, it is an error.
Apparently C23 has stricter rules for constexpr than C++ does. I can
imagine those rules being relaxed in future editions of the C standard.
Post by Thiago Adams
error: 'constexpr' pointer initializer is not null
5 | constexpr char * s[] = {"a", "b"};
Then we were asking why constexpr was used in that case.
Why not?

When I compile that with g++ 14.1.0, with -std=c++23, I get:

warning: ISO C++ forbids converting a string constant to ‘char*’ [-Wwrite-strings]

When I replace "char*" by "const char*", that warning goes away.

The initializer *can* be evaluated at compile time. Using constexpr
catches any changes to the initializer that would require run-time code
execution.

Feel free to redirect to comp.lang.c++ if you want to discuss the C++
aspects further.
--
Keith Thompson (The_Other_Keith) Keith.S.Thompson+***@gmail.com
void Void(void) { Void(); } /* The recursive call of the void */
David Brown
2024-05-24 09:03:43 UTC
Permalink
Post by Keith Thompson
[...]
Post by Thiago Adams
Post by David Brown
Post by Thiago Adams
I already see bad usages of constexpr in C++ code. It was used in
cases where we know for sure that is NOT compile time. This just
make review harder "why did someone put this here?" conclusion was
it was totally unnecessary and ignored by the compiler. The
programmer was trying to add something extra, like "magic" hoping
for something that would never happen.
IME poor or confusing uses of "constexpr" are for functions, not
objects, and C23 does not support "constexpr" for functions.
The sample C++ was something like
constexpr char * s[] = {"a", "b"};
for (int i = 0; i < sizeof(s); i++)
{
//using s[i]
}
I checked in C, it is an error.
Apparently C23 has stricter rules for constexpr than C++ does. I can
imagine those rules being relaxed in future editions of the C standard.
From the proposal for "constexpr" in C23,
<https://open-std.org/JTC1/SC22/WG14/www/docs/n3018.htm>, it says:

"""
There are some restrictions on the type of an object that can be
declared with constexpr storage duration. There is a limited number of
constructs that are not allowed:

pointer types:
allowing these to use non-trivial addresses would delay the
deduction of the concrete value from translation to link-time. For most
of the use cases, such a feature can already be coded by using a static
and const qualified pointer object, we don’t need constexpr for that.
Therefore we only allow pointer types if the initializer value is null.
"""


I'm not sure (and haven't looked at all the discussions involved, so I
could be completely wrong), but I think there is concern that constexpr
pointers, other than null pointers, might need more features in the
linker than C currently requires. C++ already has more demands of
linkers to handle things like inline variables and statics in templates.
Thiago Adams
2024-05-24 17:17:30 UTC
Permalink
Post by Keith Thompson
Post by Thiago Adams
error: 'constexpr' pointer initializer is not null
5 | constexpr char * s[] = {"a", "b"};
Then we were asking why constexpr was used in that case.
Why not?
When I see a constexpr I ask if the compiler is able to compute
everything at compile time. If not immediately it is a bad usage in my view.
Keith Thompson
2024-05-24 19:45:58 UTC
Permalink
Post by Thiago Adams
Post by Keith Thompson
Post by Thiago Adams
error: 'constexpr' pointer initializer is not null
5 | constexpr char * s[] = {"a", "b"};
Then we were asking why constexpr was used in that case.
Why not?
When I see a constexpr I ask if the compiler is able to compute
everything at compile time. If not immediately it is a bad usage in my view.
I don't understand. Do you object because it's not *immediately
obvious* that everthing can be computed at compile time? If so, why
should it have to be?

If nothing else, the fact that the code compiles should be proof enough.
You said something upthread about the compiler ignoring constexpr if the
expression isn't compile-time evaluable; perhaps you were using a buggy
compiler?

Note that the above code is C++, not C, and that it should be "constexpr
const char *".
--
Keith Thompson (The_Other_Keith) Keith.S.Thompson+***@gmail.com
void Void(void) { Void(); } /* The recursive call of the void */
Thiago Adams
2024-05-24 20:06:02 UTC
Permalink
Post by Keith Thompson
Post by Thiago Adams
Post by Keith Thompson
Post by Thiago Adams
error: 'constexpr' pointer initializer is not null
5 | constexpr char * s[] = {"a", "b"};
Then we were asking why constexpr was used in that case.
Why not?
When I see a constexpr I ask if the compiler is able to compute
everything at compile time. If not immediately it is a bad usage in my view.
I don't understand. Do you object because it's not *immediately
obvious* that everthing can be computed at compile time? If so, why
should it have to be?
My understanding is that constexpr is a tip for the compiler. Does not
ensure anything. Unless you use where constant expression is required.
So I don't like to see constexpr where I know it is not a constant
expression.

C++ has consteval (https://en.cppreference.com/w/cpp/language/consteval)
. When I first saw it I thought it was a joke.

"every potentially-evaluated call to the function must (directly or
indirectly) produce a compile time constant expression. "

Also constinit
https://en.cppreference.com/w/cpp/language/constinit

I never used these features I want never want to use. It is a total mess
in my opinion.
Post by Keith Thompson
If nothing else, the fact that the code compiles should be proof enough.
You said something upthread about the compiler ignoring constexpr if the
expression isn't compile-time evaluable; perhaps you were using a buggy
compiler?
Note that the above code is C++, not C, and that it should be "constexpr
const char *".
I may have to implement constexpr in cake... So at some point I will see
all details.
Keith Thompson
2024-05-24 20:19:16 UTC
Permalink
Post by Thiago Adams
Post by Keith Thompson
Post by Thiago Adams
Post by Keith Thompson
Post by Thiago Adams
error: 'constexpr' pointer initializer is not null
5 | constexpr char * s[] = {"a", "b"};
Then we were asking why constexpr was used in that case.
Why not?
When I see a constexpr I ask if the compiler is able to compute
everything at compile time. If not immediately it is a bad usage in my view.
I don't understand. Do you object because it's not *immediately
obvious* that everthing can be computed at compile time? If so, why
should it have to be?
My understanding is that constexpr is a tip for the compiler. Does not
ensure anything. Unless you use where constant expression is required.
So I don't like to see constexpr where I know it is not a constant
expression.
Your understanding is incorrect. "constexpr" is not a mere hint.

In C23, an object defined with "constexpr" must be initialized with a
constant expression. C++ has similar rules. Attempting to initialize
it with a non-constant expression requires a diagnostic.

int non_const = 42;
constexpr int bad = non_const; // invalid, must be diagnosed

I'd be surprised to see a C or C++ compiler that failed to diagnose such
an error. Have you run into this, or are you speculating?

Given that the C++ code

constexpr const char * s[] = {"a", "b"}; // I added "const"

compiles successfully, you can safely assume that the initializer can be
and is evaluated at compile time.
Post by Thiago Adams
C++ has consteval
(https://en.cppreference.com/w/cpp/language/consteval) . When I first
saw it I thought it was a joke.
And I know just the newsgroup where it can be discussed.

[...]
--
Keith Thompson (The_Other_Keith) Keith.S.Thompson+***@gmail.com
void Void(void) { Void(); } /* The recursive call of the void */
Scott Lurndal
2024-05-23 17:08:03 UTC
Permalink
Post by Thiago Adams
Post by David Brown
If I try to be precise about the terms "constant expression", "integer
constant expression", etc., I suspect I will get the details wrong
unless I spend a lot of time checking carefully.  So I hope it is good
enough for me to be a bit lazy and quote the error messages from gcc
(with "-std=c23 -Wpedantic").
With this code, compilation fails "initialiser element is not a
constant" for y.
    int x = 100;
    int y = x / 20;
    int zs[y];
With this code, compilation fails because the zs is actually a VLA, and
"variably modified 'zs' at file scope" is not allowed.
    const int x = 100;
    const int y = x / 20;
    int zs[y];
    constexpr int x = 100;
    constexpr int y = x / 20;
    int zs[y];
    enum { x = 100 };
    enum { y = x / 20 };
    int zs[y];
But constexpr works for other types, not just "int" which is the type of
all enumeration constants.  (And "enum" constants are a somewhat weird
way to get this effect - "constexpr" looks neater.)
And in general, I like to be able to say, to the compiler and to people
reading the code, "this thing is really fixed and constant, and stop
compiling if you think I am wrong" rather than just "I promise I won't
change this thing - or if I do, I don't mind the nasal daemons".
#define X 100
#define Y ((X) / 20)
Neither of which convey type information.
Post by Thiago Adams
int zs[Y];
I cannot see a good justification for constexpr.
Which does convey type information, and thus would
be superior to untyped macro definitions.
Thiago Adams
2024-05-23 19:06:05 UTC
Permalink
Post by Scott Lurndal
Post by Thiago Adams
Post by David Brown
If I try to be precise about the terms "constant expression", "integer
constant expression", etc., I suspect I will get the details wrong
unless I spend a lot of time checking carefully.  So I hope it is good
enough for me to be a bit lazy and quote the error messages from gcc
(with "-std=c23 -Wpedantic").
With this code, compilation fails "initialiser element is not a
constant" for y.
    int x = 100;
    int y = x / 20;
    int zs[y];
With this code, compilation fails because the zs is actually a VLA, and
"variably modified 'zs' at file scope" is not allowed.
    const int x = 100;
    const int y = x / 20;
    int zs[y];
    constexpr int x = 100;
    constexpr int y = x / 20;
    int zs[y];
    enum { x = 100 };
    enum { y = x / 20 };
    int zs[y];
But constexpr works for other types, not just "int" which is the type of
all enumeration constants.  (And "enum" constants are a somewhat weird
way to get this effect - "constexpr" looks neater.)
And in general, I like to be able to say, to the compiler and to people
reading the code, "this thing is really fixed and constant, and stop
compiling if you think I am wrong" rather than just "I promise I won't
change this thing - or if I do, I don't mind the nasal daemons".
#define X 100
#define Y ((X) / 20)
Neither of which convey type information.
Post by Thiago Adams
int zs[Y];
I cannot see a good justification for constexpr.
Which does convey type information, and thus would
be superior to untyped macro definitions.
If you want a type just but a cast or suffix if applicable.
Keith Thompson
2024-05-22 22:53:41 UTC
Permalink
[...]
Post by David Brown
Post by Thiago Adams
 - nullptr
I am fond of nullptr in C++, and will use it in C. Like most of the
C23 changes, it's not a big issue - after all, you get a lot of the
same effect with "#define nullptr (void*)(0)" or similar. But it
means your code has a visual distinction between the integer 0 and a
null pointer, and also lets the compiler or other static checking
system check better than using NULL would. (And I don't like NULL - I
dislike all-caps identifiers in general.)
Quibble: That should be

#define nullptr ((void*)0)

For example, this doesn't produce a syntax error for `sizeof nullptr`.

Better:

#if __STDC_VERSION__ < 202311L
#define nullptr ((void*)0)
#endif

C23's nullptr is of type nullptr_t, not void*. But you'd probably have
to go out of your way for that to be an issue (e.g., using nullptr in a
generic selection).

[...]
Post by David Brown
Post by Thiago Adams
 - constexpr
I will definitely use that. Sometimes I want a constant expression
for things like array sizes or static initialisers, and want to
calculate it. constexpr gives you that without having to resort to
macros. (I'd perhaps be even happier if I could just use const, as I
can in C++.)
But const doesn't mean constant. It means read-only.
`const int r = rand();` is perfectly valid.

I dislike the C++ hack of making N a constant expression given
`const int N = 42;`; constexpr made that unnecessary. C23 makes the
same (IMHO) mistake.

If I had a time machine, I'd spell "const" as "readonly" and make
"const" mean what "constexpr" now means (evaluated at compile time).

[...]
--
Keith Thompson (The_Other_Keith) Keith.S.Thompson+***@gmail.com
void Void(void) { Void(); } /* The recursive call of the void */
Thiago Adams
2024-05-23 01:21:09 UTC
Permalink
Post by Keith Thompson
[...]
Post by David Brown
Post by Thiago Adams
 - nullptr
I am fond of nullptr in C++, and will use it in C. Like most of the
C23 changes, it's not a big issue - after all, you get a lot of the
same effect with "#define nullptr (void*)(0)" or similar. But it
means your code has a visual distinction between the integer 0 and a
null pointer, and also lets the compiler or other static checking
system check better than using NULL would. (And I don't like NULL - I
dislike all-caps identifiers in general.)
Quibble: That should be
#define nullptr ((void*)0)
For example, this doesn't produce a syntax error for `sizeof nullptr`.
#if __STDC_VERSION__ < 202311L
#define nullptr ((void*)0)
#endif
C23's nullptr is of type nullptr_t, not void*. But you'd probably have
to go out of your way for that to be an issue (e.g., using nullptr in a
generic selection).
[...]
Post by David Brown
Post by Thiago Adams
 - constexpr
I will definitely use that. Sometimes I want a constant expression
for things like array sizes or static initialisers, and want to
calculate it. constexpr gives you that without having to resort to
macros. (I'd perhaps be even happier if I could just use const, as I
can in C++.)
But const doesn't mean constant. It means read-only.
`const int r = rand();` is perfectly valid.
I dislike the C++ hack of making N a constant expression given
`const int N = 42;`; constexpr made that unnecessary. C23 makes the
same (IMHO) mistake.
If I had a time machine, I'd spell "const" as "readonly" and make
"const" mean what "constexpr" now means (evaluated at compile time).
[...]
Everything is a mess: const in C++, the differences from const in C,
etc. constexpr in C23 just makes the mess bigger.

auto is a mess as well not well specified for pointer. not sure if we
had this topic here, but auto * p in C is not specified.

I would remove from C23
- nullptr
-auto
-constexpr
-embed

I like the idea of embed but there is no implementation in production so
this is crazy!
bart
2024-05-23 12:11:16 UTC
Permalink
Post by Thiago Adams
But const doesn't mean constant.  It means read-only.
`const int r = rand();` is perfectly valid.
I dislike the C++ hack of making N a constant expression given
`const int N = 42;`; constexpr made that unnecessary.  C23 makes the
same (IMHO) mistake.
If I had a time machine, I'd spell "const" as "readonly" and make
"const" mean what "constexpr" now means (evaluated at compile time).
[...]
Everything is a mess: const in C++, the differences from const in C,
etc. constexpr in C23 just makes the mess bigger.
auto is a mess as well not well specified for pointer. not sure if we
had this topic here, but auto * p in C is not specified.
I would remove from C23
- nullptr
-auto
-constexpr
-embed
I like the idea of embed but there is no implementation in production so
this is crazy!
'embed' was discussed a few months ago. I disagreed with the poor way it
was to be implemented: 'embed' notionally generates a list of
comma-separated numbers as tokens, where you have to take care of any
trailing zero yourself if needed. It would also be hopelessly
inefficient if actually implemented like that.

I compared it to the scheme in my own language, which could import text
files, but binary ones didn't really work.

Since then embedding has been considerably improved, so that it works
like this:

[]char str = sinclude("hello.c")
[]byte data = binclude("hello.exe")

The file-embedding is done by sinclude or binclude. The former adds a
zero terminator to the embedded file data (expected to be a text file),
otherwise they are the same.

binclude can initialise any kind of array, including a 2D array of any
element type, although the data in the file needs to be suitable.

C23's 'embed' was claimed to be more flexible, as you can have
consecutive 'embed' directives initialising the same array. I can do the
same:

[]byte file = binclude("hello.exe") + binclude("/cx/big/sql.exe")

proc main=
println file.len
end

This generates an executable of 1077248 bytes, and displays 1050112 when
run, the combined size of those two embedded binaries. Compiling this
took 50ms.

("+" here is a compile-time operator that can concatenate constant
strings or also binary data like this.)

Basically, you are right that the ad hoc features of C23 are messy.

I suspect that ones like 'embed' have been derived from C++ which always
likes to make things too wide-ranging and much harder to use and
implement than necessary.
David Brown
2024-05-23 13:25:43 UTC
Permalink
Post by bart
Post by Thiago Adams
But const doesn't mean constant.  It means read-only.
`const int r = rand();` is perfectly valid.
I dislike the C++ hack of making N a constant expression given
`const int N = 42;`; constexpr made that unnecessary.  C23 makes the
same (IMHO) mistake.
If I had a time machine, I'd spell "const" as "readonly" and make
"const" mean what "constexpr" now means (evaluated at compile time).
[...]
Everything is a mess: const in C++, the differences from const in C,
etc. constexpr in C23 just makes the mess bigger.
auto is a mess as well not well specified for pointer. not sure if we
had this topic here, but auto * p in C is not specified.
I would remove from C23
- nullptr
-auto
-constexpr
-embed
I like the idea of embed but there is no implementation in production
so this is crazy!
'embed' was discussed a few months ago. I disagreed with the poor way it
was to be implemented: 'embed' notionally generates a list of
comma-separated numbers as tokens, where you have to take care of any
trailing zero yourself if needed. It would also be hopelessly
inefficient if actually implemented like that.
Fortunately, it is /not/ actually implemented like that - it is only
implemented "as if" it were like that. Real prototype implementations
(for gcc and clang - I don't know about other tools) are extremely
efficient at handling #embed. And the comma-separated numbers can be
more flexible in less common use-cases.

(That was also made clear in the previous discussion. It's been a while
since you posted much here - it's nice to see you back on form :-) )
Keith Thompson
2024-05-23 20:06:39 UTC
Permalink
[...]
Post by David Brown
Post by bart
'embed' was discussed a few months ago. I disagreed with the poor
way it was to be implemented: 'embed' notionally generates a list of
comma-separated numbers as tokens, where you have to take care of
any trailing zero yourself if needed. It would also be hopelessly
inefficient if actually implemented like that.
Fortunately, it is /not/ actually implemented like that - it is only
implemented "as if" it were like that. Real prototype implementations
(for gcc and clang - I don't know about other tools) are extremely
efficient at handling #embed. And the comma-separated numbers can be
more flexible in less common use-cases.
[...]

I'm aware of a proposed implementation for clang:

https://github.com/llvm/llvm-project/pull/68620
https://github.com/ThePhD/llvm-project

I'm currently cloning the git repo, with the aim of building it so I can
try it out and test some corner cases. It will take a while.

I'm not aware of any prototype implementation for gcc. If you are, I'd
be very interested in trying it out.

(And thanks for starting this thread!)
--
Keith Thompson (The_Other_Keith) Keith.S.Thompson+***@gmail.com
void Void(void) { Void(); } /* The recursive call of the void */
David Brown
2024-05-24 13:45:52 UTC
Permalink
Post by Keith Thompson
[...]
Post by David Brown
Post by bart
'embed' was discussed a few months ago. I disagreed with the poor
way it was to be implemented: 'embed' notionally generates a list of
comma-separated numbers as tokens, where you have to take care of
any trailing zero yourself if needed. It would also be hopelessly
inefficient if actually implemented like that.
Fortunately, it is /not/ actually implemented like that - it is only
implemented "as if" it were like that. Real prototype implementations
(for gcc and clang - I don't know about other tools) are extremely
efficient at handling #embed. And the comma-separated numbers can be
more flexible in less common use-cases.
[...]
https://github.com/llvm/llvm-project/pull/68620
https://github.com/ThePhD/llvm-project
I'm currently cloning the git repo, with the aim of building it so I can
try it out and test some corner cases. It will take a while.
I'm not aware of any prototype implementation for gcc. If you are, I'd
be very interested in trying it out.
I haven't seen anything concrete, but I believe I read about it in one
of the papers discussing #embed. It may have been just some tests and
proofs-of-concept, and not a development branch or proposed implementation.
Post by Keith Thompson
(And thanks for starting this thread!)
It's not easy to find a topic that is entirely about C, hasn't been
discussed to death already, has enough controversial aspects for a
serious discussion but not so many that it leads to fights and flames,
and is not so esoteric that it causes most readers eyes to glaze over!
Keith Thompson
2024-05-23 16:43:21 UTC
Permalink
bart <***@freeuk.com> writes:
[...]
Post by bart
I suspect that ones like 'embed' have been derived from C++ which
always likes to make things too wide-ranging and much harder to use
and implement than necessary.
No, C++ doesn't have #embed. (If it did, many C compilers would already
have it, since C and C++ commonly share the preprocessor
implementation.)
--
Keith Thompson (The_Other_Keith) Keith.S.Thompson+***@gmail.com
void Void(void) { Void(); } /* The recursive call of the void */
David Brown
2024-05-24 14:19:49 UTC
Permalink
Post by Keith Thompson
[...]
Post by bart
I suspect that ones like 'embed' have been derived from C++ which
always likes to make things too wide-ranging and much harder to use
and implement than necessary.
No, C++ doesn't have #embed. (If it did, many C compilers would already
have it, since C and C++ commonly share the preprocessor
implementation.)
C++ has proposals for both #embed and std::embed<>, but AFAIK these are
not yet accepted. I expect #embed to make it (since the big tools will
support it for C anyway). std::embed<> is more powerful but has
additional complications.
Thiago Adams
2024-05-23 01:23:26 UTC
Permalink
Post by Keith Thompson
[...]
Post by David Brown
Post by Thiago Adams
 - nullptr
I am fond of nullptr in C++, and will use it in C. Like most of the
C23 changes, it's not a big issue - after all, you get a lot of the
same effect with "#define nullptr (void*)(0)" or similar. But it
means your code has a visual distinction between the integer 0 and a
null pointer, and also lets the compiler or other static checking
system check better than using NULL would. (And I don't like NULL - I
dislike all-caps identifiers in general.)
Quibble: That should be
#define nullptr ((void*)0)
For example, this doesn't produce a syntax error for `sizeof nullptr`.
#if __STDC_VERSION__ < 202311L
#define nullptr ((void*)0)
#endif
C23's nullptr is of type nullptr_t, not void*. But you'd probably have
to go out of your way for that to be an issue (e.g., using nullptr in a
generic selection).
[...]
Post by David Brown
Post by Thiago Adams
 - constexpr
I will definitely use that. Sometimes I want a constant expression
for things like array sizes or static initialisers, and want to
calculate it. constexpr gives you that without having to resort to
macros. (I'd perhaps be even happier if I could just use const, as I
can in C++.)
But const doesn't mean constant. It means read-only.
`const int r = rand();` is perfectly valid.
I dislike the C++ hack of making N a constant expression given
`const int N = 42;`; constexpr made that unnecessary. C23 makes the
same (IMHO) mistake.
If I had a time machine, I'd spell "const" as "readonly" and make
"const" mean what "constexpr" now means (evaluated at compile time).
[...]
Everything is a mess: const in C++, the differences from const in C,
etc. constexpr in C23 just makes the mess bigger.

auto is a mess as well not well specified for pointer. not sure if we
had this topic here, but auto * p in C is not specified.

I would remove from C23
- nullptr
-auto
-constexpr
-embed

I like the idea of embed but there is no implementation in production so
this is crazy!
Lawrence D'Oliveiro
2024-05-23 02:59:28 UTC
Permalink
I like the idea of embed ...
We’ve discussed this before. It just seems like a sop to those stuck with
antiquated, crippled build systems. In which case, how would they get an
up-to-date compiler that supports it?
Keith Thompson
2024-05-23 04:08:54 UTC
Permalink
Post by Lawrence D'Oliveiro
I like the idea of embed ...
We’ve discussed this before. It just seems like a sop to those stuck with
antiquated, crippled build systems. In which case, how would they get an
up-to-date compiler that supports it?
Presumably by waiting until compilers support it, like any new feature.

C23 hasn't even been officially published yet, and at least a naive
implementation of #embed should be fairly straightforward.

Obviously any code that depends on it will be restricted to newer
compilers, but after a while that shouldn't be much of an issue.
And it means that the semantics of including the contents of a
(possibly binary) file are represented in the source code, not in
one of a gazillion incompatible build systems.
--
Keith Thompson (The_Other_Keith) Keith.S.Thompson+***@gmail.com
void Void(void) { Void(); } /* The recursive call of the void */
Lawrence D'Oliveiro
2024-05-23 04:20:24 UTC
Permalink
Post by Keith Thompson
Post by Lawrence D'Oliveiro
I like the idea of embed ...
We’ve discussed this before. It just seems like a sop to those stuck
with antiquated, crippled build systems. In which case, how would they
get an up-to-date compiler that supports it?
Presumably by waiting until compilers support it, like any new feature.
Time/effort would be better spent investing in a more versatile build
system. Which would have the added advantage of supporting other languages
besides C.
Keith Thompson
2024-05-23 04:30:34 UTC
Permalink
Post by Lawrence D'Oliveiro
Post by Keith Thompson
Post by Lawrence D'Oliveiro
I like the idea of embed ...
We’ve discussed this before. It just seems like a sop to those stuck
with antiquated, crippled build systems. In which case, how would they
get an up-to-date compiler that supports it?
Presumably by waiting until compilers support it, like any new feature.
Time/effort would be better spent investing in a more versatile build
system. Which would have the added advantage of supporting other languages
besides C.
If you have an immediate need for something like #embed, then of course
waiting for compilers to implement it isn't suffficient.

But it's part of the C23 standard, that's not going to change, and code
will be written to use it.

I don't understand your apparently intense dislike for #embed. The fact
that there are other ways to accomplish the same thing doesn't make it a
bad feature. By all means don't use it if you don't like it.
--
Keith Thompson (The_Other_Keith) Keith.S.Thompson+***@gmail.com
void Void(void) { Void(); } /* The recursive call of the void */
Lawrence D'Oliveiro
2024-05-23 04:47:44 UTC
Permalink
... code will be written to use it.
Funny, isn’t it, that when I post code using other features of C (like
iso646.h), I get piled on by people who don’t like it, with quite the
opposite argument.
Malcolm McLean
2024-05-23 06:29:08 UTC
Permalink
Post by Keith Thompson
Post by Lawrence D'Oliveiro
I like the idea of embed ...
We’ve discussed this before. It just seems like a sop to those stuck with
antiquated, crippled build systems. In which case, how would they get an
up-to-date compiler that supports it?
Presumably by waiting until compilers support it, like any new feature.
C23 hasn't even been officially published yet, and at least a naive
implementation of #embed should be fairly straightforward.
Obviously any code that depends on it will be restricted to newer
compilers, but after a while that shouldn't be much of an issue.
And it means that the semantics of including the contents of a
(possibly binary) file are represented in the source code, not in
one of a gazillion incompatible build systems.
And who will use the Baby X resource compiler when we have embed?
--
Check out Basic Algorithms and my other books:
https://www.lulu.com/spotlight/bgy1mm
David Brown
2024-05-23 12:32:46 UTC
Permalink
Post by Keith Thompson
[...]
Post by David Brown
Post by Thiago Adams
 - nullptr
I am fond of nullptr in C++, and will use it in C. Like most of the
C23 changes, it's not a big issue - after all, you get a lot of the
same effect with "#define nullptr (void*)(0)" or similar. But it
means your code has a visual distinction between the integer 0 and a
null pointer, and also lets the compiler or other static checking
system check better than using NULL would. (And I don't like NULL - I
dislike all-caps identifiers in general.)
Quibble: That should be
#define nullptr ((void*)0)
Indeed.
Post by Keith Thompson
For example, this doesn't produce a syntax error for `sizeof nullptr`.
#if __STDC_VERSION__ < 202311L
#define nullptr ((void*)0)
#endif
C23's nullptr is of type nullptr_t, not void*. But you'd probably have
to go out of your way for that to be an issue (e.g., using nullptr in a
generic selection).
The use of generics can be an advantage of nullptr here. The use in
templates was a prime motivation of introducing nullptr to C++, though I
think it is fair to say that templates are very much more popular in C++
than _Generic is in C. But I haven't thought of a real-world use-case yet!
Post by Keith Thompson
[...]
Post by David Brown
Post by Thiago Adams
 - constexpr
I will definitely use that. Sometimes I want a constant expression
for things like array sizes or static initialisers, and want to
calculate it. constexpr gives you that without having to resort to
macros. (I'd perhaps be even happier if I could just use const, as I
can in C++.)
But const doesn't mean constant. It means read-only.
`const int r = rand();` is perfectly valid.
Yes - which is why "constexpr" can be useful.
Post by Keith Thompson
I dislike the C++ hack of making N a constant expression given
`const int N = 42;`; constexpr made that unnecessary.
I find that "hack" convenient at times. But I see what you mean that it
is a "hack", and I agree that "constexpr" makes such a hack unnecessary.
(Ideally, the languages would have used terms such as "read_only" and
"constant" rather than "const" and "constexpr", but that boat sailed
long ago.)
Post by Keith Thompson
C23 makes the
same (IMHO) mistake.
I don't think so - as far as I can see, it avoids that mistake (if you
feel the "hack" was a mistake). C23 can't fix the choice of names -
that was from C90.
Post by Keith Thompson
If I had a time machine, I'd spell "const" as "readonly" and make
"const" mean what "constexpr" now means (evaluated at compile time).
[...]
Keith Thompson
2024-05-23 20:37:19 UTC
Permalink
[...]
Post by David Brown
Post by Keith Thompson
I dislike the C++ hack of making N a constant expression given
`const int N = 42;`; constexpr made that unnecessary.
I find that "hack" convenient at times. But I see what you mean that
it is a "hack", and I agree that "constexpr" makes such a hack
unnecessary. (Ideally, the languages would have used terms such as
"read_only" and "constant" rather than "const" and "constexpr", but
that boat sailed long ago.)
Post by Keith Thompson
C23 makes the
same (IMHO) mistake.
I don't think so - as far as I can see, it avoids that mistake (if you
feel the "hack" was a mistake). C23 can't fix the choice of names -
that was from C90.
Apparently I was mistaken. I had thought that C23 made this legal (as
it is in C++):

#include <stdio.h>
int main(void) {
const int n = 42;
switch (n) case n: puts("ok");
}

There was a proposal to do this:
https://thephd.dev/_vendor/future_cxx/papers/C%20-%20Initialized%20const%20Integer%20Declarations.html
and I wrote a response to it:
https://github.com/Keith-S-Thompson/const-proposal-response
but the proposed wording does not appear in N3220.

(If you want n to be a constant expression, replace "const" with
"constexpr" -- or use a macro or the enum hack if you don't have a C23
compiler.)

We're definitely stuck with the "const" and "constexpr" keywords, but at
least the semantics of "const" are reasonably consistent.
--
Keith Thompson (The_Other_Keith) Keith.S.Thompson+***@gmail.com
void Void(void) { Void(); } /* The recursive call of the void */
Michael S
2024-05-23 12:43:31 UTC
Permalink
On Wed, 22 May 2024 22:11:44 +0200
Post by David Brown
I will definitely use that. Sometimes I want a constant expression
for things like array sizes or static initialisers, and want to
calculate it. constexpr gives you that without having to resort to
macros.
I don't say that everything that can be done with C23 constexpr can be
done with enum, but for uses like ones you mentioned above, 90%
probably can be done with enum.
David Brown
2024-05-23 13:31:19 UTC
Permalink
Post by Michael S
On Wed, 22 May 2024 22:11:44 +0200
Post by David Brown
I will definitely use that. Sometimes I want a constant expression
for things like array sizes or static initialisers, and want to
calculate it. constexpr gives you that without having to resort to
macros.
I don't say that everything that can be done with C23 constexpr can be
done with enum, but for uses like ones you mentioned above, 90%
probably can be done with enum.
I realise that, and use enum for such things today. But IMHO constexpr
is neater and it also covers the other 10%.

I think most of the new features of C23 neaten up the language a bit.
They are not game-changers - I doubt that any of them will significantly
change the way anyone writes their code (especially for those already
happy with gcc or clang extensions). But there are several things here
that can make code a little nicer.

So yes, I /could/ use enum constants for things that are not
enumerations. I /did/ use them for that. But going forward with C23,
I'll use constexpr instead.
Kaz Kylheku
2024-05-23 20:23:15 UTC
Permalink
Post by David Brown
So yes, I /could/ use enum constants for things that are not
enumerations. I /did/ use them for that. But going forward with C23,
I'll use constexpr instead.
The value of an enum is:

1. Compiler warns of incomplete switch cases.

2. In a debugger when you examine an enum-valued expression or
variable, you get the symbolic name:

3. Safety (with C++ enum rules: no implicit
conversion from ordinary integer type to enum).

Historically, C code bases have abused enums to defined constants
like "enum { bufsize = 1024 }" for understandable reasons, but it is a
cringe-inducing hack, which is also incomplete and inflexible; e.g. what
if we want a floating-point constant.

I've benefited from (3) in C programs that were contrived
to be compilable as C++. (That practice, though, tends to increasingly
hamper your dialect choice though, as the languages diverge and make
only small steps here and there to become closer.)
--
TXR Programming Language: http://nongnu.org/txr
Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
Mastodon: @***@mstdn.ca
David Brown
2024-05-24 14:25:20 UTC
Permalink
Post by Kaz Kylheku
Post by David Brown
So yes, I /could/ use enum constants for things that are not
enumerations. I /did/ use them for that. But going forward with C23,
I'll use constexpr instead.
1. Compiler warns of incomplete switch cases.
(gcc -Wswitch or -Wswitch-enum)

To be clear - I will, without doubt, continue to use "enum" for
enumerations and enumerated types. For enumerations, enum gives all the
advantages you mention and more (such as automatic choice of values).

But I'd rather use "constexpr" for constant expressions that are not
enumerations.
Post by Kaz Kylheku
2. In a debugger when you examine an enum-valued expression or
3. Safety (with C++ enum rules: no implicit
conversion from ordinary integer type to enum).
(gcc -Wenum-compare -Wenum-conversion -Wenum-int-mismatch)
Post by Kaz Kylheku
Historically, C code bases have abused enums to defined constants
like "enum { bufsize = 1024 }" for understandable reasons, but it is a
cringe-inducing hack, which is also incomplete and inflexible; e.g. what
if we want a floating-point constant.
I've benefited from (3) in C programs that were contrived
to be compilable as C++. (That practice, though, tends to increasingly
hamper your dialect choice though, as the languages diverge and make
only small steps here and there to become closer.)
Scott Lurndal
2024-05-23 17:10:38 UTC
Permalink
Post by Michael S
On Wed, 22 May 2024 22:11:44 +0200
Post by David Brown
I will definitely use that. Sometimes I want a constant expression
for things like array sizes or static initialisers, and want to
calculate it. constexpr gives you that without having to resort to
macros.
I don't say that everything that can be done with C23 constexpr can be
done with enum, but for uses like ones you mentioned above, 90%
probably can be done with enum.
Are C23 enums signed? or unsigned? What is the supported enum range?
Michael S
2024-05-23 17:31:59 UTC
Permalink
On Thu, 23 May 2024 17:10:38 GMT
Post by Scott Lurndal
Post by Michael S
On Wed, 22 May 2024 22:11:44 +0200
Post by David Brown
I will definitely use that. Sometimes I want a constant expression
for things like array sizes or static initialisers, and want to
calculate it. constexpr gives you that without having to resort to
macros.
I don't say that everything that can be done with C23 constexpr can
be done with enum, but for uses like ones you mentioned above, 90%
probably can be done with enum.
Are C23 enums signed? or unsigned? What is the supported enum range?
I never read the standard, so below is *according to my understanding*,
rather than the fact.
Before C23 - signed, at least as wide as int, but wider ranges are not
prohibited and can be provided by implementation.
C23 - enum without type specifier are the same as before. enum with type
specifier have range of their master type.
Keith Thompson
2024-05-23 21:28:26 UTC
Permalink
Post by Michael S
On Thu, 23 May 2024 17:10:38 GMT
[...]
Post by Michael S
Post by Scott Lurndal
Are C23 enums signed? or unsigned? What is the supported enum range?
I never read the standard, so below is *according to my understanding*,
rather than the fact.
Before C23 - signed, at least as wide as int, but wider ranges are not
prohibited and can be provided by implementation.
C23 - enum without type specifier are the same as before. enum with type
specifier have range of their master type.
In C17 and earlier:

Each enumerated type shall be compatible with char, a signed integer
type, or an unsigned integer type. The choice of type is
implementation-defined, but shall be capable of representing the
values of all the members of the enumeration.

The compatible type can be either signed or unsigned, as documented by
the compiler. Each enumerator is of type int (not, as one might expect,
of the enumeration type).

enum foo { foo0, foo1 };
enum foo obj;

obj is of type "enum foo", which may be compatible with any integer type
but must be able to represent all the values of the enumerators.

enum bar { x = -1, y = +1 };

The compatible type for enum bar must be a signed type.

C23 adds the term "underlying type" for the type with which an enum type
is compatible. It can be specified explicitly; if not, it's an
implementation-defined type as in C17 and earlier.

For an enum type without a specified underlying type, enumerators are
usually of type int, but can be of the enum type in some cases. For
example:

enum big { a = INT_MAX, b };

The underlying type is likely to be long or long long, and the
enumerators a and b are of type enum big, which is wider than int.
--
Keith Thompson (The_Other_Keith) Keith.S.Thompson+***@gmail.com
void Void(void) { Void(); } /* The recursive call of the void */
Lawrence D'Oliveiro
2024-05-23 02:49:37 UTC
Permalink
Post by Thiago Adams
I am waiting MSVC support. There are a lot of simple features MSVC could
implement and deliver in small increments. But it is very slow.
And they wonder why developers are deserting the Windows platform for
Linux.
Michael S
2024-05-23 12:36:03 UTC
Permalink
On Thu, 23 May 2024 02:49:37 -0000 (UTC)
Post by Lawrence D'Oliveiro
Post by Thiago Adams
I am waiting MSVC support. There are a lot of simple features MSVC
could implement and deliver in small increments. But it is very
slow.
And they wonder why developers are deserting the Windows platform for
Linux.
In practice, on my old home Windows PC (11 y.o. installation of 14 y.o.
OS) today, 2024-05-23, I can easily install and use gcc14.1.0 alongside
clang18.1.5 alongside one of the newest versions of MSVC (not sure
which one) alongside latest Intel ICC alongside any older version of
MSVC and ICC and with a little more effort and disk space alongside
older versions of clang and gcc at least as long back as gcc4.9. I can
use all of those either simultaneously or interchangeably.
I very much doubt that I can get similar variety of compiler versions
on Linux of similar age or even on one that is 5 years younger. Even on
most up to date Linux distros, in order to get such compilers zoo, I'd
probably have to fight against package manager rather than be assisted
by it.
Scott Lurndal
2024-05-23 16:40:09 UTC
Permalink
Post by Michael S
On Thu, 23 May 2024 02:49:37 -0000 (UTC)
Post by Lawrence D'Oliveiro
Post by Thiago Adams
I am waiting MSVC support. There are a lot of simple features MSVC
could implement and deliver in small increments. But it is very
slow.
And they wonder why developers are deserting the Windows platform for
Linux.
In practice, on my old home Windows PC (11 y.o. installation of 14 y.o.
OS) today, 2024-05-23, I can easily install and use gcc14.1.0 alongside
clang18.1.5 alongside one of the newest versions of MSVC (not sure
which one) alongside latest Intel ICC alongside any older version of
MSVC and ICC and with a little more effort and disk space alongside
older versions of clang and gcc at least as long back as gcc4.9. I can
use all of those either simultaneously or interchangeably.
I very much doubt that I can get similar variety of compiler versions
on Linux of similar age or even on one that is 5 years younger. Even on
most up to date Linux distros, in order to get such compilers zoo, I'd
probably have to fight against package manager rather than be assisted
by it.
While it is not likely that the set of pre-built packages available from the
vendor for a particular distribution will include more than two versions
each of gcc and clang, with a simple script, one can easily build
all the versions of gcc or clang that one could ever want. Our
linux systems have gcc4,5,6,7,8,9,10,11,12 and 13 on them as well
as several versions of clang. If our customers were interested
in ICC (which is unlikely as they're mainly ARM based), linux could
accomodate them as well.

And given the extensive use of gcc extensions, msvc is out of the question.
Malcolm McLean
2024-05-22 19:10:51 UTC
Permalink
Post by David Brown
In an attempt to bring some topicality to the group, has anyone started
using, or considering, C23 ?  There's quite a lot of change in it,
especially compared to the minor changes in C17.
<https://open-std.org/JTC1/SC22/WG14/www/docs/n3220.pdf>
<https://en.wikipedia.org/wiki/C23_(C_standard_revision)>
<https://en.cppreference.com/w/c/23>
I like that it tidies up a lot of old stuff - it is neater to have
things like "bool", "static_assert", etc., as part of the language
rather than needing a half-dozen includes for such basic stuff.
I like that it standardises a several useful extensions that have been
in gcc and clang (and possibly other compilers) for many years.
I'm not sure it will make a big difference to my own programming - when
I want "typeof" or "chk_add()", I already use them in gcc.  But for
people restricted to standard C, there's more new to enjoy.  And I
prefer to use standard syntax when possible.
"constexpr" is something I think I will find helpful, in at least some
circumstances.
So I'm currently writing some code (you can follow my progress on
github, it is a new branch in the Baby X resource compiler project). And
it's just standard well understood algorithm code to manipulate XML
trees. And I certainly don't feel the neeed for static_assert. But even
boolean type and const. Of course quite alot of the functions don't
actually change the structures they are passed. But is littering the
code with const going to help? And why do you really need a boolean when
an int can hold either a zero or non-zero value?

And don't you just want a pared down, clean language?
--
Check out Basic Algorithms and my other books:
https://www.lulu.com/spotlight/bgy1mm
Thiago Adams
2024-05-22 19:40:29 UTC
Permalink
Post by Malcolm McLean
And why do you really need a boolean when
an int can hold either a zero or non-zero value?
What do you use instead? A typedef?
Malcolm McLean
2024-05-22 20:39:17 UTC
Permalink
Post by Thiago Adams
And why do you really need a boolean when an int can hold either a
zero or non-zero value?
What do you use instead? A typedef?
Here's a code snippet I wrote today.

static int haserror(LEXER *lex)
{
return lex->error[0] ? 1 : 0;
}

error is a character buffer which holds the error message if an error
has been encountered. And for convenience it is placed in the lexer. If
here is no error, it holds the empty string. However it's not entirely
obvious that testing the message directly is the way you should be
testing for an error condition, so I wrote that little function to make
things clearer.

It's easy enough to make it return a boolean, of course. But I don't see
a real benefit.
--
Check out Basic Algorithms and my other books:
https://www.lulu.com/spotlight/bgy1mm
Lawrence D'Oliveiro
2024-05-23 02:48:50 UTC
Permalink
Post by Malcolm McLean
static int haserror(LEXER *lex)
{
return lex->error[0] ? 1 : 0;
}
static bool has_error(LEXER * lex)
{
return lex->error[0] != 0;
} /*has_error*/
Richard Kettlewell
2024-05-23 08:07:23 UTC
Permalink
Post by Malcolm McLean
static int haserror(LEXER *lex)
{
return lex->error[0] ? 1 : 0;
}
error is a character buffer which holds the error message if an error
has been encountered. And for convenience it is placed in the
lexer. If here is no error, it holds the empty string. However it's
not entirely obvious that testing the message directly is the way you
should be testing for an error condition, so I wrote that little
function to make things clearer.
It's easy enough to make it return a boolean, of course. But I don't
see a real benefit.
Possible benefits:

1) It conveys information to the reader about the nature of the
function. In this particular case the name also conveys that
information well enough, so there’s not actually much to be gained
here, but it other contexts there may be more of an advantage.

2) It conveys information to the compiler that may be exploited by the
optimizer (depending on the compilation model, the capabilities of
the target platform and optimizer, etc).

We are gradually migrating functions with boolean sense to returning
bool, albeit not very systematically, mainly for reasons #1.
--
https://www.greenend.org.uk/rjk/
David Brown
2024-05-22 19:50:51 UTC
Permalink
Post by Malcolm McLean
Post by David Brown
In an attempt to bring some topicality to the group, has anyone
started using, or considering, C23 ?  There's quite a lot of change in
it, especially compared to the minor changes in C17.
<https://open-std.org/JTC1/SC22/WG14/www/docs/n3220.pdf>
<https://en.wikipedia.org/wiki/C23_(C_standard_revision)>
<https://en.cppreference.com/w/c/23>
I like that it tidies up a lot of old stuff - it is neater to have
things like "bool", "static_assert", etc., as part of the language
rather than needing a half-dozen includes for such basic stuff.
I like that it standardises a several useful extensions that have been
in gcc and clang (and possibly other compilers) for many years.
I'm not sure it will make a big difference to my own programming -
when I want "typeof" or "chk_add()", I already use them in gcc.  But
for people restricted to standard C, there's more new to enjoy.  And I
prefer to use standard syntax when possible.
"constexpr" is something I think I will find helpful, in at least some
circumstances.
So I'm currently writing some code (you can follow my progress on
github, it is a new branch in the Baby X resource compiler project). And
it's just standard well understood algorithm code to manipulate XML
trees. And I certainly don't feel the neeed for static_assert.
I use static assertions everywhere I can. I used them long before C11
added them to the language, using a somewhat messy macro to force an
error if the assertion fails. They catch mistakes, they document
assumptions, they make code clearer to the reader. And they do so with
zero cost in code space or run-time, and no more effort than writing a
comment. I find it hard to understand why anyone would actively choose
not to use them.
Post by Malcolm McLean
But even
boolean type and const.
Bool is much more than "int 0" and "int 1". And it is significantly
clearer in code. (Sometimes, of course, a specific enumerated type is
clearer than bool or int.)

Const documents the code, makes the action of a function clearer to the
reader, and helps catch mistakes.

These are all things that make the language better, and have done so for
the past 25 years.
Post by Malcolm McLean
Of course quite alot of the functions don't
actually change the structures they are passed. But is littering the
code with const going to help? And why do you really need a boolean when
an int can hold either a zero or non-zero value?
And don't you just want a pared down, clean language?
I want a language with the features I need and that help me to write
good clear code. Minimal is not helpful, any more than needlessly
complex is helpful.
Malcolm McLean
2024-05-23 23:06:53 UTC
Permalink
Post by David Brown
But even boolean type and const.
Const documents the code, makes the action of a function clearer to the
reader, and helps catch mistakes.
These are all things that make the language better, and have done so for
the past 25 years.
Of course quite alot of the functions don't actually change the
structures they are passed. But is littering the code with const going
to help? And why do you really need a boolean when an int can hold
either a zero or non-zero value?
And don't you just want a pared down, clean language?
I want a language with the features I need and that help me to write
good clear code.  Minimal is not helpful, any more than needlessly
complex is helpful.
So the code I'm working on at the moment.

It's an implemention of XPath (a subset, of course). XPath is sort of
query language for XML. You pass a query string like
"/bookstore/book//title" and that selects all children of
[root]/bookstore/book with the element tag "title".

Now querying the document shouldn't change it. So in C++ it should
bepassed in as a XMLDOC const &. In C, declaring the pointer a const
XMLDOC * conveyes the intention, but doesn't actually achieve the safety
you want and get with C++.

However the algorithm I have just moved to needs a bit associated with
each node it can turn on and of. Now in fact I did this via a hash
table. But it is very tempting and far more efficient to simply add a
hacky field to the XMLNODE structure - after all, I wrote the XML
parser. And in C++ "mutable" is designed for just this. But in C,
were're either const or not. And isn't it maybe better to leave the
const qualifier off the document pointer?

In fact, wouldn't we just be better off without const? After all, you
need to read the function specifications anyway, and they should say
that querying for a path will not alter the document.
--
Check out Basic Algorithms and my other books:
https://www.lulu.com/spotlight/bgy1mm
David Brown
2024-05-24 14:39:02 UTC
Permalink
Post by Malcolm McLean
Post by David Brown
But even boolean type and const.
Const documents the code, makes the action of a function clearer to
the reader, and helps catch mistakes.
These are all things that make the language better, and have done so
for the past 25 years.
Of course quite alot of the functions don't actually change the
structures they are passed. But is littering the code with const
going to help? And why do you really need a boolean when an int can
hold either a zero or non-zero value?
And don't you just want a pared down, clean language?
I want a language with the features I need and that help me to write
good clear code.  Minimal is not helpful, any more than needlessly
complex is helpful.
So the code I'm working on at the moment.
It's an implemention of XPath (a subset, of course). XPath is sort of
query language for XML. You pass a query string like
"/bookstore/book//title" and that selects all children of
[root]/bookstore/book with the element tag "title".
Now querying the document shouldn't change it. So in C++ it should
bepassed in as a XMLDOC const &. In C, declaring the pointer a const
XMLDOC * conveyes the intention, but doesn't actually achieve the safety
you want and get with C++.
The safety is the same in C and C++ (unless your C++ code provides const
and non-const overloads for the function). References in C++ don't let
you pass a null pointer, but you can "cast away" the const in a const
reference as easily as you can remove the const in a const pointer:

void naughty(const int & x) {
int & y = (int &) x;
y++;
}

In both cases, the "const" is a promise to the reader and a promise to
the compiler, but you can break that promise if you do so explicitly.
Post by Malcolm McLean
However the algorithm I have just moved to needs a bit associated with
each node it can turn on and of. Now in fact I did this via a hash
table. But it is very tempting and far more efficient to simply add a
hacky field to the XMLNODE structure - after all, I wrote the XML
parser. And in C++ "mutable" is designed for just this. But in C,
were're either const or not. And isn't it maybe better to leave the
const qualifier off the document pointer?
"mutable" is just a kosher way of breaking your const promises. In
cases where "mutable" might be useful, I generally prefer to
differentiate between the part of structure that is fixed and
unchanging, and the part that is more volatile status. (This can also
be better from the viewpoint of cache friendliness, if that is of
concern.) But if I had a situation where C++ "mutable" would be the
best choice, and I had to implement it in C without "mutable", I am not
sure that casting to non-const in the implementation function would be
must worse.
Post by Malcolm McLean
In fact, wouldn't we just be better off without const?
No.

We'd be better off having everything const - /really/ constant - by
default, and having to explicitly declare the few things that actually
have to be changed after initialisation. That's how many modern
programming languages do it.
Post by Malcolm McLean
After all, you
need to read the function specifications anyway, and they should say
that querying for a path will not alter the document.
/Never/ write things in comments or documentation if you can express the
same thing in code.
Lawrence D'Oliveiro
2024-05-23 02:46:25 UTC
Permalink
Post by Malcolm McLean
And don't you just want a pared down, clean language?
The train for BCPL and BLISS now leaving on platform 1980 ...
Lawrence D'Oliveiro
2024-05-23 02:47:20 UTC
Permalink
Post by Malcolm McLean
And why do you really need a boolean when
an int can hold either a zero or non-zero value?
Knowing that there is a range containing just two possible values allows
you to use the type as an array index.
Chris M. Thomasson
2024-05-22 21:24:53 UTC
Permalink
Post by David Brown
In an attempt to bring some topicality to the group, has anyone started
using, or considering, C23 ?  There's quite a lot of change in it,
especially compared to the minor changes in C17.
Love the way std::vectors respect alignas... C++20, iirc?

[...]
David Brown
2024-05-23 13:35:21 UTC
Permalink
Post by Chris M. Thomasson
Post by David Brown
In an attempt to bring some topicality to the group, has anyone
started using, or considering, C23 ?  There's quite a lot of change in
it, especially compared to the minor changes in C17.
Love the way std::vectors respect alignas... C++20, iirc?
[...]
I have no idea what you are talking about.

But did you notice that this is c.l.c, not c.l.c++, and the topic is
C23, not C++23 ? Discussing comparisons or compatibility with C++ is
fair enough, but talking about pure C++ matters (such as std::vector<>)
is unlikely to be helpful.
Chris M. Thomasson
2024-05-23 23:05:05 UTC
Permalink
Post by David Brown
Post by Chris M. Thomasson
Post by David Brown
In an attempt to bring some topicality to the group, has anyone
started using, or considering, C23 ?  There's quite a lot of change
in it, especially compared to the minor changes in C17.
Love the way std::vectors respect alignas... C++20, iirc?
[...]
I have no idea what you are talking about.
std::vector actually respects alignas, on MSVC at least. I did not know
this worked until I tried it. Iirc, Bonita was the one that sparked my
test. It aligned itself on the proper boundaries. Very nice.
Post by David Brown
But did you notice that this is c.l.c, not c.l.c++, and the topic is
C23, not C++23 ?  Discussing comparisons or compatibility with C++ is
fair enough, but talking about pure C++ matters (such as std::vector<>)
is unlikely to be helpful.
C has it as well... Very useful!
Chris M. Thomasson
2024-05-23 23:17:16 UTC
Permalink
On 5/23/2024 4:05 PM, Chris M. Thomasson wrote:
[...]
Post by Chris M. Thomasson
C has it as well... Very useful!
alignas
David Brown
2024-05-24 14:50:28 UTC
Permalink
Post by Chris M. Thomasson
Post by David Brown
Post by Chris M. Thomasson
Post by David Brown
In an attempt to bring some topicality to the group, has anyone
started using, or considering, C23 ?  There's quite a lot of change
in it, especially compared to the minor changes in C17.
Love the way std::vectors respect alignas... C++20, iirc?
[...]
I have no idea what you are talking about.
std::vector actually respects alignas, on MSVC at least. I did not know
this worked until I tried it. Iirc, Bonita was the one that sparked my
test. It aligned itself on the proper boundaries. Very nice.
Post by David Brown
But did you notice that this is c.l.c, not c.l.c++, and the topic is
C23, not C++23 ?  Discussing comparisons or compatibility with C++ is
fair enough, but talking about pure C++ matters (such as
std::vector<>) is unlikely to be helpful.
C has it as well... Very useful!
I know C has alignas (now as a keyword in C23, instead of just _Alignas
from C11).

I know C++ has alignas (from C++11 onwards).

What I don't understand is why you think std::vector<> "respects
alignas" in C++20 - alignment for std::vector<> works like alignment for
any other class in C++, and always has done.

And what I /really/ don't understand is why you think it is remotely
relevant here? Even "alignas" in C is not particular relevant to this
thread, except that it has become a keyword in C23 instead of a macro
defined to _Alignas in <stdalign.h>.

Perhaps I should just be grateful for the small mercy of there being no
random youtube link in your post.
Chris M. Thomasson
2024-05-24 18:08:16 UTC
Permalink
Post by David Brown
Post by Chris M. Thomasson
Post by David Brown
Post by Chris M. Thomasson
Post by David Brown
In an attempt to bring some topicality to the group, has anyone
started using, or considering, C23 ?  There's quite a lot of change
in it, especially compared to the minor changes in C17.
Love the way std::vectors respect alignas... C++20, iirc?
[...]
I have no idea what you are talking about.
std::vector actually respects alignas, on MSVC at least. I did not
know this worked until I tried it. Iirc, Bonita was the one that
sparked my test. It aligned itself on the proper boundaries. Very nice.
Post by David Brown
But did you notice that this is c.l.c, not c.l.c++, and the topic is
C23, not C++23 ?  Discussing comparisons or compatibility with C++ is
fair enough, but talking about pure C++ matters (such as
std::vector<>) is unlikely to be helpful.
C has it as well... Very useful!
I know C has alignas (now as a keyword in C23, instead of just _Alignas
from C11).
I know C++ has alignas (from C++11 onwards).
What I don't understand is why you think std::vector<> "respects
alignas" in C++20 - alignment for std::vector<> works like alignment for
any other class in C++, and always has done.
And what I /really/ don't understand is why you think it is remotely
relevant here?  Even "alignas" in C is not particular relevant to this
thread, except that it has become a keyword in C23 instead of a macro
defined to _Alignas in <stdalign.h>.
alignas is very nice because it can help me make a 100% portable version
of some of my old exotic lock-free memory allocators that use rounding
to get down to a header. Any point in the region can be rounded down to
get at the header for the block. It involves aligning the main region on
a large boundary, say 8192 bytes. This is a little trick for high
performance lock-free allocators.

Iirc, I can make std::vector align its elements to say, L2 cachelines,
and I can make std::vector align itself on a large boundary say 8192
bytes. All in std C++! That is nice.

Any point in the block can round itself down to the nearest 8192 block,
then we have direct access to its header. Also, then I make a rule that
each memory block in a region is at least the size of a pointer. Then I
can use them in a lock-free stack, or something. This is damn near zero
overhead.

I have posted about some of these allocators in the past.
Post by David Brown
Perhaps I should just be grateful for the small mercy of there being no
random youtube link in your post.
No shit. ;^)
Chris M. Thomasson
2024-05-24 18:21:17 UTC
Permalink
On 5/24/2024 11:08 AM, Chris M. Thomasson wrote:
[...]

Fwiw, here is an older region allocator that is meant to use a threads
stack space. It not one of my lock-free ones but:

https://pastebin.com/raw/f37a23918
(raw text, no ads and shit like that...)

This old code uses some old alignment hacks!

#define RALLOC_ALIGN_OF(mp_type) \
offsetof( \
struct { \
char pad_RALLOC_ALIGN_OF; \
mp_type type_RALLOC_ALIGN_OF; \
}, \
type_RALLOC_ALIGN_OF \
)

Yikes!
Lawrence D'Oliveiro
2024-05-24 23:51:20 UTC
Permalink
I know C has alignas ...
Just for a moment, I wondered “what is an aligna?” ...

Lawrence D'Oliveiro
2024-05-23 03:13:50 UTC
Permalink
Post by David Brown
<https://open-std.org/JTC1/SC22/WG14/www/docs/n3220.pdf>
Unicode identifiers!

typedef int
typėdef;
David Brown
2024-05-23 13:42:29 UTC
Permalink
Post by Lawrence D'Oliveiro
Post by David Brown
<https://open-std.org/JTC1/SC22/WG14/www/docs/n3220.pdf>
Unicode identifiers!
typedef int
typėdef;
These have been around since C99...

There are a couple of minor tweaks to the characters supported, I think,
but nothing anyone is likely to notice in practice.
Michael S
2024-05-23 12:02:26 UTC
Permalink
On Wed, 22 May 2024 18:55:36 +0200
Post by David Brown
In an attempt to bring some topicality to the group, has anyone
started using, or considering, C23 ? There's quite a lot of change
in it, especially compared to the minor changes in C17.
<https://open-std.org/JTC1/SC22/WG14/www/docs/n3220.pdf>
<https://en.wikipedia.org/wiki/C23_(C_standard_revision)>
<https://en.cppreference.com/w/c/23>
I like that it tidies up a lot of old stuff - it is neater to have
things like "bool", "static_assert", etc., as part of the language
rather than needing a half-dozen includes for such basic stuff.
I like that it standardises a several useful extensions that have
been in gcc and clang (and possibly other compilers) for many years.
I'm not sure it will make a big difference to my own programming -
when I want "typeof" or "chk_add()", I already use them in gcc. But
for people restricted to standard C, there's more new to enjoy. And
I prefer to use standard syntax when possible.
"constexpr" is something I think I will find helpful, in at least
some circumstances.
Removed
1) Old-style function declarations and definitions
2) Representations for signed integers other than two's complement
3) Permission that u/U-prefixed character constants and string
literals may be not UTF-16/32
4) Mixed wide string literal concatenation
5) Support for calling realloc() with zero size (the behavior becomes
undefined) 6) __alignof_is_defined and __alignas_is_defined
7) static_assert is not provided as a macro defined in <assert.h>
(becomes a keyword) 8) thread_local is not provided as a macro defined
in <threads.h> (becomes a keyword)

1) good
2) good, but insufficient. The next logical step is to make both left
and right shift of negative integers by count that does not exceed #
of bits in respective type fully defined
3) IDNC
4) IDNC
5) IDNC
6) IDNC
7) bad. Breaks existing code for weak reason
8) bad. Breaks existing code for weak reason


Deprecated
1) <stdnoreturn.h>
2) Old feature-test macros
__STDC_IEC_559__
__STDC_IEC_559_COMPLEX__
3) _Noreturn function specifier
4) _Noreturn attribute token
5) asctime()
6) ctime()
7) DECIMAL_DIG (use the appropriate type-specific macro
(FLT_DECIMAL_DIG, etc) instead)
8) Definition of following numeric limit macros in <math.h> (they
should be used via <float.h>)
INFINITY
DEC_INFINITY
NAN
DEC_NAN
9) __bool_true_false_are_defined

No opinion on most of those.
W.r.t. 5 and 6.
IMHO, all old-UNIX-style APIs that return pointers to static
objects within library or rely on presence of static object within
library for purpose of preserving state for subsequent calls should be
systematically deprecated and for majority of them there should be
provided thread-safe alternatives akin to ctime_s().
That is, with exception of family of functions that uses FILE*. Not
that I like them very much, but they are ingrained too deeply.
So, peeking just asctime and ctime out of long list of problematic
APIs does not appear particularly consistent. If they were asking me
where to start, I'd start with rand().

With regard to new feature, the list is too long to comment in one post.
Just want to say that strfrom* family is long overdue, but still appear
incomplete. The guiding principle should be that all format specifiers
available in printf() with sole exception of %s should be provided as
strfrom* as well.
David Brown
2024-05-23 13:56:39 UTC
Permalink
Post by Michael S
On Wed, 22 May 2024 18:55:36 +0200
Post by David Brown
In an attempt to bring some topicality to the group, has anyone
started using, or considering, C23 ? There's quite a lot of change
in it, especially compared to the minor changes in C17.
<https://open-std.org/JTC1/SC22/WG14/www/docs/n3220.pdf>
<https://en.wikipedia.org/wiki/C23_(C_standard_revision)>
<https://en.cppreference.com/w/c/23>
I like that it tidies up a lot of old stuff - it is neater to have
things like "bool", "static_assert", etc., as part of the language
rather than needing a half-dozen includes for such basic stuff.
I like that it standardises a several useful extensions that have
been in gcc and clang (and possibly other compilers) for many years.
I'm not sure it will make a big difference to my own programming -
when I want "typeof" or "chk_add()", I already use them in gcc. But
for people restricted to standard C, there's more new to enjoy. And
I prefer to use standard syntax when possible.
"constexpr" is something I think I will find helpful, in at least
some circumstances.
Removed
1) Old-style function declarations and definitions
2) Representations for signed integers other than two's complement
3) Permission that u/U-prefixed character constants and string
literals may be not UTF-16/32
4) Mixed wide string literal concatenation
5) Support for calling realloc() with zero size (the behavior becomes
undefined)
6) __alignof_is_defined and __alignas_is_defined
7) static_assert is not provided as a macro defined in <assert.h>
(becomes a keyword)
8) thread_local is not provided as a macro defined
in <threads.h> (becomes a keyword)
1) good
Yes, at long last.
Post by Michael S
2) good, but insufficient. The next logical step is to make both left
and right shift of negative integers by count that does not exceed #
of bits in respective type fully defined
Agreed.
Post by Michael S
3) IDNC
4) IDNC
5) IDNC
6) IDNC
7) bad. Breaks existing code for weak reason
8) bad. Breaks existing code for weak reason
I am of the opinion that people should specify the standard they use as
part of their build procedures. (I'd have liked a standard way to
specify the C standard version code uses, so that it could be fixed in
source code files.) I don't think people should take random code for
Cxx and assume blindly that it will work for Cyy.

Yes, these will break some code. But I don't think it will break much,
and it will be nice to cut down on some of these headers. I have some
very old code that defines static_assert as a macro involving typedefs
with structs that can have positive or negative sizes, for C90 and C99.
I don't expect to compiler these as C23 without testing - backwards
compatibility is vital, but excessive backwards compatibility restricts
improvements to the language.

Still, it's a valid complaint. No change is going to please everyone!
Keith Thompson
2024-05-23 16:40:03 UTC
Permalink
Michael S <***@yahoo.com> writes:
[...]
Post by Michael S
Removed
[...]
Post by Michael S
7) static_assert is not provided as a macro defined in <assert.h>
(becomes a keyword)
8) thread_local is not provided as a macro defined in <threads.h>
(becomes a keyword)
[...]
Post by Michael S
7) bad. Breaks existing code for weak reason
8) bad. Breaks existing code for weak reason
In pre-C23, _Static_assert and _Thread_local are keywords, and
static_assert and thread_local are macros that expand to those keywords.

In C23, _Static_assert, _Thread_local, static_assert, and thread_local
are all keywords. Code that simply uses the old ugly keywords would not
break.

Code that does something like "#ifdef static_assert". I suppose the
headers could have retained the old macro definitions.

#define static_assert static_assert
#define thread_local thread_local
--
Keith Thompson (The_Other_Keith) Keith.S.Thompson+***@gmail.com
void Void(void) { Void(); } /* The recursive call of the void */
David Brown
2024-05-24 15:10:31 UTC
Permalink
Post by Keith Thompson
[...]
Post by Michael S
Removed
[...]
Post by Michael S
7) static_assert is not provided as a macro defined in <assert.h>
(becomes a keyword)
8) thread_local is not provided as a macro defined in <threads.h>
(becomes a keyword)
[...]
Post by Michael S
7) bad. Breaks existing code for weak reason
8) bad. Breaks existing code for weak reason
In pre-C23, _Static_assert and _Thread_local are keywords, and
static_assert and thread_local are macros that expand to those keywords.
In C23, _Static_assert, _Thread_local, static_assert, and thread_local
are all keywords. Code that simply uses the old ugly keywords would not
break.
Code that does something like "#ifdef static_assert". I suppose the
headers could have retained the old macro definitions.
#define static_assert static_assert
#define thread_local thread_local
The sort of code that could theoretically break is when you have
definitions like this:

#define STATIC_ASSERT_NAME_(line) STATIC_ASSERT_NAME2_(line)
#define STATIC_ASSERT_NAME2_(line) assertion_failed_at_line_##line
#define static_assert(claim, warning) \
typedef struct { \
char STATIC_ASSERT_NAME_(__COUNTER__) [(claim) ? 2 : -2]; \
} STATIC_ASSERT_NAME_(__COUNTER__)

That works in any C version, until C23, almost as well as
_static_assert. I used this when C11 support was rare in the tools I used.

While using #define for a C keyword is undefined behaviour, in practice
I think you'd have a hard time finding code and a compiler that used
such a macro and which did not work just as well in C23 mode.

(I don't know if anyone is in the habit of declaring macros named
"thread_local".)
Keith Thompson
2024-05-24 19:29:14 UTC
Permalink
Post by David Brown
Post by Keith Thompson
[...]
Post by Michael S
Removed
[...]
Post by Michael S
7) static_assert is not provided as a macro defined in <assert.h>
(becomes a keyword)
8) thread_local is not provided as a macro defined in <threads.h>
(becomes a keyword)
[...]
Post by Michael S
7) bad. Breaks existing code for weak reason
8) bad. Breaks existing code for weak reason
In pre-C23, _Static_assert and _Thread_local are keywords, and
static_assert and thread_local are macros that expand to those keywords.
In C23, _Static_assert, _Thread_local, static_assert, and
thread_local
are all keywords. Code that simply uses the old ugly keywords would not
break.
Code that does something like "#ifdef static_assert". I suppose the
headers could have retained the old macro definitions.
#define static_assert static_assert
#define thread_local thread_local
The sort of code that could theoretically break is when you have
#define STATIC_ASSERT_NAME_(line) STATIC_ASSERT_NAME2_(line)
#define STATIC_ASSERT_NAME2_(line) assertion_failed_at_line_##line
#define static_assert(claim, warning) \
typedef struct { \
char STATIC_ASSERT_NAME_(__COUNTER__) [(claim) ? 2 : -2]; \
} STATIC_ASSERT_NAME_(__COUNTER__)
That works in any C version, until C23, almost as well as
_static_assert. I used this when C11 support was rare in the tools I used.
You mean _Static_assert.
Post by David Brown
While using #define for a C keyword is undefined behaviour, in
practice I think you'd have a hard time finding code and a compiler
that used such a macro and which did not work just as well in C23
mode.
(I don't know if anyone is in the habit of declaring macros named
"thread_local".)
"static_assert" is already a macro defined in <assert.h> starting in
C11. The above code is valid in pre-C23, but will break in C11 and C17
if it includes <assert.h> directly or indirectly. You can fix it by
adding "#undef static_assert" or by picking a different name, or by
making your macro definition conditional on __STDC_VERSION__ >= 202311L.
--
Keith Thompson (The_Other_Keith) Keith.S.Thompson+***@gmail.com
void Void(void) { Void(); } /* The recursive call of the void */
BGB-Alt
2024-05-23 22:15:44 UTC
Permalink
Post by Michael S
On Wed, 22 May 2024 18:55:36 +0200
Post by David Brown
In an attempt to bring some topicality to the group, has anyone
started using, or considering, C23 ? There's quite a lot of change
in it, especially compared to the minor changes in C17.
<https://open-std.org/JTC1/SC22/WG14/www/docs/n3220.pdf>
<https://en.wikipedia.org/wiki/C23_(C_standard_revision)>
<https://en.cppreference.com/w/c/23>
I like that it tidies up a lot of old stuff - it is neater to have
things like "bool", "static_assert", etc., as part of the language
rather than needing a half-dozen includes for such basic stuff.
I like that it standardises a several useful extensions that have
been in gcc and clang (and possibly other compilers) for many years.
I'm not sure it will make a big difference to my own programming -
when I want "typeof" or "chk_add()", I already use them in gcc. But
for people restricted to standard C, there's more new to enjoy. And
I prefer to use standard syntax when possible.
"constexpr" is something I think I will find helpful, in at least
some circumstances.
Removed
1) Old-style function declarations and definitions
2) Representations for signed integers other than two's complement
3) Permission that u/U-prefixed character constants and string
literals may be not UTF-16/32
4) Mixed wide string literal concatenation
5) Support for calling realloc() with zero size (the behavior becomes
undefined) 6) __alignof_is_defined and __alignas_is_defined
7) static_assert is not provided as a macro defined in <assert.h>
(becomes a keyword) 8) thread_local is not provided as a macro defined
in <threads.h> (becomes a keyword)
1) good
I am mixed on 1, while I don't write any code using it, I am still using
some code that uses them (and requiring, say, modifying the Dhrystone
benchmark because it was using K&R style C, seems a bit weak).
Post by Michael S
2) good, but insufficient. The next logical step is to make both left
and right shift of negative integers by count that does not exceed #
of bits in respective type fully defined
Agreed. I might go further, but either way.


Though, I have found that there is wonk in a few cases, like some code
expecting that variable shifts are Mod N, while also expecting that
constant shifts are non-modulo (and that negative shifts will shift in
the opposite direction).

Granted, better for code to *not* rely on the behavior of out-of-range
or negative shifts, but alas...


May not matter that much (IIRC, when porting code like this to GCC, it
will give an error about the out-of-range shifts). Granted, in some ways
GCC was less conservative.

Though, the same program had also relied on the ability to use
out-of-bounds access to global arrays to access things inside other
global arrays (this broke in my initial porting attempt, as my compiler
does not preserve the relative order or alignment of global variables;
IOW: the C toplevel is not a struct...).
Post by Michael S
3) IDNC
4) IDNC
5) IDNC
6) IDNC
7) bad. Breaks existing code for weak reason
8) bad. Breaks existing code for weak reason
Agreed, would probably keep them as #define's.

One general thing of mine is, despite adding some amount of language
extensions in my own compiler, not changing things that breaks existing
code.
Post by Michael S
Deprecated
1) <stdnoreturn.h>
2) Old feature-test macros
__STDC_IEC_559__
__STDC_IEC_559_COMPLEX__
3) _Noreturn function specifier
4) _Noreturn attribute token
5) asctime()
6) ctime()
7) DECIMAL_DIG (use the appropriate type-specific macro
(FLT_DECIMAL_DIG, etc) instead)
8) Definition of following numeric limit macros in <math.h> (they
should be used via <float.h>)
INFINITY
DEC_INFINITY
NAN
DEC_NAN
9) __bool_true_false_are_defined
3 and 4. Mixed, would mostly only effect library headers so probably OK.
Post by Michael S
No opinion on most of those.
W.r.t. 5 and 6.
IMHO, all old-UNIX-style APIs that return pointers to static
objects within library or rely on presence of static object within
library for purpose of preserving state for subsequent calls should be
systematically deprecated and for majority of them there should be
provided thread-safe alternatives akin to ctime_s().
That is, with exception of family of functions that uses FILE*. Not
that I like them very much, but they are ingrained too deeply.
So, peeking just asctime and ctime out of long list of problematic
APIs does not appear particularly consistent. If they were asking me
where to start, I'd start with rand().
I ended up turning "errno" into thread-local state in my implementation
with no real ill effect.

This mostly leaves the "locale" stuff as global state (in my case, the
locale mostly effects whether to interpret some things as UTF-8 or 1252
/ 8859-1, if it can't be figured out in other ways).
Post by Michael S
With regard to new feature, the list is too long to comment in one post.
Just want to say that strfrom* family is long overdue, but still appear
incomplete. The guiding principle should be that all format specifiers
available in printf() with sole exception of %s should be provided as
strfrom* as well.
Not much opinion here.
Tim Rentsch
2024-05-24 00:37:39 UTC
Permalink
Michael S <***@yahoo.com> writes:

[comments on various new features in C23]

Overall I am quite disappointed by C23. IMO it's a step
backwards rather than forwards.
W.r.t. [asctime() and ctime() being removed]
IMHO, all old-UNIX-style APIs that return pointers to static
objects within library or rely on presence of static object within
library for purpose of preserving state for subsequent calls
should be systematically deprecated and for majority of them there
should be provided thread-safe alternatives akin to ctime_s().
That is, with exception of family of functions that uses FILE*.
Not that I like them very much, but they are ingrained too deeply.
So, peeking just asctime and ctime out of long list of problematic
APIs does not appear particularly consistent. If they were asking
me where to start, I'd start with rand().
I agree with the suggestion that restartable versions of "dirty"
functions be added to the C standard. I strongly disagree that
the old ones should be taken out. If compilers choose to give
warnings, that's fine, but these functions should not be removed
just because some people think they are clunky.
[...] Just want to say that strfrom* family is long overdue, but
still appear incomplete. The guiding principle should be that all
format specifiers available in printf() with sole exception of %s
should be provided as strfrom* as well.
What's the motivation for having separate functions? To me this
looks like creeping featuritis.
Michael S
2024-05-24 09:05:44 UTC
Permalink
On Thu, 23 May 2024 17:37:39 -0700
Post by Tim Rentsch
[comments on various new features in C23]
Overall I am quite disappointed by C23. IMO it's a step
backwards rather than forwards.
W.r.t. [asctime() and ctime() being removed]
IMHO, all old-UNIX-style APIs that return pointers to static
objects within library or rely on presence of static object within
library for purpose of preserving state for subsequent calls
should be systematically deprecated and for majority of them there
should be provided thread-safe alternatives akin to ctime_s().
That is, with exception of family of functions that uses FILE*.
Not that I like them very much, but they are ingrained too deeply.
So, peeking just asctime and ctime out of long list of problematic
APIs does not appear particularly consistent. If they were asking
me where to start, I'd start with rand().
I agree with the suggestion that restartable versions of "dirty"
functions be added to the C standard. I strongly disagree that
the old ones should be taken out. If compilers choose to give
warnings, that's fine, but these functions should not be removed
just because some people think they are clunky.
[...] Just want to say that strfrom* family is long overdue, but
still appear incomplete. The guiding principle should be that all
format specifiers available in printf() with sole exception of %s
should be provided as strfrom* as well.
What's the motivation for having separate functions? To me this
looks like creeping featuritis.
My practical motivation is space-constrained environments, where I
possibly want one or two or three formatters. sprintf() gives me
all or nothing and all can be too expensive. Many embedded environments
have big and small variants of sprintf that can be chosen at link time,
but what's in small variant does not necessarily match a set that I
want in my specific project. And is not necessarily well documented.

My aesthetic motivation is a symmetry between strto* and strfrom*.

My esoteric motivation is : sprintf() is historically associated with
"standard I/O". Functionality in question has no relationship to I/O.
But let's leave it aside, it's not important.
Tim Rentsch
2024-05-24 13:54:35 UTC
Permalink
Post by Michael S
On Thu, 23 May 2024 17:37:39 -0700
Post by Tim Rentsch
[...] Just want to say that strfrom* family is long overdue, but
still appear incomplete. The guiding principle should be that all
format specifiers available in printf() with sole exception of %s
should be provided as strfrom* as well.
What's the motivation for having separate functions? To me this
looks like creeping featuritis.
My practical motivation is space-constrained environments, where I
possibly want one or two or three formatters. sprintf() gives me all
or nothing and all can be too expensive. Many embedded environments
have big and small variants of sprintf that can be chosen at link
time, but what's in small variant does not necessarily match a set
that I want in my specific project. And is not necessarily well
documented.
Okay, I see now where you're coming from, although I'm not sure that
the strfrom*() functions will give you what you want (in terms of
memory footprint, etc). But I get your motivation.

Question: which of the four formats (%A, %E, %F, %G) are ones you
expect to use? Also I'm curious: do all of your target platforms
use IEEE floating point, or do some use other representations?
Michael S
2024-05-24 15:46:23 UTC
Permalink
On Fri, 24 May 2024 06:54:35 -0700
Post by Tim Rentsch
Post by Michael S
On Thu, 23 May 2024 17:37:39 -0700
Post by Tim Rentsch
[...] Just want to say that strfrom* family is long overdue, but
still appear incomplete. The guiding principle should be that all
format specifiers available in printf() with sole exception of %s
should be provided as strfrom* as well.
What's the motivation for having separate functions? To me this
looks like creeping featuritis.
My practical motivation is space-constrained environments, where I
possibly want one or two or three formatters. sprintf() gives me
all or nothing and all can be too expensive. Many embedded
environments have big and small variants of sprintf that can be
chosen at link time, but what's in small variant does not
necessarily match a set that I want in my specific project. And is
not necessarily well documented.
Okay, I see now where you're coming from, although I'm not sure that
the strfrom*() functions will give you what you want (in terms of
memory footprint, etc). But I get your motivation.
Question: which of the four formats (%A, %E, %F, %G) are ones you
expect to use?
Rarely: any of those, mostly for debugging.
In productioon code: %e is most likely, but %f could happen.
But it's not just a floating point. "Small" variants of sprintf() on
32-bit platforms often unable to handle %lld and %llu.
Post by Tim Rentsch
Also I'm curious: do all of your target platforms
use IEEE floating point, or do some use other representations?
Currently, only IEEE. In the past, there were others, but that was quite
a long tyme ago. Back, when after few years in other field I just
started my pro programming carieer, I spend couple of years doing
mostly TMS320C30. I don't remember for sure, but it is likely that I
never used formatted FP output there; our boards were probably too
short of memory for that.
Michael S
2024-05-23 14:19:11 UTC
Permalink
On Wed, 22 May 2024 18:55:36 +0200
Post by David Brown
In an attempt to bring some topicality to the group, has anyone
started using, or considering, C23 ? There's quite a lot of change
in it, especially compared to the minor changes in C17.
Why C Standard Committee, while being recently quite liberal in field
of introducing new keywords (too liberal for my liking, many new things
do not really deserve keywords not prefixed by __) is so conservative
in introduction of program control constructs? I don't remember any
new program control introduced under Committee regime.
And I want at least one.


Another area that was mostly unchanged since 1st edition of K&R is
storage classes. Even such obvious thing as removal of 'auto' class
took too long. If I am not mistaken, totally obsolete 'register' class
is still allowed. And I don't remember any additions.
Personally I can think about at least two useful backward-compatible
additions in that area.
James Kuyper
2024-05-23 18:35:57 UTC
Permalink
On 5/23/24 10:19, Michael S wrote:
...
Post by Michael S
Another area that was mostly unchanged since 1st edition of K&R is
storage classes. Even such obvious thing as removal of 'auto' class
took too long. If I am not mistaken, totally obsolete 'register' class
is still allowed. And I don't remember any additions.
constexpr and thread_local have both been added to the list of
Storage-class specifiers since K&R.
David Brown
2024-05-23 20:10:22 UTC
Permalink
Post by Michael S
On Wed, 22 May 2024 18:55:36 +0200
Post by David Brown
In an attempt to bring some topicality to the group, has anyone
started using, or considering, C23 ? There's quite a lot of change
in it, especially compared to the minor changes in C17.
Why C Standard Committee, while being recently quite liberal in field
of introducing new keywords (too liberal for my liking, many new things
do not really deserve keywords not prefixed by __) is so conservative
in introduction of program control constructs? I don't remember any
new program control introduced under Committee regime.
And I want at least one.
What program control construct would you like?
Post by Michael S
Another area that was mostly unchanged since 1st edition of K&R is
storage classes. Even such obvious thing as removal of 'auto' class
took too long. If I am not mistaken, totally obsolete 'register' class
is still allowed.
"register" is still in C23. (Some compilers pay attention to it. gcc
with optimisation disabled puts local variables on the stack, except for
those marked "register" that get put in registers.) It got dropped from
C++ when "auto" was re-purposed in C++11, but with the keyword
"register" kept for future use. I would not have objected to the same
thing happening in C23.
Post by Michael S
And I don't remember any additions.
_Thread_local was added in C11, with the alias thread_local in C23.

What would you like to see here?
Post by Michael S
Personally I can think about at least two useful backward-compatible
additions in that area.
Michael S
2024-05-23 21:34:24 UTC
Permalink
On Thu, 23 May 2024 22:10:22 +0200
Post by David Brown
Post by Michael S
On Wed, 22 May 2024 18:55:36 +0200
Post by David Brown
In an attempt to bring some topicality to the group, has anyone
started using, or considering, C23 ? There's quite a lot of change
in it, especially compared to the minor changes in C17.
Why C Standard Committee, while being recently quite liberal in
field of introducing new keywords (too liberal for my liking, many
new things do not really deserve keywords not prefixed by __) is so
conservative in introduction of program control constructs? I don't
remember any new program control introduced under Committee regime.
And I want at least one.
What program control construct would you like?
Ability to break from nested loops. Ability to"continue" outer loops
would be nice too, but less important.
I am not sure what syntax I want for this feature, never considered
myself a competent language designer.
Post by David Brown
Post by Michael S
Another area that was mostly unchanged since 1st edition of K&R is
storage classes. Even such obvious thing as removal of 'auto' class
took too long. If I am not mistaken, totally obsolete 'register'
class is still allowed.
"register" is still in C23. (Some compilers pay attention to it.
gcc with optimisation disabled puts local variables on the stack,
except for those marked "register" that get put in registers.) It
got dropped from C++ when "auto" was re-purposed in C++11, but with
the keyword "register" kept for future use. I would not have
objected to the same thing happening in C23.
Post by Michael S
And I don't remember any additions.
_Thread_local was added in C11, with the alias thread_local in C23.
_Thread_local is a special-purpose thing, probably not applicable at
all for programming of small embedded systems, which nowadays is the
only type of programming in C that I do for money rather than as hobby.
With regard to constexpr, mentioned above by James Kuyper, my feeling
about it is that it belongs to metaprogramming so I would not consider
it a real storage class.
Post by David Brown
What would you like to see here?
Instead of solutions, let's talk about problems that I want to solve:

1. global objects, declared in header files and included several times.
Where defined?
For some linkers, mostly unixy linkers, in case of none-initialized
objects (implicitly initialized to zero) it somehow works.
For linkers used on embedded systems it requires additional effort.
I think, for initialized globals it takes additional effort even with
unixy linkers.
I wnat it to "just work" everywhere. I think that the best way to get
it without breaking existing semantics is a new storage class.

2. Reversing defaults for visibility of objects and functions at file
scope.
Something like:
#pragma export_by_default(off).
When this pragma is in effect, we need a way to make objects and
functions globally visible. I think that it's done best with new
storage class.
Post by David Brown
Post by Michael S
Personally I can think about at least two useful backward-compatible
additions in that area.
Lawrence D'Oliveiro
2024-05-24 01:06:42 UTC
Permalink
On Thu, 23 May 2024 22:10:22 +0200 David Brown
Post by David Brown
What program control construct would you like?
Ability to break from nested loops.
At least 90% of the time, when I want to exit from an inner loop in C,
there will be some kind of cleanup I need to do in the outer loop before
that can exit too. So the ability to jump straight out will rarely be
used.
Malcolm McLean
2024-05-24 05:38:18 UTC
Permalink
Post by Lawrence D'Oliveiro
On Thu, 23 May 2024 22:10:22 +0200 David Brown
Post by David Brown
What program control construct would you like?
Ability to break from nested loops.
At least 90% of the time, when I want to exit from an inner loop in C,
there will be some kind of cleanup I need to do in the outer loop before
that can exit too. So the ability to jump straight out will rarely be
used.
goto gives you the functionality you require.

I usually use goto for handling malloc() failures. So if an allocation
fails within a deeply nested loop, I will jump to code at the end of the
function, free up amy half-constructed objects, and return an error
condition.
--
Check out Basic Algorithms and my other books:
https://www.lulu.com/spotlight/bgy1mm
Lawrence D'Oliveiro
2024-05-24 05:42:41 UTC
Permalink
Post by Malcolm McLean
Post by Lawrence D'Oliveiro
On Thu, 23 May 2024 22:10:22 +0200 David Brown
Post by David Brown
What program control construct would you like?
Ability to break from nested loops.
At least 90% of the time, when I want to exit from an inner loop in C,
there will be some kind of cleanup I need to do in the outer loop
before that can exit too. So the ability to jump straight out will
rarely be used.
goto gives you the functionality you require.
I avoid those. I structure my code like a Nassi-Shneiderman diagram, where
each block has one entrance at the top, and one exit at the bottom. Easier
to keep track of error conditions and cleanups that way.
Malcolm McLean
2024-05-24 06:47:48 UTC
Permalink
Post by Lawrence D'Oliveiro
Post by Malcolm McLean
Post by Lawrence D'Oliveiro
On Thu, 23 May 2024 22:10:22 +0200 David Brown
Post by David Brown
What program control construct would you like?
Ability to break from nested loops.
At least 90% of the time, when I want to exit from an inner loop in C,
there will be some kind of cleanup I need to do in the outer loop
before that can exit too. So the ability to jump straight out will
rarely be used.
goto gives you the functionality you require.
I avoid those. I structure my code like a Nassi-Shneiderman diagram, where
each block has one entrance at the top, and one exit at the bottom. Easier
to keep track of error conditions and cleanups that way.
I virtually always use goto for memory allocation failure.

It does mean that, strictly, the function is no longer a "structured"
subroutine. But reality is usually that memory allocation failure will
mean program termination pretty soon.
--
Check out Basic Algorithms and my other books:
https://www.lulu.com/spotlight/bgy1mm
Michael S
2024-05-24 08:42:24 UTC
Permalink
On Fri, 24 May 2024 06:38:18 +0100
Post by Malcolm McLean
Post by Lawrence D'Oliveiro
On Thu, 23 May 2024 22:10:22 +0200 David Brown
Post by David Brown
What program control construct would you like?
Ability to break from nested loops.
At least 90% of the time, when I want to exit from an inner loop in
C, there will be some kind of cleanup I need to do in the outer
loop before that can exit too. So the ability to jump straight out
will rarely be used.
goto gives you the functionality you require.
Sure, me too. Because that's what I have.
If they hadn't given me {, }, else, while, for, and do then I would
use goto to simulate all those as well. It gives functionality I
require, don't it?
Post by Malcolm McLean
I usually use goto for handling malloc() failures. So if an
allocation fails within a deeply nested loop, I will jump to code at
the end of the function, free up amy half-constructed objects, and
return an error condition.
I do similar thing too, but that's just a habit that I can't overcome.
It has no practical sense in environments that I work today. I could
just as well return immediately, without cleaning up, it would have
zero practical difference except that my code would be shorter and will
look cleaner.
Tim Rentsch
2024-05-24 01:35:58 UTC
Permalink
Michael S <***@yahoo.com> writes:

[what new language features would you like?]
Ability to break from nested loops. Ability to"continue" outer
loops would be nice too, but less important. [...]
1. global objects, declared in header files and included
several times. Where defined? [...] I wnat it to "just work"
everywhere. [...]
Both of these features seem like frills. Neither one is either
necessary or common; they would make the language bigger but
especially any better. Adding them would be the proverbial tail
wagging the dog.
2. Reversing defaults for visibility of objects and functions
at file scope.
#pragma export_by_default(off).
Not sure taking a half-and-half approach on this is a good idea,
but if so I think it's better to have the choice be a per-TU
compilation option rather than a #pragma.
When this pragma is in effect, we need a way to make objects and
functions globally visible. I think that it's done best with new
storage class.
Just use extern. No new storage class needed.
With regard to constexpr, mentioned above by James Kuyper, my
feeling about it is that it belongs to metaprogramming so I
would not consider it a real storage class.
Having 'constexpr' be classified as a storage class illustrates
how poorly thought out it is.
Keith Thompson
2024-05-24 02:42:11 UTC
Permalink
Tim Rentsch <***@z991.linuxsc.com> writes:
[...]
Post by Tim Rentsch
Having 'constexpr' be classified as a storage class illustrates
how poorly thought out it is.
constexpr is not classified as a storage class. N3022, like earlier
editions, says there are four storage durations: static, thread,
automatic, and allocated.

The constexpr keyword is treated syntactically as a
storage-class-specifier, for the same reason that typedef is.
It doesn't specify a storage class, but it was a convenient way to
add it to the existing grammar without breaking anything.

Perhaps the syntactic category "storage-class-specifier" should have
been renamed when "typedef" was added to it.
--
Keith Thompson (The_Other_Keith) Keith.S.Thompson+***@gmail.com
void Void(void) { Void(); } /* The recursive call of the void */
Tim Rentsch
2024-05-24 03:28:29 UTC
Permalink
Post by Keith Thompson
[...]
Post by Tim Rentsch
Having 'constexpr' be classified as a storage class illustrates
how poorly thought out it is.
constexpr is not classified as a storage class. N3022, like earlier
editions, says there are four storage durations: static, thread,
automatic, and allocated.
Obviously I was talking about the syntactic classification.
Don't be obtuse.
David Brown
2024-05-24 15:57:35 UTC
Permalink
Post by Michael S
On Thu, 23 May 2024 22:10:22 +0200
Post by David Brown
Post by Michael S
On Wed, 22 May 2024 18:55:36 +0200
Post by David Brown
In an attempt to bring some topicality to the group, has anyone
started using, or considering, C23 ? There's quite a lot of change
in it, especially compared to the minor changes in C17.
Why C Standard Committee, while being recently quite liberal in
field of introducing new keywords (too liberal for my liking, many
new things do not really deserve keywords not prefixed by __) is so
conservative in introduction of program control constructs? I don't
remember any new program control introduced under Committee regime.
And I want at least one.
What program control construct would you like?
Ability to break from nested loops. Ability to"continue" outer loops
would be nice too, but less important.
I am not sure what syntax I want for this feature, never considered
myself a competent language designer.
I've heard people request this before. I can't say I've ever felt it
was something I'd have use for, but there's lots of things in C that I
never need.

There is a proposal for adding it to C:

<https://open-std.org/JTC1/SC22/WG14/www/docs/n3195.htm>
Post by Michael S
Post by David Brown
Post by Michael S
Another area that was mostly unchanged since 1st edition of K&R is
storage classes. Even such obvious thing as removal of 'auto' class
took too long. If I am not mistaken, totally obsolete 'register'
class is still allowed.
"register" is still in C23. (Some compilers pay attention to it.
gcc with optimisation disabled puts local variables on the stack,
except for those marked "register" that get put in registers.) It
got dropped from C++ when "auto" was re-purposed in C++11, but with
the keyword "register" kept for future use. I would not have
objected to the same thing happening in C23.
Post by Michael S
And I don't remember any additions.
_Thread_local was added in C11, with the alias thread_local in C23.
_Thread_local is a special-purpose thing, probably not applicable at
all for programming of small embedded systems, which nowadays is the
only type of programming in C that I do for money rather than as hobby.
I have never seen the point of it either. Why would anyone want a
variable that exists for /all/ threads in a program, but independently
per thread? The only use I can think of is for errno (which is, IMHO, a
horror unto itself) but since that is defined by the implementation, it
does not need to use _Thread_local. (Indeed, thread-local errno macros
existed long before C11.)

You and I (as small embedded systems programmers) are perhaps biased in
being allergic to the wasted bytes of ram a thread-local variable would
likely use!
Post by Michael S
With regard to constexpr, mentioned above by James Kuyper, my feeling
about it is that it belongs to metaprogramming so I would not consider
it a real storage class.
The term "storage-class specifier" is a bit of a misnomer, in that it is
more of a syntactic term than referring just to the storage duration or
placement of objects. "typedef" is also a storage-class specifier, for
example.
Post by Michael S
Post by David Brown
What would you like to see here?
Good idea.
Post by Michael S
1. global objects, declared in header files and included several times.
Where defined?
In C, they must be defined in exactly one translation unit.
Post by Michael S
For some linkers, mostly unixy linkers, in case of none-initialized
objects (implicitly initialized to zero) it somehow works.
The use of "int global_x;" in headers is undefined behaviour (AFAIK) in
C, and its support is a hangover from linker support for Fortran common
blocks. And it is the source of many odd errors for people who are not
careful enough in their coding. If your compiler supports this
misfeature (such as "gcc -fcommon"), and you accidentally declare two
uninitialised non-static variables with the same name in two files, you
have no detection or protection from the chaos that ensues. With
compilers that don't support this ("gcc -fno-common"), you get a
link-time error showing your problem. (gcc made "-fno-common" the
default in version 10. That's 9 major versions late, IMHO, but better
late than never.)

(To the extent that it "works", it is handled by putting the symbol name
and data space reservation in a "common" section. At link time, common
symbols with the same name are merged - whether that makes sense for the
code or not.)
Post by Michael S
For linkers used on embedded systems it requires additional effort.
I can't say I have ever seen it as an effort. Almost all my C "modules"
come in pairs - "file.h" and "file.c". All non-local variables (and all
functions) are either static and declared only in "file.c", or they are
externally linked and have an "extern" declaration in "file.h" and a
definition (with or without initialisation) in "file.c" (which #includes
"file.h"). It is a very simple and clean arrangement, easily checked by
gcc warnings, and there are never any undetected conflicts.

(And probably 90% or more of current small-systems embedded development
uses gcc and binutils linker.)
Post by Michael S
I think, for initialized globals it takes additional effort even with
unixy linkers.
I wnat it to "just work" everywhere. I think that the best way to get
it without breaking existing semantics is a new storage class.
This is all very much a non-issue for well-structured code.


The only time it can matter is if you want to write "header-only"
modules. This is popular in C++, but not in C. In C++ it relies on the
linker merging the same symbols for inline declarations such as inline
functions, template functions, template class and function statics, and
- since C++17 - inline variables. So you can write "inline int
global_x;" or "inline int global_y = 123;" in a header, and it will be
created once and only once (if it is used somewhere). The
compiler/linker can't check for consistency of initialiser, unless you
are using link-time optimisation.
Post by Michael S
2. Reversing defaults for visibility of objects and functions at file
scope.
#pragma export_by_default(off).
When this pragma is in effect, we need a way to make objects and
functions globally visible. I think that it's done best with new
storage class.
I would much prefer if file-level variables and functions were "static"
by default and required explicit exporting. But that ship sailed 50
years ago.

What you can do - what /I/ do - is be rigid in making sure all your
exported variables and functions are declared as "extern" in a header
that is also included by the defining C file. Use "gcc
-Werror=missing-declarations -Werror=missing-variable-declarations" to
enforce these rules. It is not quite as good as going back in time and
fixing C at the start, but it's close!
Scott Lurndal
2024-05-24 16:16:27 UTC
Permalink
Post by David Brown
Post by Michael S
On Thu, 23 May 2024 22:10:22 +0200
_Thread_local is a special-purpose thing, probably not applicable at
all for programming of small embedded systems, which nowadays is the
only type of programming in C that I do for money rather than as hobby.
I have never seen the point of it either. Why would anyone want a
variable that exists for /all/ threads in a program, but independently
per thread?
Very common in kernel programming (e.g. the use of '%gs' in x86_linux)
as a pointer to the 'per-cpu' data structure.

We use thread local to implement 'self' methods in certain
classes (so rather than passing pointers around, one can
simply call class::self() to get a pointer to the
class for each thread.

class c_processor {
...
/**
* Per-thread value of the processor object.
*/
static __thread c_processor *p_this;
...

public:
c_processor(c_system *, c_logger *, processor_number_t, bool);
~c_processor(void);

static c_processor *self(void) { return p_this; }

...




c_processor *pp = c_processor::self().
Michael S
2024-05-24 16:22:56 UTC
Permalink
On Fri, 24 May 2024 17:57:35 +0200
Post by David Brown
I can't say I have ever seen it as an effort. Almost all my C
"modules" come in pairs - "file.h" and "file.c". All non-local
variables (and all functions) are either static and declared only in
"file.c", or they are externally linked and have an "extern"
declaration in "file.h" and a definition (with or without
initialisation) in "file.c" (which #includes "file.h"). It is a very
simple and clean arrangement, easily checked by gcc warnings, and
there are never any undetected conflicts.
Declaration/definition pair is repeating yourself, which is not a good
think.
Of course, the same applies to declaration/definition of externally
visible functions, but somehow in case of functions I am more tolerant
to repetitions than in case of variable. Probably, a psychological
phenomenon - I feel that functions are less trivial, so repetition is
less wasteful.
But I'd like to get rid of these repetitions to, I just did not figure
out a way to do it that does not compromise even more important concern
of seperation between interface and implementation (yes, I dislike Java
for that reason too).
bart
2024-05-24 18:38:16 UTC
Permalink
Post by Michael S
On Fri, 24 May 2024 17:57:35 +0200
Post by David Brown
I can't say I have ever seen it as an effort. Almost all my C
"modules" come in pairs - "file.h" and "file.c". All non-local
variables (and all functions) are either static and declared only in
"file.c", or they are externally linked and have an "extern"
declaration in "file.h" and a definition (with or without
initialisation) in "file.c" (which #includes "file.h"). It is a very
simple and clean arrangement, easily checked by gcc warnings, and
there are never any undetected conflicts.
Declaration/definition pair is repeating yourself, which is not a good
think.
Of course, the same applies to declaration/definition of externally
visible functions, but somehow in case of functions I am more tolerant
to repetitions than in case of variable. Probably, a psychological
phenomenon - I feel that functions are less trivial, so repetition is
less wasteful.
But I'd like to get rid of these repetitions to, I just did not figure
out a way to do it that does not compromise even more important concern
of seperation between interface and implementation (yes, I dislike Java
for that reason too).
I normally use a private systems language which some here have claimed
is just C with a different syntax.

Nevertheless, this particular problem has been solved:

* There is only a single definition of any function, variable, type,
struct, enum or macro

* No separate declarations are needed. Definitions can appear in any order

* There are no header files

* Exported definitions have a 'global' atribute

It also has a module scheme so that, for example, only the lead module
of a program needs to be submitted to the compiler.

C's facilities for this stuff are quite crude. It would be difficult to
retro-fit a scheme like mine.

At best a separate preprocessing pass can done before normal
compilation, which can produce the necessary declarations. Or perhaps a
clever IDE can generate some of this stuff.

But working only with the raw language as it is now, the tidiest
solution is the .h/.c file pairs that have been mentioned.
Keith Thompson
2024-05-24 20:06:05 UTC
Permalink
bart <***@freeuk.com> writes:
[...]
Post by bart
I normally use a private systems language which some here have claimed
is just C with a different syntax.
[...]

I don't recall anyone claiming that.
--
Keith Thompson (The_Other_Keith) Keith.S.Thompson+***@gmail.com
void Void(void) { Void(); } /* The recursive call of the void */
bart
2024-05-24 20:20:52 UTC
Permalink
Post by Keith Thompson
[...]
Post by bart
I normally use a private systems language which some here have claimed
is just C with a different syntax.
[...]
I don't recall anyone claiming that.
When it was last discussed it was in a different group but the same
people who post here.
Keith Thompson
2024-05-23 21:38:23 UTC
Permalink
Post by Michael S
On Wed, 22 May 2024 18:55:36 +0200
Post by David Brown
In an attempt to bring some topicality to the group, has anyone
started using, or considering, C23 ? There's quite a lot of change
in it, especially compared to the minor changes in C17.
Why C Standard Committee, while being recently quite liberal in field
of introducing new keywords (too liberal for my liking, many new things
do not really deserve keywords not prefixed by __) is so conservative
in introduction of program control constructs? I don't remember any
new program control introduced under Committee regime.
And I want at least one.
Which is?

New keywords are typically prefixed by an underscore and an upper case
letter, such as C11's "_Generic". There are no (standard) keywords
starting with "__".
Post by Michael S
Another area that was mostly unchanged since 1st edition of K&R is
storage classes. Even such obvious thing as removal of 'auto' class
took too long.
The automatic storage duration isn't going anywhere. The "auto" storage
class specifier has lost its old useless semantics in C23 and is used
for type inference.
Post by Michael S
If I am not mistaken, totally obsolete 'register' class
is still allowed. And I don't remember any additions.
The "register" keyword isn't entirely obsolete. It's a constraint
violation to take the address of a register object. It's also a hint
that access to an object should be as fast as possible, though such
hints are not as useful with modern optimizing compiler. I agree that
removing it wouldn't be a great loss (aside from requiring modifications
to existing code).
Post by Michael S
Personally I can think about at least two useful backward-compatible
additions in that area.
What are they?
--
Keith Thompson (The_Other_Keith) Keith.S.Thompson+***@gmail.com
void Void(void) { Void(); } /* The recursive call of the void */
Michael S
2024-05-23 21:48:02 UTC
Permalink
On Thu, 23 May 2024 14:38:23 -0700
Post by Keith Thompson
Post by Michael S
On Wed, 22 May 2024 18:55:36 +0200
Post by David Brown
In an attempt to bring some topicality to the group, has anyone
started using, or considering, C23 ? There's quite a lot of change
in it, especially compared to the minor changes in C17.
Why C Standard Committee, while being recently quite liberal in
field of introducing new keywords (too liberal for my liking, many
new things do not really deserve keywords not prefixed by __) is so
conservative in introduction of program control constructs? I don't
remember any new program control introduced under Committee regime.
And I want at least one.
Which is?
New keywords are typically prefixed by an underscore and an upper case
letter, such as C11's "_Generic". There are no (standard) keywords
starting with "__".
You are right. I confused Standard C language with
implementation-defined extensions.

But the point stands: in recent times Committee is (to my liking) not
sufficiently conservative in adding keywords that do not start with the
underscore followed by uppercase.
Bonita Montero
2024-05-23 19:25:43 UTC
Permalink
Post by David Brown
In an attempt to bring some topicality to the group, has anyone started
using, or considering, C23 ?  There's quite a lot of change in it,
especially compared to the minor changes in C17.
<https://open-std.org/JTC1/SC22/WG14/www/docs/n3220.pdf>
<https://en.wikipedia.org/wiki/C23_(C_standard_revision)>
<https://en.cppreference.com/w/c/23>
I like that it tidies up a lot of old stuff - it is neater to have
things like "bool", "static_assert", etc., as part of the language
rather than needing a half-dozen includes for such basic stuff.
I like that it standardises a several useful extensions that have been
in gcc and clang (and possibly other compilers) for many years.
I'm not sure it will make a big difference to my own programming - when
I want "typeof" or "chk_add()", I already use them in gcc.  But for
people restricted to standard C, there's more new to enjoy.  And I
prefer to use standard syntax when possible.
"constexpr" is something I think I will find helpful, in at least some
circumstances.
I ask myself what the point is in further developing a language
like this that can actually no longer be saved.
Thiago Adams
2024-05-23 19:49:53 UTC
Permalink
Post by Bonita Montero
I ask myself what the point is in further developing a language
like this that can actually no longer be saved.
do you mean C++?
Bonita Montero
2024-05-24 05:36:54 UTC
Permalink
Post by Thiago Adams
Post by Bonita Montero
I ask myself what the point is in further developing a language
like this that can actually no longer be saved.
do you mean C++?
No, C.
jak
2024-05-24 07:32:38 UTC
Permalink
Post by Bonita Montero
Post by Thiago Adams
Post by Bonita Montero
I ask myself what the point is in further developing a language
like this that can actually no longer be saved.
do you mean C++?
No, C.
I think you have a lot of confusion about programming languages. C and
C++ are not comparable languages. it can be c and assembler, or c++ and
java. If you really want to compare c to c++, then c++ is to c as rust
is to c++. I'm pretty convinced that c++ will be abandoned long before
c. Just for one example, c++ would be abandoned years ago if c# didn't
produce CLI code only because C# lacks nothing important than C++ and
the learning curve is much steeper (it also benefits from reflection).
Bonita Montero
2024-05-24 16:34:08 UTC
Permalink
Post by jak
Post by Bonita Montero
Post by Thiago Adams
Post by Bonita Montero
I ask myself what the point is in further developing a language
like this that can actually no longer be saved.
do you mean C++?
No, C.
I think you have a lot of confusion about programming languages. C and
C++ are not comparable languages.
C and C++ have a lot in common since 95% of what you can do you can do
in C++ also in the same way. But C++ puts 500% on top of that to solve
your tasks with a fraction of the code and if you use that the code
looks totally different than C.
Post by jak
I'm pretty convinced that c++ will be abandoned long before c.
Maybe, but for sure not in favour of C.
Post by jak
Just for one example, c++ would be abandoned years ago if c# didn't
produce CLI code only because C# lacks nothing important than C++
and the learning curve is much steeper (it also benefits from reflection).
Being a good C++ programmer needs a lot of experience, but if you've
done that you get a magnitude more productivity. And often you decide
for simple approaches in C because complex approaches are a lot of work.
Often this complex and more efficient approach is easy to handle in C++
if you managed to understand the language.
Kenny McCormack
2024-05-23 19:58:11 UTC
Permalink
In article <v2o57g$1t5p4$***@raubtier-asyl.eternal-september.org>,
Bonita Montero <***@gmail.com> wrote:
...
Post by Bonita Montero
I ask myself what the point is in further developing a language
like this that can actually no longer be saved.
You are certainly welcome to stop posting here.

Seriously, that other newsgroup wants and needs you.
--
Prayer has no place in the public schools, just like facts
have no place in organized religion.
-- Superintendent Chalmers
Bonita Montero
2024-05-24 16:35:00 UTC
Permalink
Post by Kenny McCormack
...
Post by Bonita Montero
I ask myself what the point is in further developing a language
like this that can actually no longer be saved.
You are certainly welcome to stop posting here.
Seriously, that other newsgroup wants and needs you.
I just mentioned a language which makes a fraction of the work
for the same task.
Loading...