Live data from Hacker News

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

giodicanio.com

171–180 of 182 posts

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

#171

Earlier quoted context omitted.

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% af…

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.

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

#172
post #153

Earlier quoted context omitted.

You’re really citing a mess in a Ruby code base caused by lack of typing as evidence for why void * is problematic in C/C++? These are so wildly different cases that the comparison isn’t meaningful. This is like saying you should wear a helmet while playing tennis because sometimes helmets save bicyclists lives.

> You’re really citing a mess in a Ruby code base caused by lack of typing as evidence for why void * is problematic in C/C++? If you read GP's post you'll understand it exemplifies exactly the issue that the likes of (void *) present in C. I mean, read the message, particularly this: > later on it's not that easy to understand which shape it should have in the specific conditional branch you're trying to fix That is…

> later on it's not that easy to understand which shape it should have in the specific conditional branch you're trying to fix

This is not idiomatic C. I have no doubt that someone (likely many someones) have written a function that takes a void * and then internally does some insane half baked dynamic typing. But I’ve never seen it and it’s not common.

You also cannot fix this behavior by changing the pointer type. The type of the pointer is essentially meaningless in this case.

> That is exactly the purpose of void *. By design. It's a pointer to an unspecified type. The unspecified type is exactly why this thing is used.

This is also the purpose of byte * in the examples. Coercing an arbitrary pointer from void to byte doesn’t accomplish anything. It’s lipstick on a pig at best.

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

#173
post #106

Earlier quoted context omitted.

I think the article names hashing as a use-case, which I can somewhat still agree. Operations that only depend on the bytes, I guess. But yeah, most things worth saying about this article have been said here already

Sure, if the function is expected to not treat the data as anything but bytes, then it might be acceptable in narrow circumstances. But in such a case I'd argue FOR the ceremony, as a way of declaring from the API "The input is a sequence of bytes that I won't treat as anything other than a sequence of bytes", and declaring from each and every call site: "This is not a mistake; we really are 'converting' this struct…

A more immediate concern for hashing by treating a struct as a bag of bytes is padding.

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

#174

Earlier quoted context omitted.

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.

You have some valid points, but I think you could have made them without the gross hyperbole and slander.

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

#175

Earlier quoted context omitted.

But these compilation times optimizations don't significantly undermine the other goals. Given that we're talking about std::span , a pretty small template all things considered, I think practical evidence (e.g. actual cases) of impact is needed.

The problem is not the size of the span template, it’s putting whatever logic into a header file instead of a sealed compilation unit. In a void* function prototype, whatever network code or gnarly dependency is all shielded behind a compilation unit. If you make it a template function the compiler will have to (re)process a lot of code. You could make the interface templatized in a header file and have the actual im…

It is, however, more readable and maintainable.

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

#176
post #156

Earlier quoted context omitted.

> Arguably, the void* does that as well? How do you figure? The type is a pointer to quite literally anything, including nothing (ie a pointer that cannot be dereferenced). If you're working with bytes, indicate this with the type.

All pointers in C/C++ can point to “nothing”. Swapping “void *” for “byte *” is basically an aesthetic choice.

Right. So why not use a typed reference?

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

#177
post #156

Earlier quoted context omitted.

All pointers in C/C++ can point to “nothing”. Swapping “void *” for “byte *” is basically an aesthetic choice.

Right. So why not use a typed reference?

1. void * is a type

2. The functions in question here operate on raw memory. void * conveys the same information as byte *. It’s not untyped vs typed. It’s two different types that convey the same information but require slightly different mechanics to obtain.

3. The author explained his reasoning for preferring void * over byte * (or uint8_t *)

4. A reference also isn’t the same thing as a pointer in C++

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

#178

Earlier quoted context omitted.

But these compilation times optimizations don't significantly undermine the other goals. Given that we're talking about std::span , a pretty small template all things considered, I think practical evidence (e.g. actual cases) of impact is needed.

The problem is not the size of the span template, it’s putting whatever logic into a header file instead of a sealed compilation unit. In a void* function prototype, whatever network code or gnarly dependency is all shielded behind a compilation unit. If you make it a template function the compiler will have to (re)process a lot of code. You could make the interface templatized in a header file and have the actual im…

