Boost logo

Boost :

Subject: Re: [boost] Adding polymorphic_value to boost
From: Jonathan (jonathanbcoe_at_[hidden])
Date: 2017-11-20 22:37:13


> On 20 Nov 2017, at 22:11, Richard Hodges via Boost <boost_at_[hidden]> wrote:
>
> sent direct to Jonathan in error - reproduced here:
>
>> polymorphic_value solves a very real problem and allows deletion of a
> large amount of error-prone boiler-plate code.
>
> completely agree. It would allow deletion of even more code if it didn't
> propagate_const.
>
>> Without the second category, the first is just an implementation detail.
>
> Fine. Let's make it a policy. Then everyone's happy. The truth will out.
>
>> I’d really like to see real examples
>
> Sometimes (in a comms-based system, often) even though the handle object's
> interface is const, and accessor will do some internal work which may need
> to mutate the implementation (even on another thread). I spend a lot of
> time writing comms/protocol code which I normally implement as asio
> services. Database access, websockets, AMQP messaging, http, etc.
>
> If you've spent any time with asio you'll know all about the handle-body
> idiom and all the nasty boilerplate required to correctly implement
> move/copy/sharing of io objects bound to an executor.
>
> Sometimes I'll want the concept of an object and I may decide to give it
> shared-body semantics. But then I realise I can get a
> performance/correctness win by providing the same interface but with
> different copy/move behaviour.
>
> Sometimes I realise I need such an object which will just clone itself when
> copied.
>
> It is useful to write these objects in terms of:
>
> template<template<class...> class Pointer>
> struct basic_foo
> {
> // ..
>
> Pointer<foo_impl> impl_;
> };
>
> now I can specialise:
>
> struct unique_foo : basic_foo<std:unique_ptr> { };
> struct shared_foo : basic_foo<std:shared_ptr> { };
> struct value_foo : basic_foo<boost::polymorphic_value> { };
> struct foo_observer : basic_foo<std::reference_wrapper> { };
>
> I have implemented both the interface and the implementation only once and
> am now afforded complete flexibility in how I manage my foos.
>
> With a tiny bit more templatery I can even automatically get:
>
> auto a = unique_foo(...);
> auto b = shared_foo(std::move(a));
>
> or
>
> auto a = value_foo(...);
> auto b = a;
> auto c = shared_foo(std::move(a));
>
> and I can always get:
>
> auto o1 = foo_observer(b), o2 = foo_observer(c);
>
>
> The consistency is important (at least to me) because it affords perfect
> composability.
>
>> On 20 November 2017 at 22:36, Jonathan Coe <jonathanbcoe_at_[hidden]> wrote:
>>
>> On 20 Nov 2017, at 21:12, Richard Hodges via Boost <boost_at_[hidden]>
>> wrote:
>>
>>>> None of these make copies. polymorphic_value is
>>>> not called XXX_ptr precisely because its semantics
>>>> are different from a pointer.
>>>
>>> shared_ptr, unique_ptr and reference_wrapper are all "different" from a
>>> pointer, but they all share 4 common traits:
>>>
>>> 1. The all manage/observe lifetime of another _single_ object in some
>>> defined but distinct way.
>>>
>>> 2. They all allow access to that object through operator*
>>>
>>> 3. They don't propagate const.
>>>
>>> 4. they don't have any pointery arithmetic behaviour
>>>
>>> Looking at the design history of polymorphic_value it seems that it
>>> originally came from a desire to complete the circle by providing another
>>> XXX_ptr which supported copying.
>>>
>>> This aim is _eminently useful_ as is evidenced by the numerous
>>> implementations of things like it on github (and in my own code).
>>>
>>> I also have an interest is such an object as standard because I always
>> seem
>>> to end up needing one.
>>>
>>> I do not have a use case for a const-propagating one. Never have. I can
>>> specify const in the angle-brackets. I've done that probably once.
>>>
>>>> This situation doesn't make sense for polymorphic_value.
>>>
>>> To you perhaps. However I have 3 projects on the go right now which could
>>> use polymorphic_value immediately as a retro-fit for home-made solutions
>>> *if it did not propagate const*.
>>>
>>> The concept of propagating const I can deal with trivially. To
>> incorporate
>>> it into this class mixes concerns to the detriment of its usefulness (to
>>> me).
>>>
>>> So If it came to a vote and my voice had any weight, I would say:
>>>
>>> * with implicit const-propagation - NO
>>>
>>> * remove the const propagation - absolute YES
>>>
>>> additionally, ideally rename it back to cloned_ptr, (or indirect if you
>>> must). Because what it is absolutely not is a value.
>>>
>>> It is logically the same as the other 4 non-pointers listed above, it's
>>> just that it have different owned-object lifetime behaviour.
>>>
>>>
>>> I completely understand the value-centric argument. I am a strong
>> proponent
>>> of it.
>>>
>>> My argument is that this is (or ought to be) a tool for *building* value
>>> types. It is not in of itself a value type, and neither can it ever be.
>> It
>>> does not exhibit any "value" behaviour (equality and the like).
>>>
>>> R
>>>
>>> On 20 November 2017 at 21:35, Steven Watanabe via Boost <
>>> boost_at_[hidden]> wrote:
>>>
>>>> AMDG
>>>>
>>>>> On 11/20/2017 01:11 PM, Richard Hodges via Boost wrote:
>>>>> I watched Sean Parent's talk covering what has now become
>>>> polymorphic_value.
>>>>>
>>>>> One thing that I am concerned about is Sean's insistence on propagating
>>>>> const implicitly.
>>>>>
>>>>> This would be at odds with the behaviour shared_ptr, unique_ptr, and
>>>>> reference_wrapper.
>>>>>
>>>>
>>>> None of these make copies. polymorphic_value is
>>>> not called XXX_ptr precisely because its semantics
>>>> are different from a pointer.
>>>>
>>>>> Sometimes I want a const pointer to a mutable thing, and sometimes I
>>>> want a
>>>>> mutable pointer to a const thing (and sometimes I want to be able to
>>>>> observe a thing without affecting its lifetime).
>>>>>
>>>>
>>>> This situation doesn't make sense for polymorphic_value.
>>>>
>>>>> So my 2-pence would be that propagate_const should not be implicit in
>>>> this
>>>>> class.
>>>>>
>>>>> My rationale is that an obvious use case for me would be manufacturing
>>>>> specialisations on pointer_type allowing a consistent interface leading
>>>> to
>>>>> an inner implementation while simply changing ownership behaviour.
>>>>>
>>>>> In that sense I am strongly of the view that the return type of
>>>>> polymorphic_value<Foo>::operator*() const should be Foo& and not const
>>>> Foo&.
>>>>>
>>>>> If I want a polymorphic cloning pointer to const Foo I can declare it
>>>> with
>>>>> polymorphic_value<const Foo>.
>>>>>
>>>>> I have never had reason to quibble with Sean's thinking before now, but
>>>> on
>>>>> this he is dead wrong.
>>>>>
>>>>> If I want const propagation, it's simple enough to wrap any pointer in
>>>>> propagate_const<>
>>>>>
>>>>> This feels to me like idealists overriding pragmatists.
>>>>>
>>>>> Or have I missed something obvious?
>>>>>
>>>>
>>>> In Christ,
>>>> Steven Watanabe
>>>>
>>>>
>>>> _______________________________________________
>>>> Unsubscribe & other changes: http://lists.boost.org/
>>>> mailman/listinfo.cgi/boost
>>>>
>>>
>>> _______________________________________________
>>> Unsubscribe & other changes: http://lists.boost.org/
>> mailman/listinfo.cgi/boost
>>
>> It’s interesting how quickly people become polarised on this. One thing to
>> note is that polymorphic_value<const T> is not the same as a
>> const-propagating polymorphic_value<T>; I hope that puts us all on the same
>> page.
>>
>> polymorphic_value is designed so that compiler-generated special member
>> functions for objects with polymorphic components (sub-objects) are
>> generated correctly. As such it needs to propagate const. There’s no sense
>> in part of an object being mutable in a context where the object itself is
>> immutable.
>>
>> polymorphic_value solves a very real problem and allows deletion of a
>> large amount of error-prone boiler-plate code.
>>
>> Some people don’t want const-propagation because they favour a composable
>> deep-copy + deep-const combination type. Some people have a use case for a
>> deep-copying non const-propagating pointer-like hybrid type. Without the
>> second category, the first is just an implementation detail. I’d really
>> like to see real examples (not thought-experiments) that are solved by a
>> type like cloned_ptr. I have never encountered one but my experience is not
>> so broad and I’m well aware that absence of evidence is not evidence of
>> absence.
>
> _______________________________________________
> Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

I have no experience with ASIO, thanks for taking the time to put some examples together. It will take me some time to digest and come back with questions. Pleasingly we seem to be debating how useful this type can be rather than whether it’s useful at all.

Regards,

Jon


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