Live data from Hacker News

What to do with C++ modules?

nibblestew.blogspot.com

231–240 of 269 posts

Re: What to do with C++ modules?

#231
post #225

Earlier quoted context omitted.

Great. template void drop(std::unique_ptr &&) {} Also, you don’t really need this because of RAII. You can make it simpler, and here’s how you’d do this in C++26. template void drop(T &&) {} It can get even simpler! void drop(std::movable auto){} Do you see my point about C++ incorporating the good ideas at a glacial pace?

> template void drop(std::unique_ptr &&) {} So what happens for objects that aren't behind a unique_ptr? > Also, you don’t really need this because of RAII. drop() indeed isn't used much because of RAII, but it is handy for those instances where you do actually want to dispose of something "early".

See the other versions I added.

> dispose of something "early".

This is a bit of an anti pattern in C++, but doable of course.

Re: What to do with C++ modules?

#232
post #209

Earlier quoted context omitted.

What developers actually wanted, even years back when C++ didn't have anything named "move" was the destructive move semantic. It's an elegant, easy to understand feature for a language. I like it very much in Rust which has it. C++ didn't get that. The proposal paper at the time says it's impossible (for C++). But what they did propose was the feature you've seen in C++ today, which they call "move", but it has slig…

I am still confused about what you are claiming to be not possible in C++ when you are designing your library. Yes, I understand that there’s a lot of bad code out there and C++ happily enables that. But that was not my point.

What’s not possible in C++ is the actual prevention of the moved-from value from being used after the move.

Entire API’s are designed around this: my type that tracks a background task can have a shutdown() method that moves self, so that if you call foo.shutdown(), you can’t use foo any more.

This is more than just preventing a value from being used, it also facilitates making a rule that “moves are only just memcpy()”, and it can actually be enforced:

C++ move semantics require you to write arbitrary code that pillages the moved-from value (like setting the heap pointer of a moved-from value to nullptr) to ensure the old value is safe: rust just says “nope, there’s no constructor, moves are just a memcpy. We will keep it safe by simply not letting code use the old value.”

C++ can never have this unless they offered yet another mutually incompatible form of move semantics (lol, what would the sigil be? &*&?)

Re: What to do with C++ modules?

#233

Modules are unusable in real projects with real dependencies. Until that changes there’s no chance I’ll look at them.

how are modules related to dependencies in general? You can use your modules at the same time using dependencies via includes. And this works well.

Can't include _any_ header downstream if you import std, it is also unknown how you're gonna export and share modules across dependencies you have no indention of 'porting' to modules...

Re: What to do with C++ modules?

#234
post #210

Earlier quoted context omitted.

That was a giant mistake on C++'s part, as you cannot do this: static if (feature) { int bar() { betty(); }} ... lots of code ... static if (feature) bar(); Forcing a new scope cuts the utility of it about in half, and there's no way around it. But if you need a scope with D's static if: static if (expression) {{ int x; foo(x); }} the extra { } will do it. But, as it turns out, this is rarely desirable.

I would use std::enable_if or C++20 concepts. Both work fine for selectively enabling functions.

C++20 concepts are a step in the right direction, but:

- D had this feature long before C++ did.

- It isn't the same thing as "static if". Without "static if", conditionally compiling variables into classes is much more elaborate, basically requiring subclassing to do, which is not really semantically how subclassing should be used (the result is also way more confusing and oblique than the equivalent directly expressed construct you'd have using "static if").

Re: What to do with C++ modules?

#235
post #225

Earlier quoted context omitted.

Maybe it will be clearer if you read and understand my favourite function from Rust's standard library (its full name is core::mem::drop) pub fn drop (_x: T) {}

Great. template void drop(std::unique_ptr &&) {} Also, you don’t really need this because of RAII. You can make it simpler, and here’s how you’d do this in C++26. template void drop(T &&) {} It can get even simpler! void drop(std::movable auto){} Do you see my point about C++ incorporating the good ideas at a glacial pace?

Edited to inject: Actually on second thoughts maybe none of these even do what you thought they did, they're even sillier than I'd assumed.

So funny thing, your C++ 26 solution doesn't do what my Rust function does.

Actually even the first one doesn't, but you likely don't really care about a std::unique_ptr, so it doesn't feel like a difference, and the negligence only really bites when it's not "just" that pointer type.

You see that Rust function destroyed the T. It's gone, no more T. But your C++ function makes a new T, then destroys the old one, so there's still a T, it's not gone after all.

Re: What to do with C++ modules?

#236
post #231

Earlier quoted context omitted.