You don't have to make function templated to use span. Given:

  void DoSomething(void* p, size_t numBytes);
Presuming p to mean a buffer of bytes, the direct declaration equivalent using span would be:

  void DoSomething(std::span /* or std::span */ p);
No templated logic in header files necessary. The only template instantion is std:span, which, in theory, should already be used in most files. The author argues this still makes the code more complex, because of the need of reinterpret_cast, but does it actually?

std::span provides multiple ways of safely accessing data. For one, it provides an contiguous iterator, so you get access to the algorithms library basically for free. Second, you get safer accessors to the data inside, such as at, and even [] can be protected through contracts. Finally, even if you don't care about/can use these features, tying the pointer and length together reduces chances for variable confusion.

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

#179

Earlier quoted context omitted.

> Fair point (although to be honest: 'complexify' feels a bit of an exaggeration here to me) Both uint8_t and std::byte require a header ( or ) which may expose you to platform x config specific build failures if you do any conditional #including, and the latter is a whole damn enum class with a strange adversion to arithmetic, where `byte |= 1` becomes `byte |= std::byte(1)`, `byte += 1` becomes `byte = std::byte(st…

> and the latter is a whole damn enum class with a strange adversion to arithmetic, where `byte |= 1` becomes `byte |= std::byte(1)`, `byte += 1` becomes `byte = std::byte(std::to_integer (byte) + 1);`, and both become something you can accidentally step into in your full debug builds because it's an actual function call (at least on MSVC - still extra instructions on clang/gcc, but I can see the dang call instructio…

> That's the point, std::byte is for opaque bytes. You're not expected to do arithmetic directly just like you can't do arithmetic on void.

That'd make a whole lot more sense to me if `std::byte` didn't explicitly implement a whole bunch of bit twiddling operations:

    > |= &= ^= | & ^ ~
This isn't opaque. This is a poor man's half-assed `std::bitset` with a guaranteed `sizeof(std::byte) == 1`.

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

#180
post #92
post #60

Earlier quoted context omitted.

> a "simple" and "beautiful" function that was mangled into incomprehensibility over the years, and where if a more expressive type signature had been written from the start, it would have restricted the damage caused over time. ...Can you give a concrete example? I've been programming literally since the 80s and that doesn't ring true at all for me.

Have you been in static types the whole time? It's a really, really common failure state in dynamic programming languages, the Everything Function, that started out as something simple, but then someone added a flag to make it also do this other thing, and then you need a flag to only do that other thing sometimes, and someone needed to operate on multiple things so they made a string parameter also optionally an arr…

> Have you been in static types the whole time?

In fact, Python has been my most-used language for 15+ years and I rarely use annotations.

> the Everything Function

This doesn't happen in my own code; 10 lines is unusually long for me. In others' code, it comes across that the problem is more to do with not properly splitting up the task (a lot of the time, a reluctance to extract loop bodies, in particular). The case logic does have to go somewhere and it isn't always practical to hide it with polymorphism (which has its own problems; I write my own classes quite a bit less than average I would say, and especially avoid inheritance). It's often better when you have one thing that lays out the case logic explicitly while delegating all the actual work.

But aside from Everything Functions, a lot of code bases have more of a problem with the Everything Class that just contains way too much state and still doesn't neatly refactor the work away (and where there are passing attempts to extract a few lines, they often end up in a "method" that doesn't actually touch `self`).

> I mean that one of the "everything functions" I tore down had two distinct calling patterns that were distinct functions that not only shouldn't have been festooned with so many options, but never should have been one function at all because they weren't even conceptually the same thing or even particularly related.

Yeah, sounds like you work in unusually unpleasant circumstances.

But I don't really see how a lack of type expression leads to this kind of thing. The default assumption for the type of a parameter, in untyped Python, should be: "an object that supports the operations currently used with it in the existing code". Going beyond that is like adding additional methods to a class that hasn't actually been written, and needs to be well considered.

A lot of people on HN don't seem to like dynamic typing. I think it's more that it's not for them, and that's fine. There will always be people with different mental models.

Post reply on HN