←back to thread

The C23 edition of Modern C

(gustedt.wordpress.com)
397 points bwidlar | 7 comments | | HN request time: 0.611s | source | bottom
Show context
belter ◴[] No.41850897[source]
Important reminder just in the Preface :-)

Takeaway #1: "C and C++ are different: don’t mix them, and don’t mix them up"

replies(6): >>41850960 #>>41851047 #>>41851166 #>>41851693 #>>41853183 #>>41855660 #
jasode ◴[] No.41851693[source]
>Takeaway #1: "C and C++ are different: don’t mix them, and don’t mix them up"

Where "mixing C/C++" is helpful:

- I "mix C in with my C++" projects because "sqlite3.c" and ffmpeg source code is written C. C++ was designed to interoperate with C code. C++ code can seamlessly add #include "sqlite3.h" unchanged.

- For my own code, I take advantage of "C++ being _mostly_ a superset of C" such as using old-style C printf in C++ instead of newer C++ cout.

Where the "C is a totally different language from C++" perspective is helpful:

- knowing that compilers can compile code in "C" or "C++" mode which has ramifications for name mangling which leads to "LINK unresolved symbol" errors.

- knowing that C99 C23 has many exceptions to "C++ is a superset of C" : https://en.wikipedia.org/wiki/Compatibility_of_C_and_C%2B%2B...

replies(4): >>41851853 #>>41852165 #>>41852449 #>>41856015 #
tialaramex ◴[] No.41852165[source]
The entire I/O streams (where std::cout comes from) feature is garbage, if this was an independent development there is no way that WG21 would have taken it, the reason it's in C++ 98 and thus still here today is that it's Bjarne's baby. The reason not to take it is that it's contradictory to the "Don't use operator overloading for unrelated operations" core idea. Bjarne will insist that "actually" these operators somehow always meant streaming I/O but his evidence is basically the same library feature he's trying to justify. No other language does this, and it's not because they can't it's because it was a bad idea when it was created, it was still a bad idea in 1998, the only difference today is that C++ has a replacement.

The modern fmt-inspired std::print and std::println etc. are much nicer, preserving all the type checking but losing terrible ideas like stored format state, and localisation by default. The biggest problem is that today C++ doesn't have a way to implement this for your own types easily, Barry illustrates a comfortable way this could work in C++ 26 via reflection which on that issue closes the gap with Rust's #[derive(Debug)].

replies(8): >>41852524 #>>41852543 #>>41853207 #>>41853365 #>>41854242 #>>41854396 #>>41855139 #>>41855859 #
pjmlp ◴[] No.41852524[source]
Perfectly iostreams happy user since 1993.
replies(6): >>41852904 #>>41853058 #>>41853344 #>>41853427 #>>41854676 #>>41854939 #
1. Dwedit ◴[] No.41853058[source]
int a;

cin >> a;

Then the program goes berserk as soon as the first non-number is read out of standard input. All the other "cin >> integer" lines are immediately skipped.

Yes, I know about error checking, clearing error condition, discarding characters. But it's a whole lot of stuff you need to do after every single "cin>>" line. It makes the simplicity of cin not worth it.

replies(3): >>41853339 #>>41853403 #>>41855407 #
2. tightbookkeeper ◴[] No.41853339[source]
You’re holding it wrong. Like nan, the point is you don’t have to error check every operation.

You check error for the whole batch.

3. eMSF ◴[] No.41853403[source]
How could you ever continue after the second statement without checking if you actually read an integer or not? How would you know what you can do with a?
replies(2): >>41854295 #>>41855723 #
4. jvanderbot ◴[] No.41854295[source]
You couldn't or wouldn't. but why have a read statement like cin>> which looks so nice and clean when you then have to go and check everything with flags and boolean casts on stateful objects.

I agree. It's lunacy. just be explicit and use functions or equivalent like literally every other language.

5. PaulDavisThe1st ◴[] No.41855407[source]

   fscanf (STDIN, "%d", &a);
the program goes beserk as soon as the first non-number is read out of standard input.

in both cases, you need error checking (which you "know about").

replies(1): >>41856642 #
6. chongli ◴[] No.41855723[source]
Well in a language like Haskell you could solve this with monads and do-notation. The general idiom in Haskell is to use a Maybe or Either monad to capture success/failure and you assume you’re on the happy path. Then you put the error handling at the consumer end of the pipeline when you unwrap the Maybe or Either.

I believe Rust has adopted similar idioms. I’ve heard the overall idea referred to as Railway-oriented programming.

In C++ you could implement it with exceptions, though they bring in a bunch of their own baggage that you don’t have to deal with when using monads.

7. unwind ◴[] No.41856642[source]
No actual C programmer who has been around the block more than halfway should do that. The mantra is: "read into a character buffer, then parse that".

It's more code, sure, but it buys you a lot of good things. I/O is hard.