Hi all!
There is the family of boost pointer cast (boost::static_pointer_cast, boost::dynamic_pointer_cast, ...) that allow a developer to write generic code by casting pointers regardless of whether they are some smart pointer (e.g. shared_ptr) or plain pointers.
However, those functions have not been implemented for unique_ptr, because they create a copy of their pointer and unique_pointers can not be copied.
In my case I still needed to cast some unique_ptr and since it's ok to move unique_ptrs I wrote a function that does exactly that.
The code is below – Comments are very, very welcome. It works for my case, but I am sure it is not as generic and safe as it should be ;) . Specifically, for a library, it should be made to work on any kind of smart/plain pointer. Not just unique_ptr.
Is there any interest to include a boost::static_moving_pointer_cast and the other types of casts in boost?
Best,
Karolin
/// Dynamic cast a unique_ptr.
///
/// This function shall move the pointer in in_ptr, into
/// a new unique_pointer of the desire type, dynamic_casting
/// it in the process.
/// In given pointer shall contain a NULL pointer afterwards.
///
/// If unsuccessful, function shall throw an error and leave
/// in_ptr unmodified.
///
/// This behaviour contrasts a bit with the behaviour of the
/// normal dynamic_cast:
/// 1. dynamic_cast copies objects, this moves them since
/// unique_pointers can not be copied.
/// 2. dynamic_cast returns a NULL pointer if the pointer to
/// cast is of an incomplete type, this throws
/// incomplete_cast instead, to ease error handling.
///
/// @tparam T The type to convert to (as non-pointer)
/// @tparam F The type to convert from (as non-pointer)
/// @param in_ptr The unique_ptr to cast
/// @return in_ptr, cast to the desired type
template
On Tue, Oct 6, 2015 at 6:19 AM, Karolin Varner
Hi all!
There is the family of boost pointer cast (boost::static_pointer_cast, boost::dynamic_pointer_cast, ...) that allow a developer to write generic code by casting pointers regardless of whether they are some smart pointer (e.g. shared_ptr) or plain pointers.
However, those functions have not been implemented for unique_ptr, because they create a copy of their pointer and unique_pointers can not be copied.
In my case I still needed to cast some unique_ptr and since it's ok to move unique_ptrs I wrote a function that does exactly that. The code is below – Comments are very, very welcome. It works for my case, but I am sure it is not as generic and safe as it should be ;) . Specifically, for a library, it should be made to work on any kind of smart/plain pointer. Not just unique_ptr.
Is there any interest to include a boost::static_moving_pointer_cast and the other types of casts in boost?
Best, Karolin
/// Dynamic cast a unique_ptr. /// /// This function shall move the pointer in in_ptr, into /// a new unique_pointer of the desire type, dynamic_casting /// it in the process. /// In given pointer shall contain a NULL pointer afterwards. /// /// If unsuccessful, function shall throw an error and leave /// in_ptr unmodified. /// /// This behaviour contrasts a bit with the behaviour of the /// normal dynamic_cast: /// 1. dynamic_cast copies objects, this moves them since /// unique_pointers can not be copied. /// 2. dynamic_cast returns a NULL pointer if the pointer to /// cast is of an incomplete type, this throws /// incomplete_cast instead, to ease error handling. /// /// @tparam T The type to convert to (as non-pointer) /// @tparam F The type to convert from (as non-pointer) /// @param in_ptr The unique_ptr to cast /// @return in_ptr, cast to the desired type template
std::unique_ptr<T> dynamic_moving_pointer_cast(std::unique_ptr<F> &in_ptr) {
The argument should be &&, not &, or else you won't be able to bind to r-values.
// Exception safety: If The dynamic cast throws an error, // or of we throw incomplete_cast, in_ptr will still be // unmodified T *plain_out_ptr = dynamic_cast
(in_ptr.get()); if (plain_out_ptr == NULL && in_ptr.get() != NULL) throw incomplete_cast(); // Exception safety: Both operations are noexcept in_ptr.release(); return std::unique_ptr<T>(plain_out_ptr); }
It would nice to generalize this to arbitrary smart pointers, because even for copyable smart pointers (e.g. shared_ptr, intrusive_ptr), there is interest in "cast moving" to avoid needlessly touching the reference count in such case (better performance). However, I am not sure we can implement this non-intrusively. -- François
Le 06/10/15 12:19, Karolin Varner a écrit :
Hi all!
There is the family of boost pointer cast (boost::static_pointer_cast, boost::dynamic_pointer_cast, ...) that allow a developer to write generic code by casting pointers regardless of whether they are some smart pointer (e.g. shared_ptr) or plain pointers.
However, those functions have not been implemented for unique_ptr, because they create a copy of their pointer and unique_pointers can not be copied.
In my case I still needed to cast some unique_ptr and since it's ok to move unique_ptrs I wrote a function that does exactly that. The code is below – Comments are very, very welcome. It works for my case, but I am sure it is not as generic and safe as it should be ;) . Specifically, for a library, it should be made to work on any kind of smart/plain pointer. Not just unique_ptr.
Is there any interest to include a boost::static_moving_pointer_cast and the other types of casts in boost? I believe this will be a useful addition.
/// Dynamic cast a unique_ptr. /// /// This function shall move the pointer in in_ptr, into /// a new unique_pointer of the desire type, dynamic_casting /// it in the process. /// In given pointer shall contain a NULL pointer afterwards. /// /// If unsuccessful, function shall throw an error and leave /// in_ptr unmodified. /// /// This behaviour contrasts a bit with the behaviour of the /// normal dynamic_cast: /// 1. dynamic_cast copies objects, this moves them since /// unique_pointers can not be copied. /// 2. dynamic_cast returns a NULL pointer if the pointer to /// cast is of an incomplete type, this throws /// incomplete_cast instead, to ease error handling. I wouldn't throw in this case to be coherent with dynamic_pointer_cast. If you need a better error handling for dynamic_moving_pointer_cast you need also for dynamic_pointer_cast.
Just wondering if we can not make dynamic_pointer_cast to work as your
dynamic_moving_pointer_cast, when the parameter is a rvalue reference
template
On 7/10/2015 19:16, Vicente J. Botet Escriba wrote:
Just wondering if we can not make dynamic_pointer_cast to work as your dynamic_moving_pointer_cast, when the parameter is a rvalue reference
template
shared_ptr<T> dynamic_pointer_cast(shared_ptr<U> && r) noexcept; C++ International Standard The use will be
dynamic_pointer_cast<U>(ret_up()); dynamic_pointer_cast<U>(move(up));
It would theoretically be possible if you have access to the internals of shared_ptr (otherwise it would be no better than copying). However I'm dubious about this usage -- dynamic casts naturally admit the possibility of failure, which results in discarding the input parameter in cases that it's a temporary (hence the changed semantics in the proposed dynamic_moving_pointer_cast). This basically translates into an operation that says "return the pointer without touching its refcount if the cast succeeds, otherwise decrement the refcount". In particular the latter case can result in almost immediately deleting a value newly-created by the called function, or otherwise unique prior to the cast. The equivalent operation on unique_ptr would simply be "delete input pointer if cast fails". Either of these seem like an odd thing for code to want to do in practice. Usually polymorphic code doesn't transfer ownership at the same time. I'm curious if there would be any good real-world examples for this.
Le 07/10/15 09:03, Gavin Lambert a écrit :
On 7/10/2015 19:16, Vicente J. Botet Escriba wrote:
Just wondering if we can not make dynamic_pointer_cast to work as your dynamic_moving_pointer_cast, when the parameter is a rvalue reference
template
shared_ptr<T> dynamic_pointer_cast(shared_ptr<U> && r) noexcept; C++ International Standard The use will be
dynamic_pointer_cast<U>(ret_up()); dynamic_pointer_cast<U>(move(up));
It would theoretically be possible if you have access to the internals of shared_ptr (otherwise it would be no better than copying). Well we have boost::unique_ptr to play with.
However I'm dubious about this usage -- dynamic casts naturally admit the possibility of failure, which results in discarding the input parameter in cases that it's a temporary (hence the changed semantics in the proposed dynamic_moving_pointer_cast). IIUC passing the unique_ptr by reference allows to don't move if the dynamic_cast fails. I don't see how this implies an exception.
This basically translates into an operation that says "return the pointer without touching its refcount if the cast succeeds, otherwise decrement the refcount". In particular the latter case can result in almost immediately deleting a value newly-created by the called function, or otherwise unique prior to the cast. Good point.
The equivalent operation on unique_ptr would simply be "delete input pointer if cast fails".
Either of these seem like an odd thing for code to want to do in practice. Usually polymorphic code doesn't transfer ownership at the same time. I'm curious if there would be any good real-world examples for this.
I agree with your analysis. Vicente
Vicente J. Botet Escriba wrote:
Just wondering if we can not make dynamic_pointer_cast to work as your dynamic_moving_pointer_cast, when the parameter is a rvalue reference
Yes, that was my first thought as well.
template
Hi On 10/07/2015 09:45 PM, Peter Dimov wrote:
Vicente J. Botet Escriba wrote:
Just wondering if we can not make dynamic_pointer_cast to work as your dynamic_moving_pointer_cast, when the parameter is a rvalue reference
template
unique_ptr<T> dynamic_pointer_cast( unique_ptr<U> && r ) noexcept { T * p = dynamic_cast ( r.get() ); if( p ) r.release(); return unique_ptr<T>( p ); }
Agreed. That seems like the best option. On 10/07/2015 09:03 AM, Gavin Lambert wrote:
On 7/10/2015 19:16, Vicente J. Botet Escriba wrote:
Just wondering if we can not make dynamic_pointer_cast to work as your dynamic_moving_pointer_cast, when the parameter is a rvalue reference
It would theoretically be possible if you have access to the internals of shared_ptr (otherwise it would be no better than copying).
We do have access to the internals of the boost smart pointers, so we could provide a set of specialized moving casts for std::unique_ptr, boost::shared_ptr and boost::scoped_ptr. For std::shared_ptr and the other pointers we might still emulate a moving cast in order to facilitate generic programming. Best, Karolin
On 8/10/2015 09:27, Karolin Varner wrote:
On 10/07/2015 09:45 PM, Peter Dimov wrote:
template
unique_ptr<T> dynamic_pointer_cast( unique_ptr<U> && r ) noexcept { T * p = dynamic_cast ( r.get() ); if( p ) r.release(); return unique_ptr<T>( p ); } [...] On 10/07/2015 09:03 AM, Gavin Lambert wrote: On 7/10/2015 19:16, Vicente J. Botet Escriba wrote: Just wondering if we can not make dynamic_pointer_cast to work as your dynamic_moving_pointer_cast, when the parameter is a rvalue reference
It would theoretically be possible if you have access to the internals of shared_ptr (otherwise it would be no better than copying).
We do have access to the internals of the boost smart pointers, so wecould provide a set of specialized moving casts for std::unique_ptr, boost::shared_ptr and boost::scoped_ptr.
unique_ptr is straightforward, as Peter Dimov has demonstrated above. scoped_ptr is similarly straightforward. As I mentioned on the other thread branch though, I'm a bit skeptical that either of these types would be useful for a dynamic moving cast -- dynamic cast yes, moving yes, but both at once is very peculiar semantics. It's shared_ptr that you need internal access for, if you want to be able to avoid the refcount changes completely. And this one is more likely to be useful for dynamic moving casts, though it's still hard to think of a motivating case.
For std::shared_ptr and the other pointers we might still emulate a moving cast in order to facilitate generic programming.
I'm not sure emulation is any better than existing dynamic_pointer_cast;
this was my attempt:
template
Karolin Varner wrote:
For std::shared_ptr and the other pointers we might still emulate a moving cast in order to facilitate generic programming.
Like Gavin, I'm not sure I quite see the point of using dynamic_pointer_cast on an rvalue in generic code, because if it fails, the original is lost.
Hi Gavin On 10/08/2015 03:01 PM, Peter Dimov wrote:
Karolin Varner wrote:
For std::shared_ptr and the other pointers we might still emulate a moving cast in order to facilitate generic programming.
Like Gavin, I'm not sure I quite see the point of using dynamic_pointer_cast on an rvalue in generic code, because if it fails, the original is lost.
Yeah, I was thinking of using an implementation, where the input pointer would only be moved, if the cast is successful. In case the cast wasn't successfully. we could return a NULL pointer and leave the input as it was. Best, Karolin
Hi Karolin, On 2015/10/06 19:19, Karolin Varner wrote:
There is the family of boost pointer cast (boost::static_pointer_cast, boost::dynamic_pointer_cast, ...) that allow a developer to write generic code by casting pointers regardless of whether they are some smart pointer (e.g. shared_ptr) or plain pointers.
However, those functions have not been implemented for unique_ptr, because they create a copy of their pointer and unique_pointers can not be copied. I disagree this option because user can write unsafe code easily, see http://melpon.org/wandbox/permlink/x6Fj1QSwdD9fBAfA . It based on your dynamic_cast version, but it is also dangerous for other casts (static, const, ...).
Best, Kohei Takahashi
Hi, I have created a pull request based on the discussion in this thread: https://github.com/boostorg/smart_ptr/pull/23 It doesn't yet contain optimizations for copyable pointers, but it contains pointer_cast overloads for std::shared_ptr and std::unique_ptr. Since this came up in this thread, I also created a motivating example for static_pointer_cast(std::unique_ptr<...>&&) and dynamic_pointer_cast(std::unique_ptr<...>&&) http://melpon.org/wandbox/permlink/R3QWA7MaxZ5o1ocG It also seems that, even there are no overloads with move semantics for shared_ptr, calling the pointer casts with std::move(...) works. So in principle we could use move(...) for those and implement optimizations later. Though I am not quite convinced, that this is good behavior. Best, Karolin
participants (6)
-
Francois Duranleau
-
Gavin Lambert
-
Karolin Varner
-
Kohei Takahashi
-
Peter Dimov
-
Vicente J. Botet Escriba