Hacker News new | past | comments | ask | show | jobs | submit
Important reminder just in the Preface :-)

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

>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...

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)].

Remember that C++ originally didn't have variadic templates, so something like std::format would have been impossible back in the day. Back in the day, std::iostream was a very neat solution for type safe string formatting. As you conceded, it also makes it very easy to integrate your own types. It was a big improvement over printf(). Historic perspective is everything.
> Don't use operator overloading for unrelated operations

This disn't stop with <iostream>, they keep doing it - the latest example I can think of is std::ranges operations being "piped" with |.

> The biggest problem is that today C++ doesn't have a way to implement this for your own types easily

I’m not sure about the stdlib version, but with fmtlib you can easily implement formatters for your own types. https://fmt.dev/11.0/api/#formatting-user-defined-types

loading story #41853483
Perfectly iostreams happy user since 1993.
loading story #41853058
loading story #41852904
loading story #41854676
loading story #41853427
loading story #41854939
loading story #41853344
Over the years, I have heard numerous complaints about C++ I/O streams. Is there a better open source replacement? Or do you recommend to use C functions for I/O?
>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.

Hindsight is 20/20, remember that. Streams are not that bad of an idea and have been working fine for decades. You haven't named a problem with it other than the fact the operators are used for other stuff in other contexts. But operator overloading is a feature of C++ so most operators, even the comma operator, can be something other than what you expect.

>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)].

You can trivially implement input and output for your own types with streams.

You appear to be a Rust guy whose motive is to throw shade on C++ for things that are utterly banal and subjective issues.

loading story #41855426
What’s wrong with it?
C++ can seamlessly include C89 headers.

The C library headers for libraries I write often include C11/C99 stuff that is invalid in C++.

Even when they are in C89, they are often incorrect to include without the include being in an `extern "C"`.

loading story #41851979
loading story #41852136
loading story #41852251
> C++ code can seamlessly add #include "sqlite3.h" unchanged.

Almost seamlessly. You have to do

  extern “C” {
    #include "sqlite3.h"
  }
