Boost logo

Boost :

Subject: Re: [boost] Adding polymorphic_value to boost
From: Richard Hodges (hodges.r_at_[hidden])
Date: 2017-11-20 22:11:56


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.


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