jeudi 20 octobre 2016

unique_ptr's of zero size

I often work with multi-dimensional arrays, and I am not a big fan of std::vector, since it is not possible to instantiate a std::vector or a std::vector of std::vector's using a reference without copying the underlying data.

For one-dimensional arrays, I use the following

template<typename T>
using deleted_aligned_array = std::unique_ptr<T[], std::function<void(T*)> >;

template<typename T>
deleted_aligned_array<T> deleted_aligned_array_create(size_t n) {
  return deleted_aligned_array<T>((T*)_mm_malloc(n*sizeof(T),16), [](T* f)->void { _mm_free(f);});
}

This is very convenient and allows me to instantiate a dynamically sized array, which also works for a size of zero. Further, I can use std::forward to pass on the data without copying.

For a two-dimensional array, I would like to do something like

template<typename T>
using deleted_aligned_array2 = std::unique_ptr<T*,std::function<void(T**)>>;

template<typename T>
deleted_aligned_array2<T> deleted_aligned_array_create(size_t m, size_t n) {
  auto arr = deleted_aligned_array2(new T*[m](), [&](T** x) {
                                              if (malloc_usable_size(x) > 0) {
                                                _mm_free(&(x[0][0]));
                                              }
                                              delete[] x;});
  if (m*n > 0) {
    arr.get()[0] = (T*) _mm_malloc(m*n*sizeof(T),16);

    // Row pointers
    for (size_t iRow = 1; iRow < m; iRow++) {
      (m_data.get())[iRow] = &(m_data.get()[0][iRow*n]);
    }
  }
  return arr;
}

It works for zero-size arrays, but I get an error from valgrind for obvious reasons, invalid read of size 8.

Is it possible to solve this in an elegant way, without creating an entire class keeping a std::unique_ptr member, where I implement move-constructor, move-assignment etc. Ultimately, I would like to generalize this to be used for any dimension

template<typename T, size_t Dim>
deleted_aligned_array<T,D> deleted_aligned_array_create(...);

The returned array should be a unique pointer with row pointer recursively initialized and it should support zero-size arrays, e.g.

auto arr = deleted_aligned_array_create<float,3>(4,5,10);

should return a 3-dimensional array with row and column pointers.

Issues: 1) Avoid reading invalid data in a simple way. 2) Use a template parameter D for generating the types: T*, T** and simply passing on D to code recursively generating row pointers (this I already have). 3) Preferably in a portable way. malloc_usable_size is a GNU extension and calling it on x results in an invalid read, when the size is 0.

Thanks in advance

Aucun commentaire:

Enregistrer un commentaire