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@lists.isocpp.org> 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@lists.isocpp.org
http://lists.isocpp.org/mailman/listinfo.cgi/std-discussion


--
Brian Bi