Boost logo

Boost :

Subject: Re: [boost] [smart_ptr] Is there any interest in unique_ptr with type erased deleter?
From: Andrey Davydov (andrey.a.davydov_at_[hidden])
Date: 2017-03-21 13:33:13


On Tue, Mar 21, 2017 at 4:11 PM, Andrey Semashev <andrey.semashev_at_[hidden]>
wrote:

> On 03/21/17 14:54, Andrey Davydov wrote:
>
>> On Tue, Mar 21, 2017 at 2:15 PM, Andrey Semashev via Boost
>> <boost_at_[hidden] <mailto:boost_at_[hidden]>> wrote:
>>
>> On 03/21/17 12:54, Andrey Davydov via Boost wrote:
>>
>> On Tue, Mar 21, 2017 at 12:00 PM, Richard Hodges
>> <hodges.r_at_[hidden] <mailto:hodges.r_at_[hidden]>> wrote:
>>
>> What is required, more than simply creating a type-erased
>> deleter?
>>
>>
>> Also deleter must capture pointer, because pointer which was
>> passed to
>> constructor can differ from the pointer which will be deleted.
>> For example,
>>
>> struct Base { /* ... */ };
>> struct Derived : Base { /* ... */ };
>>
>> ptr<Derived> p1(new Derived);
>> void * raw_p1 = p1.get();
>> ptr<Base> p2 = std::move(p1);
>> void * raw_p2 = p2.get();
>> assert(raw_p1 != raw_p2);
>>
>>
>> The above piece of code strikes me as very unsafe and better be
>> avoided in the first place. Designing new tools to handle this case
>> is IMHO misguided.
>>
>> Why this code is very unsafe? If change `ptr` to `shared_ptr` this will
>> become perfectly valid and widely used pattern.
>>
>
> Because comparing the void pointers is not safe in general. The above code
> in particular will probably work (although not sure if even that's
> guaranteed, depending on whether Base and Derived qualify as standard
> layout classes), but if the class hierarchy is different, that code will
> silently break.

I agree, I wrote this code just to demonstrate that deleter must capture
pointer because it can be changed due to upcasting.

> But, of course, nothing stops you from saving the state in the deleter.
>
> struct poly_deleter
> {
> template< typename T >
> poly_deleter(T* p) : m_p(p), m_delete(&poly_deleter::do_delete< T >)
> {
> }
>
> void operator() (void*) const noexcept
> {
> m_delete(m_p);
> }
>
> private:
> template< typename T >
> static void do_delete(void* p) noexcept
> {
> delete static_cast< T* >(p);
> }
>
> void* m_p;
> void (*m_delete)(void*);
> };
>
> This will work even if you form std::unique_ptr<void, poly_deleter> (i.e.
> don't have a universal base class).
>

Your poly_deleter implementation is the same as I used inside my smart
pointer implementation, but there is an issue with std::unique_ptr<T,
poly_deleter>, namely it is not constructible from std::unique_ptr<T,
std::default_delete<T>> because poly_deleter must capture pointer and so it
is not constructible from std::default_delete.

-- 
Andrey Davydov

Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk