Subject: Re: [boost] interest in a stateful pointer library?
From: Hans Dembinski (hans.dembinski_at_[hidden])
Date: 2017-10-09 15:50:07
> On 9. Oct 2017, at 16:42, Peter Dimov via Boost <boost_at_[hidden]> wrote:
> Hans Dembinski wrote:
>> > Out of curiosity, what's the specific problem that was pointed out? What > behavior is undefined?
>> I was pointed to this answer on SO:
>> I am relying on two things:
>> - memory addresses map trivially to consecutive integers
>> - the address of aligned memory allocated by Boost.Align always converts into an integer that is a multiple of the alignment value
>> Neither seems to be guaranteed by the standard. The conversion from memory address to integer is an implementation detail, the SO answer gives examples of "exotic" mappings which are conceivable (not that there is any machine out there which actually does it this way).
> As far as I can see, you're storing an uintptr_t. This is not undefined. The only thing you're missing is an ASSERT that the tag_mask part is 0 after the reinterpret_cast. Yes, this is not guaranteed to work in principle, but if it does, it's not undefined.
Ok, yes, but tripping an assert (or raising a compile-time error) on an unsupported platform is not a nice solution, I think. Wouldn't it be better to provide a fallback that at least works somehow without crashing build or runtime? I talked a bit about that in my reply to Arvid.
> Your converting constructor seems broken though. Even if we put aside the problem that destroying U* is not the same as destroying T*, you have to convert to T* first, then to U*, then back to uintptr_t. (And assert here as well, as the new value is no longer guaranteed to have a tag_mask of 0.)
Maybe you are right, I am not sure. I copied the declaration from std::unique_ptr. This should only compile if U* and T* are convertible, e.g. if T is the base of U. AFAIU this is supposed to mimic what one can do with raw pointers:
auto d = new Derived(); // inherits from Base with virtual dtor
auto b = static_cast<Base*>(d); // still the same physical address, so should map to the same integer, no?
delete b; // correctly deletes Derived using vtable
> Stepping back a bit, I would have expected a tagged_ptr to be a low-level component that just adds tag bits to a pointer, rather than being responsible for allocating and destroying objects and arrays. Perhaps the way you've implemented it makes it more useful for certain scenarios though, I'm not sure.
I made tagged_ptr mimic std::unique_ptr, because I rely on having full control over (de)allocation to harvest the low bits of the pointer. I wanted to have an interface which makes sure that I cannot pass a pointer that has been wrongly allocated. I thought the easiest way to do that is to make a smart pointer which can only be created via a "make_tagged" factory. Since std::unique_ptr supports objects and arrays, I also tried to support both.