C++ Logo

std-discussion

Advanced search

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

From: Aleksey Demakov <ademakov_at_[hidden]>
Date: Thu, 25 Jul 2019 17:05:14 +0300
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

Received on 2019-07-25 09:07:23