Live data from Hacker News

The beauty and simplicity of the good old C-style void* in C++

giodicanio.com

41–50 of 182 posts

Re: The beauty and simplicity of the good old C-style void* in C++

#41
post #35

Earlier quoted context omitted.

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 possi…

> 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 arc…

> No - if something is UB in the spec, it's UB.

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.

Re: The beauty and simplicity of the good old C-style void* in C++

#42

I think that the author is right in everything he says and yes, there is beauty in it. However, the antithesis is also correct that there exist better solutions to solve the issues. Both premises hold true. I have an extensive assembler coding background on 6510, M68000, and i486. I had a very hard time accepting that something could be solved faster and more stable in a higher order language while the downside is mo…

There will always be cases (like audio processing, car brakes, pace makers) where hard real-time constraints prohibit GC languages (as well as l1 cache, instruction reordering and other optimizations). Also, consider that Python's performance frequently originates in it's bindings to libraries written in C, C++, Fortran, Rust.

I recently ran a few Java benchmarks and found that an array holding a bunch of objects incurred approx 3x the number of bytes compared to the expected number based on underlying class data structure. With current RAM prices, that is something to consider if you're building software that's meant to scale. Mileage may vary, but I expect JavaScript or Python will be similar.

So, sure. There is a case to be made that ergonomics and dev velocity. And premature micro optimizations might take your focus away from good systems architecture. But I've frequently found the need to peal of leaky abstractions and having to understand and be savvy at low level stuff too. Nothing wrong with studying the guts of a C64 or Amiga, today.

Python, Java or TypeScript are good educational tools, but you'd be doing yourself a disservice if you'd confine yourself to them without understanding computers.

Re: The beauty and simplicity of the good old C-style void* in C++

#43
> “Hey, why do you use the unsafe old C-style void* pointer?

Exactly, one should avoid unnecessarily erasing pointer target types. Luckily, C++ gives much better tools for that than C. This should have been a tem—

> Use some safe explicit type like uint8_t, which clearly represents an 8-bit byte!”

Sigh. Out of the frying pan, into the fire.

Re: The beauty and simplicity of the good old C-style void* in C++

#45

Earlier quoted context omitted.

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 possi…

Yeah but also, quick question: 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…

> even if the variable is declared static

No, for static even padding bytes are zero.

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.

Re: The beauty and simplicity of the good old C-style void* in C++

#46
Ick. The entire article starts from the fundamentally flawed premise that "you want a function that takes a blob of memory as an argument". Then they discuss bytewise access into structures..

Passing around void pointers is simply not a safe thing to do in C++. You can't do anything with a void pointer, so you're probably going to cast it as something else. Use that type instead, so that your caller knows they need to pass a valid pointer to that type. If the pointer has the wrong alignment then that will result undefined behavior. If you need to support multiple pointer types, use templates.

And, unless there are some really weird circumstances, you actually don't want to access your structures bytewise. Offsets can shift with compiler flags/versions. If you want serialization , please use a serialization library that correctly handles all of the odd cases. These can be quite efficient.

I've only actually had to munge bytes in a class once. Somebody decided that a previously POD class that was passed between processors with different memory spaces needed a virtual function, so I had to overwrite the vtable when I received it to make it valid.

Re: The beauty and simplicity of the good old C-style void* in C++

#47
post #36

Earlier quoted context omitted.

> A truly zero-cost abstraction Sadly the MSVC ABI makes std::span and std::string_view a pessimisation: https://github.com/tringi/win64_abi_call_overhead_benchmark https://godbolt.org/z/7baaox7re

Sounds like a compiler bug to me. It is a valid reason to avoid them in some rare cases right now, but it doesn't make the feature itself bad

Those are ABI. Unless it is inlining them, the overhead is to stay.

Re: The beauty and simplicity of the good old C-style void* in C++

#48

Earlier quoted context omitted.

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 possi…

Yeah but also, quick question: 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…

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.

Re: The beauty and simplicity of the good old C-style void* in C++

#49

I think that the author is right in everything he says and yes, there is beauty in it. However, the antithesis is also correct that there exist better solutions to solve the issues. Both premises hold true. I have an extensive assembler coding background on 6510, M68000, and i486. I had a very hard time accepting that something could be solved faster and more stable in a higher order language while the downside is mo…

There will always be cases (like audio processing, car brakes, pace makers) where hard real-time constraints prohibit GC languages (as well as l1 cache, instruction reordering and other optimizations). Also, consider that Python's performance frequently originates in it's bindings to libraries written in C, C++, Fortran, Rust. I recently ran a few Java benchmarks and found that an array holding a bunch of objects inc…

Note that, while complex, there exist GCs that can handle both soft real time and even hard real time constraints - especially for Java. Memory overhead is a problem with GC languages, though, and that one is by design.

Re: The beauty and simplicity of the good old C-style void* in C++

#50
post #2

> 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 man…

> 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 stru…

Afaik std::span does not need anything that was not in C++98 already, or am I missing something?
Post reply on HN