C++ Logo

std-discussion

Advanced search

Re: How to destroy an array created with a user-defined placement new

From: Brian Bi <bbi5291_at_[hidden]>
Date: Thu, 25 Jul 2019 10:56:28 -0500
There is no way to get the runtime to tell you the number of elements in
the array. All you can do is (as you know) use `delete[]` to call the
appropriate number of destructors.

You can of course solve this problem "the hard way": instead of using
`new[]`, you write a function that calls `operator new[]` with the
appropriate size (including space for an extra size_t at the beginning,
where the number of elements is stored), writes the size, uses placement
new to initialize each element of the array, then returns a pointer to the
first element. This is of course what `new[]` is doing anyway, and the
standard library containers all do similar things (none of them use
`new[]`), since they need to be able to precisely control when object
lifetimes begin and end. Now the deleter knows exactly how to look up the
size.

On Thu, Jul 25, 2019 at 9:05 AM Aleksey Demakov via Std-Discussion <
std-discussion_at_[hidden]> wrote:

> Hi all,
>
> With a custom allocator I can use user-defined placement new for
> objects and arrays alike.
>
> void *operator new (std::size_t size, Arena& arena) { return
> arena.alloc(size); }
> void *operator new[] (std::size_t size, Arena& arena) { return
> arena.alloc(size); }
>
> MyObj { ~MyObj() {} }
>
> std::unique_ptr<MyObj, MyDeleter> ptr(new (arena) MyObj, MyDeleter(arena));
> std::unique_ptr<MyObj[], MyArrayDeleter> arr(new (arena) MyObj[n],
> MyArrayDeleter(arena));
>
> Now I can easily provide a deleter for a single object.
>
> struct MyDeleter {
> Arena& arena;
> MyDeleter(Arena& a) : arena(a) {}
> void operator()(MyObj *ptr) {
> ptr->~MyObj();
> Arena.free(ptr);
> }
> };
>
> But I have a problem with providing a deleter for arrays.
>
> As far as I understand the C++ runtime calls operator new[] with the
> size argument that accommodates the array itself and its size. The
> runtime stores the size and constructs the required number of items.
> If the delete[] operator is applied to the array then the size is used
> by the runtime to destruct the items one by one.
>
> But this information seems to be totally inaccessible to user if there
> is a need to destruct the array manually.
>
> struct MyArrayDeleter {
> Arena& arena;
> MyArrayDeleter(Arena& a) : arena(a) {}
> void operator()(MyObj *arr) {
> // OOPS, lost here: for (std::size_t i = 0; i < UNKNOWN_SIZE;
> i++) arr[i].~MyObj();
> arena.free(arr);
> }
> };
>
> Obviously it's possible to explicitly store the array size one more
> time in MyArrayDeleter.
>
> struct MyArrayDeleter {
> Arena& arena;
> std::size_t size;
> MyArrayDeleter(Arena& a, std::size_t n) : arena(a), size(n) {}
> void operator()(MyObj *arr) {
> for (std::size_t i = 0; i < size; i++) arr[i].~MyObj();
> arena.free(arr);
> }
> };
>
> This is somewhat bug-prone however. There are two (runtime-managed and
> user-managed) copies of the array size. One needs to be careful to
> supply the same number to the new operator and to the deleter. Not to
> mention it takes a few extra bytes of storage.
>
> Is there a way to solve this issue in standard C++ without such
> deficiencies?
>
> Regards,
> Aleksey
> --
> Std-Discussion mailing list
> Std-Discussion_at_[hidden]
> http://lists.isocpp.org/mailman/listinfo.cgi/std-discussion
>


-- 
*Brian Bi*

Received on 2019-07-25 10:58:38