[move] BOOST_RV_REF(TypeWithoutMoveEmulation) parameters
Hi, I'd like to use BOOST_RV_REF, with both C++98 and C++11 compilers, for some sink-functions, with types that are not "move-enabled". I.e. types that the sink can "consume"/"move from" by other means (swapping, calling .release(), ...). Some of them I can - eventually - change to be fully move-enabled, others I can not (e.g. STL classes). I just assumed that this was possible by simply passing the argument with boost::move(arg). Apparently it's not though, because boost::move will return a plain T& for types without move emulation. For the time being I have defined my own helper function template "move_any" that will always return a rv<T>&. Is there such a function that I missed? And if not, would you think it'd make sense to provide one (I certainly think it would)? Regards, Paul Groke
On 20/05/2017 02:16, Groke, Paul via Boost wrote:
I'd like to use BOOST_RV_REF, with both C++98 and C++11 compilers, for some sink-functions, with types that are not "move-enabled". I.e. types that the sink can "consume"/"move from" by other means (swapping, calling .release(), ...). Some of them I can - eventually - change to befully move-enabled, others I can not (e.g. STL classes).
I just assumed that this was possible by simply passing the argument with boost::move(arg). Apparently it's not though, because boost::move will return a plain T& for types without move emulation. For the time being I have defined my own helper function template "move_any" that will always return a rv<T>&.
Is there such a function that I missed? And if not, would you think it'd make sense to provide one (I certainly think it would)?
Regards, Paul Groke
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
On 20/05/2017 02:16, Groke, Paul wrote:
I'd like to use BOOST_RV_REF, with both C++98 and C++11 compilers, for some sink-functions, with types that are not "move-enabled". I.e. types that the sink can "consume"/"move from" by other means (swapping, calling .release(), ...). Some of them I can - eventually - change to be fully move-enabled, others I can not (e.g. STL classes).
I just assumed that this was possible by simply passing the argument with boost::move(arg). Apparently it's not though, because boost::move will return a plain T& for types without move emulation. For the time being I have defined my own helper function template "move_any" that will always return a rv<T>&.
Is there such a function that I missed? And if not, would you think it'd make sense to provide one (I certainly think it would)?
Apologies for the prior blank reply; accidentally fat-fingered the Send button. I don't think this makes sense. The Boost.Move emulation library is exactly that -- an as-close-to-possible *emulation* of rvalue-reference-based C++11 move for pre-C++11 compilers. As such it is naturally designed for modification of the type-to-be-moved (it requires adding move constructors etc as applicable). Additionally, when actually compiled in C++11 it gracefully upgrades to actual rvalue references -- as such the rv<T> class disappears entirely; this is an implementation detail and you're not supposed to use it outside of the macros in the documented interface of Boost.Move. (Also, I have grave doubts that returning one by reference could be correct code, as they're typically locally constructed IIRC.) If you want to write some generic code around swap(), then do that; this exists for both C++11 and prior. Also, be careful with move. It's a shiny new toy so people are excited to play with it, but if not used carefully (*especially* with a non-C++11 STL) it can easily lead to incorrect behaviour or degraded performance. Learn both when and when *not* to use it.
On Monday, 22. Mai 2017 03:21, Gavin Lambert wrote:
I just assumed that this was possible by simply passing the argument with boost::move(arg). Apparently it's not though, because boost::move will return a plain T& for types without move emulation. For the time being I have defined my own helper function template "move_any" that will always return a rv<T>&.
Is there such a function that I missed? And if not, would you think it'd make sense to provide one (I certainly think it would)?
Apologies for the prior blank reply; accidentally fat-fingered the Send button.
I don't think this makes sense. The Boost.Move emulation library is exactly that -- an as-close-to-possible *emulation* of rvalue-reference-based C++11 move for pre-C++11 compilers. As such it is naturally designed for modification of the type-to-be-moved (it requires adding move constructors etc as applicable).
Additionally, when actually compiled in C++11 it gracefully upgrades to actual rvalue references -- as such the rv<T> class disappears entirely; this is an implementation detail and you're not supposed to use it outside of the macros in the documented interface of Boost.Move. (Also, I have grave doubts that returning one by reference could be correct code, as they're typically locally constructed IIRC.)
I'm sorry, I guess that was confusing. My "move_any" function does of course return an rvalue reference in >= C++11 mode. It just doesn't behave differently for types that are not move-enabled in C++98 mode. Regarding returning boost::rv by reference - I didn't invent that, that's exactly what Boost.Move does. It's a necessary hack to allow Boost.Move to work more or less transparently. I simply copied the definition of boost::move sans the "should I return boost::rv<T>& for this type" checks in C++98 mode.
If you want to write some generic code around swap(), then do that; this exists for both C++11 and prior.
I want to write sink functions that can "consume" a value. They're not member functions of the class they're consuming. In C++11 I would use rvalue references for this. In C++98 I don't know any elegant way of expressing this. Of course I could write the function to take a normal lvalue reference, but that's not visible at the call site. If I use BOOST_RV_REF instead, then it becomes visible at the call site because the caller explicitly has to write "move", which IMO is a great improvement. Of course I could use my own helper type for this. But since everything (except the "move_any" function) is already there in Boost.Move, and I also want to fully move-enable some types in the future using Boost.Move (which would require interoperability between my own solution and Boost.Move), I'd like to use Boost.Move/BOOST_RV_REF for it. As an example, given this C++11 code: void Foo::StringSink(std::string&& s) { ... } ... void Bar::SomeEventHandler() { this->foo->StringSink(std::move(this->stringMember)); // stringMember is never used after this point } I'd like to be able to emulate that in C++98 ala void Foo::StringSink(BOOST_RV_REF(std::string) s) { ... } ... void Bar::SomeEventHandler () { this->foo->StringSink(boost::move_any(this->stringMember)); // stringMember is never used after this point } Boost.Move allows me to do this, but only if the type passed to boost::move() is itself move enabled. Which IMO is an unnecessary restriction.
Also, be careful with move. It's a shiny new toy so people are excited to play with it, but if not used carefully (*especially* with a non-C++11 STL) it can easily lead to incorrect behaviour or degraded performance. Learn both when and when *not* to use it.
I don't want to come across as confrontational, but ... what makes you think I don't know when and when not to use it? I'm working with more or less performance critical code here. Not so much that I can warrant writing code that's very hard to write and very hard to understand for the sake of performance. Which is why I don't want to use T& for my sink parameters. But it's definitely code where I don't want to copy e.g. a std::string if I don't have to copy it. And there are quite a few places where I have to "move" strings (or objects containing strings) around, where I cannot use C++98 copy elision stuff. And regarding implementation details: One of the reasons why I would want this to be implemented in Boost.Move is that I don't want my code to rely on the internals of Boost.Move. Regards, Paul Groke
As an example, given this C++11 code:
void Foo::StringSink(std::string&& s) { ... } ... void Bar::SomeEventHandler() { this->foo->StringSink(std::move(this->stringMember)); // stringMember is never used after this point }
I'd like to be able to emulate that in C++98 ala
void Foo::StringSink(BOOST_RV_REF(std::string) s) { ... } ... void Bar::SomeEventHandler () { this->foo->StringSink(boost::move_any(this->stringMember)); // stringMember is never used after this point } Boost.Move allows me to do this, but only if the type passed to boost::move() is itself move enabled. Which IMO is an unnecessary restriction.
In C++98 you can pass a parameter that is not movable (meaning, implements
internal boost::rv facility) to boost::move it will just send a reference
to it.
If the type doesn't implements boost::rv, I think you're stuck with copies
and such, so it's not an unnecessary restriction.
As for stl containers such as std::string or std::vector, you could use
boost::container::string (vector, etc.), they emulate move semantics for
C++98 compilers, and as far as I used them in my applications, in most
cases they outperform stl containers.
But the main drawback that comes with this workaround is the
interoperability with other APIs that use std::string (or other) because
they don't know boost::container, so you have to write more code that
handle this use case.
Regards
Danny
2017-05-22 10:44 GMT+02:00 Groke, Paul via Boost
On Monday, 22. Mai 2017 03:21, Gavin Lambert wrote:
I just assumed that this was possible by simply passing the argument with boost::move(arg). Apparently it's not though, because boost::move will return a plain T& for types without move emulation. For the time being I have defined my own helper function template "move_any" that will always return a rv<T>&.
Is there such a function that I missed? And if not, would you think it'd make sense to provide one (I certainly think it would)?
Apologies for the prior blank reply; accidentally fat-fingered the Send button.
I don't think this makes sense. The Boost.Move emulation library is exactly that -- an as-close-to-possible *emulation* of rvalue-reference-based C++11 move for pre-C++11 compilers. As such it is naturally designed for modification of the type-to-be-moved (it requires adding move constructors etc as applicable).
Additionally, when actually compiled in C++11 it gracefully upgrades to actual rvalue references -- as such the rv<T> class disappears entirely; this is an implementation detail and you're not supposed to use it outside of the macros in the documented interface of Boost.Move. (Also, I have grave doubts that returning one by reference could be correct code, as they're typically locally constructed IIRC.)
I'm sorry, I guess that was confusing. My "move_any" function does of course return an rvalue reference in >= C++11 mode. It just doesn't behave differently for types that are not move-enabled in C++98 mode. Regarding returning boost::rv by reference - I didn't invent that, that's exactly what Boost.Move does. It's a necessary hack to allow Boost.Move to work more or less transparently. I simply copied the definition of boost::move sans the "should I return boost::rv<T>& for this type" checks in C++98 mode.
If you want to write some generic code around swap(), then do that; this exists for both C++11 and prior.
I want to write sink functions that can "consume" a value. They're not member functions of the class they're consuming. In C++11 I would use rvalue references for this. In C++98 I don't know any elegant way of expressing this. Of course I could write the function to take a normal lvalue reference, but that's not visible at the call site. If I use BOOST_RV_REF instead, then it becomes visible at the call site because the caller explicitly has to write "move", which IMO is a great improvement. Of course I could use my own helper type for this. But since everything (except the "move_any" function) is already there in Boost.Move, and I also want to fully move-enable some types in the future using Boost.Move (which would require interoperability between my own solution and Boost.Move), I'd like to use Boost.Move/BOOST_RV_REF for it.
As an example, given this C++11 code:
void Foo::StringSink(std::string&& s) { ... } ... void Bar::SomeEventHandler() { this->foo->StringSink(std::move(this->stringMember)); // stringMember is never used after this point }
I'd like to be able to emulate that in C++98 ala
void Foo::StringSink(BOOST_RV_REF(std::string) s) { ... } ... void Bar::SomeEventHandler () { this->foo->StringSink(boost::move_any(this->stringMember)); // stringMember is never used after this point }
Boost.Move allows me to do this, but only if the type passed to boost::move() is itself move enabled. Which IMO is an unnecessary restriction.
Also, be careful with move. It's a shiny new toy so people are excited to play with it, but if not used carefully (*especially* with a non-C++11 STL) it can easily lead to incorrect behaviour or degraded performance. Learn both when and when *not* to use it.
I don't want to come across as confrontational, but ... what makes you think I don't know when and when not to use it? I'm working with more or less performance critical code here. Not so much that I can warrant writing code that's very hard to write and very hard to understand for the sake of performance. Which is why I don't want to use T& for my sink parameters. But it's definitely code where I don't want to copy e.g. a std::string if I don't have to copy it. And there are quite a few places where I have to "move" strings (or objects containing strings) around, where I cannot use C++98 copy elision stuff.
And regarding implementation details: One of the reasons why I would want this to be implemented in Boost.Move is that I don't want my code to rely on the internals of Boost.Move.
Regards, Paul Groke
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/ mailman/listinfo.cgi/boost
danny kabrane wrote:
As for stl containers such as std::string or std::vector, you could use boost::container::string (vector, etc.), they emulate move semantics for C++98 compilers, and as far as I used them in my applications, in most cases they outperform stl containers.
Thanks for the tip. I didn't know about boost::container::string.
But the main drawback that comes with this workaround is the interoperability with other APIs that use std::string (or other) because they don't know boost::container, so you have to write more code that handle this use case.
Yes. Which is why we probably won't be able to use boost::container::string. Anyway it's good to know about the possibility. Regards, Paul Groke
participants (3)
-
danny kabrane
-
Gavin Lambert
-
Groke, Paul