(https://isocpp.org/wiki/faq/mixing-c-and-cpp#include-c-hdrs-...)
loading story #41853127
loading story #41852651
Yep; I think of it as "C/C++" and not "C" and/or "C++" i.e. one "multi-paradigm" language with different sets of mix-and-match.
My brief foray into microcontroller land has taught me that C and C++ are very much mixed.

It's telling that every compiler toolchain that compiles C++ also compiles C (for some definition of "C"). With compiler flags, GCC extensions, and libraries that are kinda-sorta compatible with both languages, there's no being strict about it.

_My_ code might be strict about it, but what about tinyusb? Eventually you'll have to work with a library that chokes on `--pedantic`, because much (most?) code is not written to a strict C or C++ standard, but is "C/C++" and various extensions.

loading story #41856074
Specially relevant to all those folks that insist on "Coding C with a C++ compiler", instead of safer language constructs, and standard library alternatives provided by C++ during the last decades.
Funny because for a long time the Microsoft MSVC team explicitly recommended compiling C code with a C++ compiler because they couldn't be arsed to update their C frontend for over two decades (which thankfully has changed now) ;)

https://herbsutter.com/2012/05/03/reader-qa-what-about-vc-an...

That thing always baffled me, this huge company building a professional IDE couldn't figure out how to ship updates to the C compiler.

> it is hard to say no to you, and I’m sorry to say it. But we have to choose a focus, and our focus is to implement (the standard) and innovate (with extensions like everyone but which we also contribute for potential standardization) in C++.

I mean, yeah if it came from a two member team at a startup, sure focus on C++, understandably. But Microsoft, what happened to "Developers! Developers! Developers!"?

It's not baffling, it's remarkably consistent. They implemented Java as J++ and made their version incompatible in various ways with the standard so it was harder to port your code away from J++ (and later J#). They implemented things in the CSS spec almost exactly opposite the specification to lock people into IE (the dominant browser, if you have to make your site work with 2+ incompatible systems which will you focus on?). Not supporting C effectively with their tools pushed developers towards their C++ implementation, creating more lock-in opportunities.
loading story #41852459
loading story #41851984
loading story #41852439
Perfectly valid to do if you need to interface with a large C code base and you just want to do some simple OO here and there. Especially if you cannot have runtime exceptions and the like.

This is how I managed to sneak C++ into an embedded C codebase. We even created some templates for data structures that supported static allocation at compile time.

What would be an example of "simple OO here and there" that cannot be done cleanly in plain C?
Templating on pixel classes so that a blitter builds all supported pixel paths separately and inlines them.

Yes you can do it less cleanly with macros or inline functions. But you can't do it performantly with struct and function pointers.

You can do anything in C that you want to. Of course one can make v-tables and all of that, and even do inheritance.

But having the "class" keyword is nice. Having built in support for member functions is nice.

Sometimes a person just wants the simplicity of C++ 2003.

(In reality I was working on a project where our compiler only supported C++ 2003 and we had a UI library written in C++ 2003 and honestly pure C UI libraries kind of suck compared to just sprinkling in a bit of C++ sugar.)

    > You can do anything in C that you want to.
How about destructors?
{"deleted":true,"id":41851799,"parent":41851140,"time":1729018019,"type":"comment"}
RAII
Is RAII Object orientation? I thought it was an idiom of C++ by Stroustrup.
It doesn't necessarily have to be OO no. Rust uses RAII and it uses traits instead of traditional OO style inheritance etc. You do need something like destructors/drop trait for it to work as far as I know though.
The killer feature of RAII is when combined with exceptions. But sneaking in exceptions in an embedded C project isn't something I'd encourage or recommend.

C++ imo doesn't offer anything compelling for the embedded usecase. Especially not considering all the footguns and politics it brings.

You can of course be strict and diligent about it but if you are you are pretty much just writing C anyway. Better to do it explicitly.

Allowing the use of the C++ standard library has been one of my biggest regrets (not that it was my decision to make, I fought it).

loading story #41852332
loading story #41852178
Namespaces, methods.
loading story #41851261
Yeah, but one should provide C++ type safe abstractions on top.

Just like one doesn't use Typescript to keep writing plain old JavaScript, then why bother.

I mean as long as your goal is specifically to do that I think it's fine. Using a C++ compiler to compile a C program isn't that rare.
A couple of months ago, in the company I work, there was a talk from HR, where they explained how to make a good CV (the company is firing lots of people). She say: "if you have experience in programming C, you can writing just that, or, if you have lots of experience in C, is customary to write ``C++ Experience'' "

Sooo... yeah... I should definitely change company!

That literally made me do a spit take, and it was fizzy water and it burned.

My god. That's amazing.

loading story #41853090
loading story #41852598
If you want a language with a great C FFI, C++ is hard to beat!
Bjarne should have called it ++C.
loading story #41854437
Because people choose to use pre-increment by default instead of post-increment?

Why is that?

loading story #41851650
loading story #41853498
loading story #41851427
loading story #41851557
Personally this[1] just makes C much more complicated for me, and I choose C when I want simplicity. If I want complicated, I would just pick C++ which I typically would never want. I would just pick Go (or Elixir if I want a server).

"_BitInt(N)" is also ugly, reminds me of "_Bool" which is thankfully "bool" now.

[1] guard, defer, auto, constexpr, nullptr (what is wrong with NULL?), etc. On top of that "constexpr" and "nullptr" just reeks of C++.

That said, Modern C is an incredible book, I have been using it for C99 (which I intend to continue sticking to).

loading story #41856362
loading story #41855214
loading story #41855553
loading story #41855182
loading story #41854987
loading story #41855526
Most important aspect of C is its portability. From small microcontrollers to almost any computing platform. I doubt that any new version of C will see that much adoption.

If I want to live on cutting edge I would rather use C++2x or Rust rather than C.

Am I missing something? What benefit this supposedly modern C offers?

loading story #41856401
loading story #41855595
loading story #41855343
loading story #41855410
loading story #41855880
I was going to ask if there is a good list of C books and then answered my own question. It categorizes _Modern C_ as Intermediate level.

https://stackoverflow.com/questions/562303/the-definitive-c-...

loading story #41853777
loading story #41856208
Table of contents in the sidebar doesn't work properly for me when I click on an entry (in macOS Preview).
I just test some links in the table of content, works fine for me. Using zathura pdf reader.
Also works in Adobe and Firefox, but doesn't work in Chrome and Edge.
Doesn't work for me either... but I will not dismiss the book because of that.
Table of contents is definitely broken right now.
loading story #41856509
So happy that we still get the dinosaur mascots! This is a good book.
My kingdom for fully specified, well defined portable bitfields.
Wow, the use of attributes like [[__unsequenced__]], [[maybe_unused]] and [[noreturn]] throughout the book is really awful. It seems pretty pedantic of the author to litter all the code examples with something that is mostly optional. For a second I wondered if C23 required them.
loading story #41854373
One of my favorite books ever.
Really looking forward to #embed, once the compilers catch up. Until then, Golang.
This is not how C standards work. If it appears in the standard, it means that it is already implemented in some compilers (in that case, at least in gcc and clang).
That isn't really how it goes, that is how it used to be up to C99.
loading story #41852590
Or

    xxd --include <file>
:)
The anti-Rust approach!
Clang 19 has it.
I end up using a .S asm file with .incbin directives to embed files.

#embed would be much nicer

Incbin works just fine from inline asm fwiw
Inline assembly isn't supported for x86-64 and ARM on MSVC which unfortunately also means the incbin trick can't be used there anymore.
How does "Modern" C compare safety-wise to Rust or Zig?
Modern C still promptly decays an array to a pointer, so no array bounds checking is possible.

D does not decay arrays, so D has array bounds checking.

Note that array overflow bugs are consistently the #1 problem with shipped C code, by a wide margin.

> no array bounds checking is possible.

This isn’t strictly true, a C implementation is allowed to associate memory-range (or more generally, pointer provenance) metadata with a pointer.

The DeathStation 9000 features a conforming C implementation which is known to catch all array bounds violations. ;)

