|
Twerk from Home posted:Also, I thought that compilers were smart enough to automatically use the move constructor if you pass a temporary into the constructor and a move constructor exists? I'll be more careful about make_unique for sure. ah yeah you're right that it would use the move constructor if one exists, and the copy constructor if it didn't. I was just trying to emphasize that two Derived objects were being constructed: one passed as an argument to make_unique, and one inside the body of make_unique
|
# ? Nov 16, 2022 05:36 |
|
|
# ? Jun 7, 2024 10:12 |
|
I would in general recommend worrying less about using rvalue references and doing moves everywhere. Moves are specifically there for when you need to pass ownership of types that are very large. A breakpoint of 128 bits/2x machine width is a common heuristic but you probably don't need to care until you're in the kbs. It is rare that you need to pass ownership of very large types. Most classes you write will not need to implement move semantics, most functions (and constructors) you write will not take rvalue references. The exceptions are generally around collection types, classes that own collection types, and classes (like unique_ptr) that delete their copy constructor and copy assignment so they can only be moved. Specifically: unique_ptr is not a very large type, it consists only of a single pointer. You should never pass it by reference. Pass unique_ptr by value if you want to pass ownership, or pass a (const) reference or maybe a pointer to the underlying type if you do not want to pass ownership. This is usually a good way to treat other classes that cannot be copied. Twerk from Home posted:Also, I thought that compilers were smart enough to automatically use the move constructor if you pass a temporary into the constructor and a move constructor exists? I'll be more careful about make_unique for sure. C++ code:
Edit: for the use case at hand Twerk from Home posted:In my specific case, to get concrete, I have something that's processing streaming data, and I don't want to put the whole "is it compressed? We support multiple types of compression. Is it even coming from a file?" logic into the Consumer. Instead I want to pass in a ready-to-go std::istream, and I do want ownership of it to move to the consumer. This way we can have the logic to turn paths or sockets or whatever into working istreams in one place, and the different ways to actually consume that data in another. Options: - Make consuming data a function call that takes a reference to the stream, rather than have a persistent Consumer object that takes ownership. Having a Consumer class can be an implementation detail, one that doesn't need ownership. - Just read data to buffers, then pass buffers to Consumers. - Manage the lifetimes elsewhere and just let the Consumer take a reference to the stream, if you really have to. Depends on the particulars of your situation and data, I suppose. 2: Not sure how relevant this is but be aware that extending the streaming libraries is as I understand it quite tricky if that's a thing you think you might need. You need to create a class derived from std::basic_streambuf, then construct a std::istream from that class. It's not straight-forward. Xerophyte fucked around with this message at 07:33 on Nov 16, 2022 |
# ? Nov 16, 2022 06:15 |
|
Ihmemies posted:
When I was teaching programming, I would spend lot of time telling students to read their code as they wrote it, rather than what they think they wrote, or what they wish it did. If you actually read the code, the issue is obvious.
|
# ? Nov 16, 2022 09:23 |
|
cheetah7071 posted:The syntax for what you're trying to do in Consumer is a little weird because unique_ptr is specifically for cases where only one portion of the code ever needs access to the object. If the calling function constructs the unique_ptr and then passes it to Consumer's constructor, then two parts of the code have access to it simultaneously, which breaks the core guarantee of the class, so it doesn't compile. The two options are to use a shared_ptr instead (if you really want to allow both the calling function and the Consumer object to access it) or to have the constructor for Consumer instead take the arguments you need for it to construct its own unique_ptr This is all so wrong, holy poo poo. C++ code:
------------------ The reason why the move needs to be here C++ code:
----------------- Finally there is a discussion on whether you should accept unique_ptr (and other move-only types) by value or by &&. I am firmly in the side of value, but using && isn't actually wrong either. You just need to be careful about the difference between C++ code:
|
# ? Nov 16, 2022 09:36 |
|
Ah well I'll just accept being wrong then. Sorry! e: the pattern isn't one I've encountered so far and it looks awkward to me to have functions that expect std::move. I can see the advantages though. ee: that'll teach me for thinking that working with this absolutely massive language for two years has actually taught me anything close to all the ways it's okay to use it lol cheetah7071 fucked around with this message at 10:01 on Nov 16, 2022 |
# ? Nov 16, 2022 09:37 |
|
Xarn posted:When I was teaching programming, I would spend lot of time telling students to read their code as they wrote it, rather than what they think they wrote, or what they wish it did. If you actually read the code, the issue is obvious. Multiple people have said this so far but I still have no idea. I never spot my own mistakes and rarely learn from them so most likely this one will go into the same ever growing pile of mistakes not learned from.
|
# ? Nov 16, 2022 10:26 |
|
Ihmemies posted:Multiple people have said this so far but I still have no idea. I never spot my own mistakes and rarely learn from them so most likely this one will go into the same ever growing pile of mistakes not learned from. Try it line by line: what does this line do C++ code:
C++ code:
C++ code:
C++ code:
|
# ? Nov 16, 2022 10:38 |
|
Geez apparently the functionality I had in my head for what std::move does is actually what std::forward does; I thought move would only produce an rvalue if the input was an rvalue. That'll teach me for even thinking I understand all the core functionality of this language even after two years. Silly me for thinking a language this complicated is something I'll ever really master self taught
|
# ? Nov 16, 2022 10:44 |
|
forward is great, a function name given to static_cast<decltype(x)&&>(x)
|
# ? Nov 16, 2022 10:57 |
|
Ihmemies posted:Multiple people have said this so far but I still have no idea. I never spot my own mistakes and rarely learn from them so most likely this one will go into the same ever growing pile of mistakes not learned from. Put every single calculation and/or check in a separate line, catching the value in a separate variable and check it under debugger/print it. Don't use the auto keyword for the types.
|
# ? Nov 16, 2022 12:58 |
|
All the talk earlier about pointers and inheritance earlier makes me think: it’s interesting how polymorphism and pointers are so fundamentally linked. I.e., if you want a vector of polymorphic objects you’re just expected to do it as a vector of pointers (usually to dynamically allocated memory). Since this pattern is bad for cache usage, it would be nice if it were easier to do polymorphic stuff without each individual object being a dynamic allocation. I know it’s possible: I’ve written for instance a container that exposes a “vector<Base*>” interface, but internally stores each derived type’s instances continuously in its own separate vector. But this was pretty ugly and difficult. it makes me wonder if there’s a way for a language to encode polymorphism to make something like the above way easier to do, or maybe even the default way to use polymorphism.
|
# ? Nov 16, 2022 13:33 |
giogadi posted:All the talk earlier about pointers and inheritance earlier makes me think: it’s interesting how polymorphism and pointers are so fundamentally linked. I.e., if you want a vector of polymorphic objects you’re just expected to do it as a vector of pointers (usually to dynamically allocated memory). Since this pattern is bad for cache usage, it would be nice if it were easier to do polymorphic stuff without each individual object being a dynamic allocation. You need to know the storage size of the objects, and then either have your array have variable-size elements (meaning you can no longer do direct look-up but need to walk it like a linked list) or you need to know the maximum storage size of any derived type. If you know all the possible derived types, you can use a union.
|
|
# ? Nov 16, 2022 13:56 |
|
Xarn posted:Try it line by line: It can help to read what you wrote, but if you're just reading "something with the pointer" every line you're not gonna see your bug. My suggestion to help a beginner would be to read * as "contents of" and & as "address of", unless they are in type declarations in which case they are "pointer to" or "reference to" (ignoring for now the bonkers cases like && in a declaration.)
|
# ? Nov 16, 2022 14:12 |
|
nielsm posted:If you know all the possible derived types, you can use a union. Edit: I guess a union is good though if you're doing with derived types, because you can handle it as the base class without needing to know the inner type.
|
# ? Nov 16, 2022 14:14 |
|
In theory if you have a MyVariant = variant<Base, Derived1, …, DerivedN> you should be able to write a Base& view(MyVariant mv) { return visit(mv, [](auto& elem) -> Base& { return dynamic_cast<Base&>(elem);}); } or something
|
# ? Nov 16, 2022 14:30 |
|
Here’s a great treatment on the subject of polymorphic vectors: https://johnysswlab.com/process-polymorphic-classes-in-lightning-speed/ But my main point is to wonder whether there are language features that could make this kind of thing easier - all of these solutions, while clever and effective, take a lot of work to implement giogadi fucked around with this message at 14:40 on Nov 16, 2022 |
# ? Nov 16, 2022 14:37 |
|
Ihmemies posted:Multiple people have said this so far but I still have no idea. I never spot my own mistakes and rarely learn from them so most likely this one will go into the same ever growing pile of mistakes not learned from. How do you pronounce & and * when used as a unary operator? I recommend "address of" and "value at", respectively. You may have learned reference/dereference, which is what they do but not what it gives you.
|
# ? Nov 16, 2022 15:42 |
|
cheetah7071 posted:Geez apparently the functionality I had in my head for what std::move does is actually what std::forward does; I thought move would only produce an rvalue if the input was an rvalue. That'll teach me for even thinking I understand all the core functionality of this language even after two years. Silly me for thinking a language this complicated is something I'll ever really master self taught C is a better language than C++, and more people should use it.
|
# ? Nov 16, 2022 15:45 |
|
Well the solution was to not use anything. Just remove everything Thanks, and sorry for being so bad. Anyways. I'm still battling an eternal struggle against on how to check for keys in a map. Is this legal?
|
# ? Nov 16, 2022 16:03 |
|
Coming from Python, you'll find exceptions in C++ are intended for more "exceptional" cases. std::map::find() returns a special value when the key is missing, and doesn't require a recent language version.
|
# ? Nov 16, 2022 16:10 |
|
Xerophyte posted:Edit: for the use case at hand I'm going to have a think about the factoring of this application and see how I can move less poo poo around. What if I want to take piped input from cin? cin is an istream, so I could either pass it in directly, or more likely add cin as a source for a boost filtering_stream, because we have compressed data flowing around. I'm guessing your concern is that it'd be easy to accidentally end up with multiple places reading from cin at the same time and blow my face off? I'm wanting the consumer to manage the stream because one part of the application is opening the multiple files that we're reading from, and the consumers get used in different places to read through those files. Xarn posted:Finally there is a discussion on whether you should accept unique_ptr (and other move-only types) by value or by &&. I am firmly in the side of value, but using && isn't actually wrong either. You just need to be careful about the difference between Whether you accept them by value or by &&, don't you have to call std::move where you are calling the function anyway because calling sink1(myFoo) with a std::unique_ptr<Foo> and a sink1 that accepts values will try to call the deleted copy constructor anyway? Hence a need to call sink1(std::move(myFoo)), which makes me feel like I should be accepting a && anyway. giogadi posted:Here’s a great treatment on the subject of polymorphic vectors: I'm a little surprised at just how clunky basic dependency injection feels. I get that most C++ work at this point is done in the context of older codebases with a ton of internal libraries available to solve most of these problems, but the language and stdlib itself could absolutely offer some more ergonomic ways to do this. Ihmemies's example of unordered_map::contains not existing until C++ 20 is such a great example of the C++ stdlib being huge but still missing basics.
|
# ? Nov 16, 2022 16:21 |
|
Dijkstracula posted:Coming from Python, you'll find exceptions in C++ are intended for more "exceptional" cases. OK, I'll just do it with find() then.
|
# ? Nov 16, 2022 16:23 |
|
Twerk from Home posted:Ihmemies's example of unordered_map::contains not existing until C++ 20 is such a great example of the C++ stdlib being huge but still missing basics. code:
|
# ? Nov 16, 2022 16:57 |
|
right, OP shouldn't feel that using `if (m.find(k) != m.end()) {...}` is a hack or an antipattern here.
|
# ? Nov 16, 2022 17:08 |
|
Dijkstracula posted:right, OP shouldn't feel that using `if (m.find(k) != m.end()) {...}` is a hack or an antipattern here. I know. I was just curious. This is I think how it should be done: code:
|
# ? Nov 16, 2022 17:19 |
|
Ihmemies posted:I know. I was just curious. find returns an iterator you can return the value from. You are doing the lookup twice
|
# ? Nov 16, 2022 17:31 |
|
Sweeper posted:find returns an iterator you can return the value from. You are doing the lookup twice code:
|
# ? Nov 16, 2022 17:35 |
|
If you are new to cpp bookmark https://www.cppreference.com or add a quick search so you can type “cpp std::map” into url bar thing to lookup docs. I look things up all the time because c++ is inconsistent and many apis work in ways which make sense in the context of the standard, but aren’t intuitive. It’s a great site, bless the maintainers.
|
# ? Nov 16, 2022 17:55 |
|
giogadi posted:All the talk earlier about pointers and inheritance earlier makes me think: it’s interesting how polymorphism and pointers are so fundamentally linked. I.e., if you want a vector of polymorphic objects you’re just expected to do it as a vector of pointers (usually to dynamically allocated memory). Since this pattern is bad for cache usage, it would be nice if it were easier to do polymorphic stuff without each individual object being a dynamic allocation. If you want this (and it's often a reasonable thing to want), then you probably want to create a local memory arena to allocate your objects on. You'd do something like C++ code:
You ultimately need to get a base class pointer or reference to collect the disparate derived values, yes. There's just no such thing as a polymorphic value in C++: a value is the thing it is. Is there a language that has value semantics for complex types, and also somehow allows those values to themselves be polymorphic? I struggle to see how that could be done, but I might be blinkered. Twerk from Home posted:I'm going to have a think about the factoring of this application and see how I can move less poo poo around. What if I want to take piped input from cin? cin is an istream, so I could either pass it in directly, or more likely add cin as a source for a boost filtering_stream, because we have compressed data flowing around. I'm guessing your concern is that it'd be easy to accidentally end up with multiple places reading from cin at the same time and blow my face off? I'm wanting the consumer to manage the stream because one part of the application is opening the multiple files that we're reading from, and the consumers get used in different places to read through those files. My concern is that you said "I want to pass in a ready-to-go std::istream, and I do want ownership of it to move to the consumer." istream has disabled moves specifically to ensure it is not possible to do that, because it is not in general a safe operation. An istream may be a system resource that cannot be locally owned. Moving is only enabled for classes derived from istream that represent something that can be owned and have its ownership transferred, notably ifstreams. The discussion gave me the impression that you intended to get around this by passing around std::unique_ptr<std::istream>s, which technically will work as long as you only use them for streams that can be moved (or, well, constructed/deleted locally). It smells and I would try to not do that, though.
|
# ? Nov 16, 2022 18:31 |
|
Ihmemies posted:Any chance to get language specific color coding for code tag at the forums btw? [ code=c++ ] ... [ /code ]
|
# ? Nov 16, 2022 18:38 |
|
Twerk from Home posted:
Yes, you need to call std::move in both cases. But they have different lifetime semantics. Given C++ code:
This is why I prefer taking these types by value, because then I can be sure that when the function exits, no matter the way it does so, my input has been already taken care of and I can't see the old value. This makes it harder to get to inconsistent state. leper khan posted:C is a better language than C++, and more people should use it.
|
# ? Nov 16, 2022 18:40 |
|
Xerophyte posted:Is there a language that has value semantics for complex types, and also somehow allows those values to themselves be polymorphic? I struggle to see how that could be done, but I might be blinkered. Haskell type classes? I don't think it's exactly the same idea but it's probably as close as you're going to get.
|
# ? Nov 16, 2022 18:43 |
|
I'll put up with all the C++ nonsense just to have namespaces and std::vector
|
# ? Nov 16, 2022 18:43 |
|
Speaking of all the chatter about the bizarre complexities of modern C++, anyone have thoughts on Herb Sutter's new project? https://www.youtube.com/watch?v=ELeZAKCN4tY I... kinda like the idea of it. It would be (theoretically) usable alongside current C++ code without any glue poo poo, and it would dramatically simplify the language while keeping (almost) all of the zero-overhead abstraction, and the safety of modern idiomatic C++.
|
# ? Nov 16, 2022 18:44 |
|
None of Herb Sutter's projects have gone anywhere and I don't expect this one to be the exception.
|
# ? Nov 16, 2022 18:49 |
|
Stop doing C++ "Yes please give me an RVALUE REFERENCE. Please give me SFINAE" - Statements dreamed up by the utterly deranged. They have played us for absolute fools. etc.
|
# ? Nov 16, 2022 19:22 |
|
more falafel please posted:Speaking of all the chatter about the bizarre complexities of modern C++, anyone have thoughts on Herb Sutter's new project?
|
# ? Nov 16, 2022 19:31 |
|
pseudorandom name posted:Stop doing C++ Yes. Return to C It's honestly a very pleasant language to work with. And it even gets useful features like embed before C++
|
# ? Nov 16, 2022 19:36 |
|
Xarn posted:This is why I prefer taking these types by value, because then I can be sure that when the function exits, no matter the way it does so, my input has been already taken care of and I can't see the old value. This makes it harder to get to inconsistent state. Which is to say, the piece of code that did this passed unit tests with one set of optimizations and ended up throwing a use-after-free or delete-after-free exception or something the other way because of some deletion-order stuff that relied on the unique_ptr having been destroyed before it got to some other stuff.
|
# ? Nov 16, 2022 20:06 |
|
|
# ? Jun 7, 2024 10:12 |
|
I give cppfront even less chance for success than I give to Carbon.
|
# ? Nov 16, 2022 20:11 |