> template void drop(std::unique_ptr &&) {} So what happens for objects that aren't behind a unique_ptr? > Also, you don’t really need this because of RAII. drop() indeed isn't used much because of RAII, but it is handy for those instances where you do actually want to dispose of something "early".

See the other versions I added. > dispose of something "early". This is a bit of an anti pattern in C++, but doable of course.

It’s not just for disposing things, it’s also used to decompose things into their constituent parts… like if I had something representing an HTTP response and it contains headers and a body, I could write a `fn into_parts(self) -> (Headers, Body)` that returns the parts you care about while destroying the response object.

This is useful in the grpc library I use, which has a response type with some metadata about the response, and an “inner” value that represents the thing the grpc method returned. If you just want the inner thing and don’t care about the metadata, there’s a `fn into_inner(self)` that destroys the response and leaves you the inner value.

You can write similar methods in C++ but they require leaving the original value in an “empty” state so that you can avoid expensive copying of things while still letting the moved-from value be “safe” to use. But that affects your API design… you now have to make the methods that fetch the “inner” thing be nullable/optional so that you can represent “oh this was moved from so there’s no body any more”. You don’t have to do that in Rust: moves are just a memcpy and the compiler will reject programs that use the moved-from value.

Re: What to do with C++ modules?

#237

Earlier quoted context omitted.

If you think the problem with C++ is that shared_ptr exists, you should probably just use C.

If that is what you take from it, you missed the point entirely. I could care less about shared_ptr. The issue is why should I care ? Why is it on the dev to determine how a pointer should work? Why the dev has to go back and refactor old code to be new again? Why can’t the committee build non-breaking changes to the spec? I would rather have compile flags that make a pointer an “old pointer style” vs having to menta…

that youieven think a compile flag could switch everything shows how little you understand the problem. it is fine not to understand - it isn't possible to understand everything - but stop talking as if there is an easy problem that would work.

Re: What to do with C++ modules?

#238
post #231

Earlier quoted context omitted.

> template void drop(std::unique_ptr &&) {} So what happens for objects that aren't behind a unique_ptr? > Also, you don’t really need this because of RAII. drop() indeed isn't used much because of RAII, but it is handy for those instances where you do actually want to dispose of something "early".

See the other versions I added. > dispose of something "early". This is a bit of an anti pattern in C++, but doable of course.

> See the other versions I added.

I don't think those work either. Not only do neither of those actually end the lifetime of what's passed in, but they have other flaws as well.

> template void drop(T &&) {}

This literally does nothing. Reference parameters don't affect what's passed in on their own - you need at least something on the other end (e.g., a move constructor) to do anything.

For example, consider how this would be instantiated for std::vector:

    template  void drop(std::vector&&) {}
> void drop(std::movable auto){}

I believe this is equivalent to passing by value, so this will actually invoke a copy if what's passed in isn't eligible for a move (or if the move constructor is equivalent to the copy constructor, since copyable subsumes movable). Again, consider how this would be instantiated for std::vector:

    template  void drop(std::vector) {}
> > dispose of something "early".

> This is a bit of an anti pattern in C++, but doable of course.

I don't think you can do quite the same thing in C++ without introducing new scopes.

Re: What to do with C++ modules?

#239

Earlier quoted context omitted.

That could be said of every additional c++ feature since c++0x. The committee has taken backwards compatibility, backwards - refusing to introduce any nuance change in favor of a completely new modus operandi. Which never jives with existing ways of doing things because no one wants to fix that 20 year old codebase.

My experience has been that everyone _wants_ to fix the 20 year old codebase! But it’s hard to justify putting engineers on nebulous refactoring projects that don’t directly add value.

This I understand. The risk of revenue loss if not done right, the risk of revenue loss if not done at all.

Re: What to do with C++ modules?

#240
post #225

Earlier quoted context omitted.

Great. template void drop(std::unique_ptr &&) {} Also, you don’t really need this because of RAII. You can make it simpler, and here’s how you’d do this in C++26. template void drop(T &&) {} It can get even simpler! void drop(std::movable auto){} Do you see my point about C++ incorporating the good ideas at a glacial pace?

Edited to inject: Actually on second thoughts maybe none of these even do what you thought they did, they're even sillier than I'd assumed. So funny thing, your C++ 26 solution doesn't do what my Rust function does. Actually even the first one doesn't, but you likely don't really care about a std::unique_ptr, so it doesn't feel like a difference, and the negligence only really bites when it's not "just" that pointer…

> You see that Rust function destroyed the T. It's gone, no more T. But your C++ function makes a new T, then destroys the old one, so there's still a T, it's not gone after all.

Perhaps your idea of C++ semantics is a bit off?

Post reply on HN