Boost logo

Boost :

Subject: Re: [boost] Is there any interest in non-owning pointer-like types?
From: Vicente J. Botet Escriba (vicente.botet_at_[hidden])
Date: 2017-02-03 01:37:18


Le 02/02/2017 à 02:15, Joseph Thomson a écrit :
> On Thu, Feb 2, 2017 at 6:18 AM, Niall Douglas <s_sourceforge_at_[hidden]>
> wrote:
>
>> On 01/02/2017 07:47, Joseph Thomson wrote:
>>> For some time, I have been developing a pair of non-owning pointer-like
>>> types that I am currently calling `observer_ptr` and `observer`. I had
>>> planned to propose their addition to the C++ standard library, but I have
>>> been informed by the author of the original `observer_ptr` proposal
>>> <http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4282.pdf> that
>>> the ISO C++ committee has rejected his proposal and made clear that it
>>> feels there is no place in the standard library for such types, believing
>>> that this role is filled to a satisfactory degree by regular pointers. I
>>> wholeheartedly disagree with this assessment, so I am bringing my
>> proposal
>>> here instead.
>> I don't think that exactly accurate. Rather it is that the GSL's
>> proposed schema of:
>>
>> * owner<T> is an owning pointer
>> * span<T> is a borrowed pointer/reference/view
>> * C type pointers are non-owning, non-borrowed
>>
> As far as I understand it, `span<T>` is a view of objects over a range, in
> the vein of `array_view<T>`, and raw pointers are simply non-owning. I'm
> not sure what "borrowed" means in this context.
>
> ... has general wide agreement in principle, if not in exact
>> formulation. The Library Fundamentals TS v2 does have an observer_ptr in
>> std::experimental, but there is general lack of sureness as to what it
>> actually contributes when a non-annotated pointer according to the GSL
>> schema is either an observer by definition, or unupgraded code.
>>
> That is just one of the benefits I listed (documentation of intent). The
> most benefit important IMO is *type-safety*, followed closely by a more
> minimal interface. The `observer_ptr<T>` in `std::experimental` only has
> type safety in one direction (conversion to `T*` is explicit), because the
> only way to construct it is from `T*`. Observers should preferably be
> constructed from `T&`, as this is a type-safe conversion. The
> `observer_ptr<T>` in `std::experimental` needs one change to make it type
> safe: change `make_observer(T*)` to `make_observer(T&)`.
We need a proposal to change experimental::observer_ptr :)
>
> In my opinion, designating `T*` to mean "observer" would be okay in an
> ideal world (it still has far too general an interface for something that
> just observes), but the world isn't ideal. Not everyone reads the GSL
> guidelines, and those who do won't follow it to the letter, and even if
> they did there is still plenty of "unupgraded" code. Also, I think you too
> easily dismiss the benefit of being able to distinguish between un-upgraded
> and upgraded code; this sounds very useful if you ask me. So, to reiterate
> again, `observer_ptr`:
>
> - Allows upgraded code to be distinguished from un-upgraded code (this
> is more important than you might think)
> - Removes operations that are inappropriate for an observer type
> (pointer arithmetic, array subscript, conversion to `void*`)
> - Provides type safety by allowing construction from `T&` and
> disallowing construction from and conversion to `T*`
I agree we need this vocabulary type. GSL guidelines are for code that
will be written from scratch with C++14/C++17.
The reality is that we have tons of C++98 code. Pretending that T* is a
not owning pointer because we have gsl::owner is not useful in a legacy
code project. I'm not saying that gsl::owner is not useful.
> Whether to allow implicit/explicit conversion from `T&` is a design detail.
> The benefits are there regardless.
Yes, I believe the implicit conversion could be questionable.
>
> Also, it seems to me that the natural meaning of `T*` in C++ is as an
> iterator, not an observer:
>
> int arr[] = { 1, 2, 3 };
> auto it = std::end(arr); // `decltype(it)` is `int*`
>
> I'd need a fair bit of convincing that observer<T> has merit in any form
>> except additional clarity to help demarcate unupgraded code from
>> upgraded code. If that's your argument, and you're not doing funny
>> things with implicit conversion from T& and other funny non-GSL
>> semantics, I'd suppose this though I'd suggest you actually contribute
>> it to GSL itself and persuade Neil to let it in.
>>
> `observer<T>` is similar to `not_null<T>`, in that it enforces a "not null"
> precondition, except `observer<T>` does a much better job because it
> enforces the precondition at compile-time.
>
> auto o = make_observer(null_ptr); // compile error: expects `T&`
> auto n = not_null<T*>(null_ptr); // compiles fine; run-time error
I prefer also a not null smart pointer that takes this as a
precondition. Requiring the class to check the parameter is not nullptr
(as gsl::not_null does) is more than what I need.
> `observer` forces the user to dereference their pointer, which should be
> flagged as unsafe by the static analyser if they don't check for null.
> `not_null` waits until compile-time to report the error, and makes it
> harder for a static analyser to catch the potential null pointer bug.
>
> I have had a few discussions with Neil over at the GSL GitHub page about
> the purpose of `not_null`. Honestly, the design goals of `not_null<T>` seem
> unclear to me; it seems to be trying to do too many things at once,
> simultaneously trying to be a drop-in replacement for `T*` that enforces a
> "not null" precondition at run-time (it's meant to be able to hold any kind
> of pointer or pointer-like object), but also trying to be this high-level
> modern type that has compile-time type safety (it deletes various pointer
> arithmetic operations, and it has been seriously suggested that conversion
> from `T` should be explicit). It was initially meant to be compatible with
> smart pointers like `unique_ptr`, but this goal seems to have been all but
> abandoned after it was realised that this won't really work. It's also
> meant to be used in conjunction with strings, despite the fact that
> `string_view` is a much better alternative, and with the `owner`
> annotation, despite the fact that this will probably have the same
> conceptual problems as `unique_ptr`. If my ramblings sound confused, it's
> because I am confused, because `not_null` is confused.
Maybe we could have not_null<Ptr> that works for any pointer-like type,
but what I need today is not_null_ptr<T>.
Maybe it is worth providing a single not_null<T*> instantiation.
>
> The GSL is a *much* better home for it than Boost because then you'll
>> have Bjarne batting for it, plus static checking support from Microsoft
>> in VS2017 and Google via clang-tidy. You'll also get a *huge* userbase
>> almost instantly, because the GSL or rather one of its C++ 98 clones is
>> seeing exponential growth recently. It's amazingly useful for upgrading
>> ancient C++ codebases.
>>
> This would be good. If I brought this to the GSL, I would pretty much be
> proposing that `not_null` is scrapped in favour of `observer` and
> `observer_ptr`. `not_null<zstring>` is obsoleted by `string_view`;
> `not_null<unique_ptr<T>>` etc. are broken; `not_null<owner<T>>` is of
> little value IMO, and may be conceptually broken too; honestly, the only
> useful case is `not_null<T*>`, and that currently has big issues with its
> design too. `observer` and `observer_ptr` are essentially `maybe_null` and
> `not_null` with clearer design goals and a fixed interface.
>
>
>
In reverse order ;-)

Vicente


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