Boost logo

Boost :

Subject: Re: [boost] Adding polymorphic_value to boost
From: Jonathan Coe (jonathanbcoe_at_[hidden])
Date: 2017-11-21 08:45:12


On 20 November 2017 at 22:51, Richard Hodges <hodges.r_at_[hidden]> wrote:

> > It will take me some time to digest and come back with questions.
>
> This is a subject close to my heart, so please feel free to question as
> much as you wish.
>
> > Pleasingly we seem to be debating how useful this type can be rather
> than whether it’s useful at all.
>
> As far as I am concerned, cloned_ptr was the missing piece to the puzzle
> of consistent handling of handle/body objects - particularly when
> polymorphic. It allowed value-semantic coding to be a reality in a
> consistent way. Have no fears over utility.
>
> While pointers provide the power behind c++, I am strongly of the view
> that building user code around value types is preferable from the points of
> view of maintainability, elegance and provable correctness. With a little
> tweak this class could spell the end for pointer-based paranoia in user
> code forever. That's a wonderful thing.
>
> What strikes me as the masterstroke in the proposed implementation is the
> type-erased copy/deletion which eliminates the need for clone. I wish I'd
> thought of it. In that respect your implementation is beautiful.
>
>
>
>
>
>
> On 20 November 2017 at 23:37, Jonathan <jonathanbcoe_at_[hidden]> wrote:
>
>>
>> > 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
>
>
>
Do you have sample code you might be able to share to illustrate this
point: "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'm afraid
this example is sufficiently removed from my day-to-day experience that I
struggle to see what you mean without code. An utterly unambiguous example
that's not driven by a theoretical desire for type consistency will
motivate adding `cloned_ptr`.

`polymorphic_value` should probably stay as it is, I would add `cloned_ptr`
too as they serve rather different purposes.

regards,

Jon

PS There may be considerable to-and-fro in putting an example together,
would it be wise to take this discussion off list?


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