> It seems that some people are really losing the taste for good readable code.
It seems that some people never had taste for good reliable code. Use `void ` and now any error whatsoever is a direct undefined behavior. Moreover `std::span` clearly says that you are not* taking ownership of the memory (even though the language does not check it of course), while `void *` does not.
I understand that people can have many things to say about C++, and I do as well, but `std::span` should have been there decades ago and is such a life saver in these situations. A truly zero-cost abstraction which effectively saves you from a lot of troubles.
There's lots of UB in C-family execution models. Some of which is not actually UB because the implementation defines it - e.g. aligned DWORD-sized memory access is atomic on Windows because Microsoft said it is.
By choosing to use this language you choose to navigate the UB. Otherwise you'd be writing in Go, or Python.
It is possible to write reliable code despite the presence of UB in a language just like it's possible to drive to work every day for 20 years despite most of the directions you can point the car leading to an immediate crash. That's a needle with a much thinner eye than UB in C, and most people manage it. Mainly it means being very careful about lifetime and ownership. The Linux kernel manages it 99% of the time simply by being careful about lifetime and ownership, and that's a project with a huge number of contributors who don't intimately know each other's modules. I'm the Linux kernel you can't just say "new whatever" - you must have a plan for a lifetime of that whatever, and other people will review it.
struct S {
char c;
int i;
};
struct S a = {0};
struct S b = {0};
memcmp(&a, &b, sizeof(a)) == ...
If you answered 0, you'd be wrong, the answer is undefined, thanks to padding, initialization and alignment rules. Padding bytes are undefined, and not guaranteed to be initialized to zero even if the variable is declared static (where the members would be zeroed).
This is why the compiler is angry at the post writer, and why the reinterpret_cast is needed. Ideally if they wanted to do something with the data, they'd unbox the structure.
That's why it's not a good idea to use void* to pass arbitrary data interchangeable with bytes. It's a location, it makes no representation as to what's there and how to interact with it. Let alone who owns it.
std::span solves two problems here. One is the ownership problem. The other is that span<T> is a T[]. void* is god only knows.
The post asserts:
> The code is very clear and straightforward: you pass a pointer to the custom data structure, and its size in bytes. That’s it. Simple and clear.
This is unfortunately entirely false in C thanks to the aforementioned alignment/padding UB (and of course inner pointers). This is addressed with std::span. You'd still have to reinterpret_cast your structure to get the UB.
> Why should people complexify and uglify their C++ code with the uint8_t pointer (or std::byte), when void* works just fine??
tl;dr: because it doesn't. It just kinda looks like it does if you squint, and it's going to lead to the gnarliest bugs in the world.
For automatic, yes it may effectively turn a = {} to a.member = 0, leaving the padding bytes uninitialised. Or on copies like a = b it may not copy padding bytes.
Padding bytes are initialized to zero if you zero initialize the aggregate. It is hard to keep those bytes as zero but at initialization this much is guaranteed.
I looked into it some more and it's actually worse.
For static or thread storage, in C11 and later, ={0} will guarantee padding is zeroed. For automatic storage, per C11 6.7.9, only subobjects are required to be zeroed. Padding is not. [1]
In C23 initializing with ={} will give you zeroed padding, initializing with ={0} will not.
> Some of which is not actually UB because the implementation defines it
No - if something is UB in the spec, it's UB. The implementation will do something, sure, but what it does is not fixed and may even change based on compiler version and optimization level.
> DWORD-sized memory access is atomic on Windows because Microsoft said it is
Well, Intel said it is. Mind you I don't think there are any 32-bit native architectures where aligned dword access isn't atomic. Unaligned, on the other hand ...
"Undefined behavior" in the C standard literally means "behavior which this C standard does not put any requirements on" - it says so in the definitions section of the C standard. Other things can still put requirements on it. MSVC isn't just a C++ compiler - it's a C++ compiler for x64 Windows and therefore follows the rules of C++, x64, and Windows all at once.
A compiler is still free to ignore the spec and declare that something is not UB. However, this is very much compiler based, not platform based. Windows might guarantee that aligned DWORD-sized memory accesses are atomic, but that doesn't mean Clang when compiling for Windows would respect this - but MSVC might.
No, a compiler obviously cannot do this. nothing is undefined behaviour under a known compiler, version, and settings. UB means you can't know what the code does in general not that you can't know what it does in a very specific case.
1. It means that even if your program happens to work, it can't be portable
2. It means that even if your program happens to work today, it might stop working tomorrow when you add some new code, when you change some compiler flags, or when you do even a minor compiler upgrade
Of course, a compiler can't address 1. However, a compiler can very much address item 2. If Microsoft were to say "in MSVC, we define integer overflow to wrap", then they would guarantee that `INT_MAX + 1` will produce `INT_MIN` regardless of any optimization settings, any compiler upgrades, any other changes to the code. Of course, compiling the exact same program with Clang or GCC might cause it to crash or corrupt memory or anything else - but as long as you stuck with MSVC, your program would have perfectly defined semantics.
This is similar to using compiler extensions or intrinsics - they are not portable and not defined by the standard, maybe even explicitly defined to NOT be supported per the standard (such as variable length arrays in C++ in GCC), but they are nevertheless perfectly safe as long as you stick to your chosen compiler.
Edit to add: the integer overflow example is not just a theoretical possibility - lots of C++ compilers provide the `-fwrapv` flag; when using that flag, signed integer overflow is no longer UB for that program, it is defined just the same as unsigned integer overflow.
There is a difference between UB in C, and something being undefined in some version of Microsoft C on Windows.
Many of C's UB is specifically, intentionally left undefined in the standard to express code that relies on some specific way it is handled, is not proper, portable C. Indeed, the DWORD-sized memory access being atomic doesn't apply to MS Windows prior to version 3.0 running on a 80286.
MSVC has stabilised the ABI since VS 2015, we are on VS 2026 now.
Due to customer pressure to stop doing exactly that, to the point some ISO C and C++ stuff that requires breaking the ABI has not been implemented thus far.
I am quite certain that I will find ABI breaks in GCC release notes since Slackware 2.0, when I used it for the first time.
Everybody knows that C++ did not invent the concept of spans and that it was late to the party. It doesn’t change the fact that (presumably) nobody made a proposal to the C++ standard.
> It doesn’t change the fact that (presumably) nobody made a proposal to the C++ standard.
There were proposals about this for many years. C++ is just a terrible programming language, standardized by a committee (WG21) which exists in large part to boost the ego of one man, Bjarne Stroustrup.
N3851 for example wants to name this idea "array_view" which like "string_view" is an impressively unwieldy name for a core language feature, because of course neither of these were actually proposed as core language features even though that's what they naturally should be -- but it is basically the slice type or as you (and modern C++) call it a "span".
It's true that you can't change facts but what you've got here was a belief which was unfounded, not a fact.
I really don't understand why this was not pursued further. At the very least, this should have made it into C++17 together with std::string_view.
> because of course neither of these were actually proposed as core language features even though that's what they naturally should be
Should it really? What would this even look like in C++? IMO std::span works perfectly fine as a library type.
> C++ is just a terrible programming language, standardized by a committee (WG21) which exists in large part to boost the ego of one man, Bjarne Stroustrup.
That's certainly not the reason why it was standardized. Pre-C++98 was wild west with every compiler offering there own (incompatible) idea of what C++ is. Yes, there are many problems with design by committee in general (and the C++ committee in particular), but there was a very good reason for standardizing the language. The committee is not a one man show and there are many occasions where Bjarne has publicly voiced his frustration and disagreement.
Of course it isn't, all the great egotists need a parade of sycophants to heap praise on them, you've doubtless seen modern US "Cabinet meetings" in which TV hosts newly elevated to run parts of the US government compete with experienced politicians as they all try to offer the most effusive praise for their snoring God King.
Personally, I'd throw up, but then I'm very much of Groucho Marx's view on such things.
Are your seriously comparing the C++ standard committee to the Trump administration? I know you have an axe to grind, but this is getting ridiculous.
Where exactly have you seen this "parade of sycophants" in the C++ standards committee?
As far as I know, Bjarne is just a regular committee members with just as many votes as everyone else and no veto powers. The committee frequently accepts or rejects proposals against his will. For a recent example, see his harsh criticism of the new 'contracts' feature in C++26.
Yes, I am seriously making that comparison. It's not as bad of course but it's certainly enough to make me cringe.
> Where exactly have you seen this "parade of sycophants" in the C++ standards committee?
AIUI The committee itself operates under the "Chatham House Rule" in which participants agree not to tell anybody who said anything and so we can only see group outcomes for the committee itself. For example 100% affirmative votes for Bjarne's "Profiles" proposal. At 100% everybody who had the opportunity to vote "Against" has to admit that er, they didn't, because that's just maths - but you won't now find anybody who was enthusiastic, somehow a room full of people who all now remember being uncertain voted affirmatively anyway. How about that.
> Bjarne is just a regular committee member
For almost a decade, WG21 has a "Direction Group" with a handful of members which insists that while as you say everybody is just a "regular committee member" their group ought to set the "direction" for the language and thus the committee. The exact membership of the Direction Group varies over time, but of course Bjarne Stroustrup has always been a member of this group. The group (whatever its present membership) writes only unanimously, which means everything it says has been agreed by Bjarne Stroustrup, and it cites as its reference for how to set the direction several books about C++ all written by that same Bjarne Stroustrup.
So, sure, Bjarne is "just a regular committee member" in the same way that Britain's Prime Minister is "just a regular Member of Parliament" that is, very much in theory but not at all in practice.
If Bjarne was so powerful, how come they voted contracts into C++26 despite his strong concerns? How come he publicly vents his frustration with the direction the language is taking?
Bjarne isn't god and I didn't say he was. So no, he isn't all-powerful.
Bjarne has always been frustrated by the failures of C++ and has always blamed them on other people. He's an egotist, they're always like that, I find it exhausting.
Well, you could have linked an actual proposal instead of dropping some cool facts about C, Extended Pascal, Mesa/Cedar and Modula-2, as if that explained anything.
> I understand that people can have many things to say about C++, and I do as well, but `std::span` should have been there decades ago (...)
Decades is kind of a stretch. C++11 introduced smart pointers, and finally getting C++0x out of the door was already a major victory. Given the history of C++, it would be unrealistic to introduce something like std::span before C++17.
Meantime, some organizations are still struggling to migrate to something like C++14.
It could have been there since the beginning, given that open arrays (aka spans) already existed in other languages, and there was even a failed proposal from Denis Ritchie regarding C.
This argument is moot. The issue with spans is not that they require cutting edge technology to deliver.
Before commenting, perhaps you should research why even Denis Ritchie himself could not sell his idea to C.
It's funny how every single idea that's rejected is blindly lauded as brilliant but silenced due to some kind of conspiracy, and only the ideas that emerged are somehow bad, unacceptable, or late. Is the point to feel outraged?
Easy, even one of the author's could not change WG14 mind towards security.
Governments,related cybersecurity agencies, and companies are the ones getting outraged when looking at money spent in cyber attacks due to memory corruption issues.
WG14 adopted variably modified types, a kind of dependent type. From a security standpoint it offers all the same qualities. It also in principle was easier to integrate from a backwards compatibility standpoint, with the exception of struct member analogs (which we now have but aren't yet standardized).
Maybe we would have been better off with Ritchie's counter proposal. But neither proposal was chiefly concerned with security, thus no proposals for, e.g., automatic bounds checking.
Just to be clear, I often think we would have been better off with Ritchie's proposal, assuming it would have seen at least as much adoption in implementations and usage as variably modified types, which sadly remained poor for many years after C99, and arguably still poor. But being better off doesn't mean being in a drastically better situation than we are today from a security perspective. The proposed alternatives were prerequisites for substantively improving security, but far from sufficient. And the delay in adopting and refining variably-modified types has cost much more than whatever marginal benefit Ritchie's proposal offered. Ditto for other gaps, like better facilities for handling arithmetic, e.g. overflow and mixed type comparisons. The first step in addressing overflow only came with C23 (overflow checking routines), and the latter only in the forthcoming C2y (typesafe, mixed-signedness min/max, etc).
The support for variably modified types is excellent, if you discount MSVC which is lacking support for modern C anyway (it seems to catch up a bit though).
Real-world usage certainly remains poor. Using pointers to VM types remains annoying, and I wish the committee would settle on a solution to the ordering of VM parameters. But, yeah, the VM types are solid in GCC and clang and should be used more.
> Easy, even one of the author's could not change WG14 mind towards security.
Your comment conveys a hefty dose of ignorance on the topic. I recommend you read the proposal's arguments, including how it required breaking the ABI.
Are you asserting that WG14 never had the necessary skills among all the members to help improve this proposal, or dare to bring another one during the last 40 years?
Yeah, I keep missing the existing practice in secure C compilers that is supposed to land on a WG14 paper.
You can tell me the times you wish for, I will keep playing my trumpet until I see something in C that resembles to Modula-2 was offering in safety, nowadays brought back by Zig.
No, I called out your opinionated ignorance on the topic.
> WG14 never had the necessary skills among all the members to help improve this proposal (...)
Frankly I don't think you even understand what you're arguing. I mean, to start off, if an idea is feasible then do you think it needs a full blown committee joining forces to magically fix all the problems?
> (...) or dare to bring another one during the last 40 years?
Again, you are showing a hefty dose of ignorance in the topic. Do you even understand that anyone can put together a proposal? That's how the process works. If you feel so strongly about it, where is yours? What have you been doing for the past 40 years?
It seems that some people never had taste for good reliable code. Use `void ` and now any error whatsoever is a direct undefined behavior. Moreover `std::span` clearly says that you are not* taking ownership of the memory (even though the language does not check it of course), while `void *` does not.
I understand that people can have many things to say about C++, and I do as well, but `std::span` should have been there decades ago and is such a life saver in these situations. A truly zero-cost abstraction which effectively saves you from a lot of troubles.