On 20 Nov 2017, at 22:11, Richard Hodges via Boost
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 class Pointer> struct basic_foo { // ..
Pointer
impl_; }; now I can specialise:
struct unique_foo : basic_foostd:unique_ptr { }; struct shared_foo : basic_foostd:shared_ptr { }; struct value_foo : basic_fooboost::polymorphic_value { }; struct foo_observer : basic_foostd::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
wrote: On 20 Nov 2017, at 21:12, Richard Hodges via Boost
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@lists.boost.org> 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