> The DeathStation 9000 features a conforming C implementation which is known to catch all array bounds violations. ;)

That actually really does exist already with CHERI CPUs, whose pointers are tagged with "capabilities," which catch buffer overruns at runtime.

https://tratt.net/laurie/blog/2023/two_stories_for_what_is_c...

https://msrc.microsoft.com/blog/2022/01/an_armful_of_cheris/

Right. Also it might it sound like array-to-pointer decay is forced onto the programmer. Instead, you can take the address of an array just fine without letting it decay. The type then preserves the length.
loading story #41854211
loading story #41853029
"The DeathStation 9000"

The what now?

loading story #41853918
Google it.
Yeah, why have any type of human interaction in a forum when you can just refer your fellow brethren to the automaton.
loading story #41854271
You'd be surprised: Zig has one UB (Undefined Behaviour) that C doesn't have!

In release fast mode, unsigned overflow/underflow is undefined in Zig whereas in C it wraps.

:-)

Of course C has many UBs that Zig doesn't have, so C is far less safe than Zig, especially since you can use ReleaseSafe in Zig..

UB is does not automatically make things unsafe. You can have a compiler that implements safe defaults for most UB, and then it is not unsafe.
loading story #41853004
loading story #41853762
loading story #41853083
loading story #41852548
Does C automatically wrap? I thought you need to pass `-fwrapv` to the compiler to ensure that.
loading story #41852833
loading story #41852848
loading story #41852877
Modern C is barely any different than older C. The language committee for C is extremely conservative, changes tend to happen only around the edges.
Can someone link me to an article that explains why C is basically frozen at C99 for all practical purposes? Few projects worth talking about leverage features from C11 and newer
loading story #41855054
It's only been a few years since I've come to feel I can rely on C compilers all supporting C99, for a library I'm maintaing [1]. And after a couple of years, sure enough - I get an issue opened asking for C89 compatibility because of some arcane embedded toolchain or what-not.

So, C23? ... that's nice and all, but, let's talk about it in 20 years or so T_T

[1]: https://github.com/eyalroz/printf

in example 1.1 i read that as 'tits_square' until i saw the output
GCC support has been around since gcc 11 apparently. See table at (1). This is available in ubuntu 22.04. The page below also shows support for C26!

1) https://gcc.gnu.org/projects/cxx-status.html#:~:text=C%2B%2B...

Do they still use 0-terminated strings/char* as the main string type?

Is the usage of single linked lists still prevalent as the main container type?

loading story #41851903
loading story #41854343
loading story #41853099
loading story #41853013
Continuing to use a memory-unsafe language that has no recourse for safety and is full of footguns and is frankly irresponsible for the software profession. God help us all.

By the way, the US government did the profession no favors by including C++ as a memory-unsafe language. It is possible to write memory-safe C++, safe array dereferencing C++. But it’s not obvious how to do it. Herb Sutter is working on it with CppFront. The point stands that C++ can be memory-safe code. If you make a mistake, you might write some unsafe code in C++. But you can fix that mistake and learn to avoid it.

When you write C, you are in the bad luck shitter. You have no choice. You will write memory—unsafe code and hope you don’t fuck it up. You will hope that a refactor of your code doesn’t fuck it up.

Ah, C, so simple! You, only you, are responsible for handling memory safely. Don’t fuck it up, cadet. (Don’t leave it all to computers like a C++ developer would.)

Put C in the bin, where it belongs.

loading story #41856591
loading story #41856045
Skill issue
loading story #41856860
loading story #41856600