Mercurial > octave
diff liboctave/array/Array.h @ 30486:298995435071 stable
allow custom allocators for the Array class
* Array.h, Array.cc (class Array): New template parameter, Alloc, with
default value of std::allocator<T>. Update function declarations and
definitions as needed.
(class Array<T,Alloc>::ArrayRep): Derive from Alloc. Use
std::allocator_traits to manage memory allocation.
(Array<T,Alloc>::ArrayRep::allocate,
Array<T,Alloc>::ArrayRep::deallocate): New functions to localize
allocation and construction of ArrayRep data. Use them to replace
calls to new and delete.
* Array-fwd.h: Update declaration.
author | John W. Eaton <jwe@octave.org> |
---|---|
date | Sat, 04 Dec 2021 08:49:18 -0500 |
parents | e38202d3628d |
children | abb4823df535 |
line wrap: on
line diff
--- a/liboctave/array/Array.h Thu Dec 09 21:21:11 2021 -0800 +++ b/liboctave/array/Array.h Sat Dec 04 08:49:18 2021 -0500 @@ -123,65 +123,89 @@ //! - string_vector: Array<std::string> with 1 column //! - Cell: Array<octave_value>, equivalent to an Octave cell. -template <typename T> +template <typename T, typename Alloc> class Array { protected: //! The real representation of all arrays. - class ArrayRep + class ArrayRep : public Alloc { public: - T *m_data; + typedef std::allocator_traits<Alloc> Alloc_traits; + + typedef typename Alloc_traits::rebind_traits<T> T_Alloc_traits; + typedef typename T_Alloc_traits::pointer pointer; + + pointer m_data; octave_idx_type m_len; octave::refcount<octave_idx_type> m_count; - ArrayRep (T *d, octave_idx_type l) - : m_data (new T [l]), m_len (l), m_count (1) + ArrayRep (pointer d, octave_idx_type len) + : Alloc (), m_data (allocate (len)), m_len (len), m_count (1) { - std::copy_n (d, l, m_data); + std::copy_n (d, len, m_data); } template <typename U> - ArrayRep (U *d, octave_idx_type l) - : m_data (new T [l]), m_len (l), m_count (1) + ArrayRep (U *d, octave_idx_type len) + : Alloc (), m_data (allocate (len)), m_len (len), m_count (1) { - std::copy_n (d, l, m_data); + std::copy_n (d, len, m_data); } // Use new instead of setting data to 0 so that fortran_vec and // data always return valid addresses, even for zero-size arrays. - ArrayRep (void) : m_data (new T [0]), m_len (0), m_count (1) { } - - explicit ArrayRep (octave_idx_type n) - : m_data (new T [n]), m_len (n), m_count (1) { } + ArrayRep (void) + : Alloc (), m_data (allocate (0)), m_len (0), m_count (1) { } - explicit ArrayRep (octave_idx_type n, const T& val) - : m_data (new T [n]), m_len (n), m_count (1) + explicit ArrayRep (octave_idx_type len) + : Alloc (), m_data (allocate (len)), m_len (len), m_count (1) { } + + explicit ArrayRep (octave_idx_type len, const T& val) + : Alloc (), m_data (allocate (len)), m_len (len), m_count (1) { - std::fill_n (m_data, n, val); + std::fill_n (m_data, len, val); } - explicit ArrayRep (T *ptr, const dim_vector& dv) - : m_data (ptr), m_len (dv.safe_numel ()), m_count (1) + explicit ArrayRep (pointer ptr, const dim_vector& dv, + const Alloc& xallocator = Alloc ()) + : Alloc (xallocator), m_data (ptr), m_len (dv.safe_numel ()), m_count (1) { } + // FIXME: Should the allocator be copied or created with the default? ArrayRep (const ArrayRep& a) - : m_data (new T [a.m_len]), m_len (a.m_len), m_count (1) + : Alloc (), m_data (allocate (a.m_len)), m_len (a.m_len), + m_count (1) { std::copy_n (a.m_data, a.m_len, m_data); } - ~ArrayRep (void) { delete [] m_data; } + ~ArrayRep (void) { deallocate (m_data, m_len); } octave_idx_type numel (void) const { return m_len; } // No assignment! ArrayRep& operator = (const ArrayRep&) = delete; + + pointer allocate (size_t len) + { + pointer data = Alloc_traits::allocate (*this, len); + for (size_t i = 0; i < len; i++) + T_Alloc_traits::construct (*this, data+i); + return data; + } + + void deallocate (pointer data, size_t len) + { + for (size_t i = 0; i < len; i++) + T_Alloc_traits::destroy (*this, data+i); + Alloc_traits::deallocate (*this, data, len); + } }; //-------------------------------------------------------------------- @@ -219,7 +243,7 @@ dim_vector m_dimensions; - typename Array<T>::ArrayRep *m_rep; + typename Array<T, Alloc>::ArrayRep *m_rep; // Rationale: // m_slice_data is a pointer to m_rep->m_data, denoting together with m_slice_len the @@ -232,7 +256,7 @@ octave_idx_type m_slice_len; //! slice constructor - Array (const Array<T>& a, const dim_vector& dv, + Array (const Array<T, Alloc>& a, const dim_vector& dv, octave_idx_type l, octave_idx_type u) : m_dimensions (dv), m_rep(a.m_rep), m_slice_data (a.m_slice_data+l), m_slice_len (u-l) { @@ -242,7 +266,7 @@ private: - static OCTARRAY_API typename Array<T>::ArrayRep *nil_rep (void); + static OCTARRAY_API typename Array<T, Alloc>::ArrayRep *nil_rep (void); public: @@ -257,7 +281,7 @@ //! nD uninitialized ctor. explicit Array (const dim_vector& dv) : m_dimensions (dv), - m_rep (new typename Array<T>::ArrayRep (dv.safe_numel ())), + m_rep (new typename Array<T, Alloc>::ArrayRep (dv.safe_numel ())), m_slice_data (m_rep->m_data), m_slice_len (m_rep->m_len) { m_dimensions.chop_trailing_singletons (); @@ -266,7 +290,7 @@ //! nD initialized ctor. explicit Array (const dim_vector& dv, const T& val) : m_dimensions (dv), - m_rep (new typename Array<T>::ArrayRep (dv.safe_numel ())), + m_rep (new typename Array<T, Alloc>::ArrayRep (dv.safe_numel ())), m_slice_data (m_rep->m_data), m_slice_len (m_rep->m_len) { fill (val); @@ -279,38 +303,39 @@ // object is deleted. The dimension vector DV must be consistent with // the size of the allocated PTR array. - explicit Array (T *ptr, const dim_vector& dv) + explicit Array (T *ptr, const dim_vector& dv, + const Alloc& xallocator = Alloc ()) : m_dimensions (dv), - m_rep (new typename Array<T>::ArrayRep (ptr, dv)), + m_rep (new typename Array<T, Alloc>::ArrayRep (ptr, dv, xallocator)), m_slice_data (m_rep->m_data), m_slice_len (m_rep->m_len) { m_dimensions.chop_trailing_singletons (); } //! Reshape constructor. - OCTARRAY_API Array (const Array<T>& a, const dim_vector& dv); + OCTARRAY_API Array (const Array<T, Alloc>& a, const dim_vector& dv); //! Constructor from standard library sequence containers. template<template <typename...> class Container> Array (const Container<T>& a, const dim_vector& dv); //! Type conversion case. - template <typename U> - Array (const Array<U>& a) + template <typename U, typename A = Alloc> + Array (const Array<U, A>& a) : m_dimensions (a.dims ()), - m_rep (new typename Array<T>::ArrayRep (a.data (), a.numel ())), + m_rep (new typename Array<T, Alloc>::ArrayRep (a.data (), a.numel ())), m_slice_data (m_rep->m_data), m_slice_len (m_rep->m_len) { } //! No type conversion case. - Array (const Array<T>& a) + Array (const Array<T, Alloc>& a) : m_dimensions (a.m_dimensions), m_rep (a.m_rep), m_slice_data (a.m_slice_data), m_slice_len (a.m_slice_len) { m_rep->m_count++; } - Array (Array<T>&& a) + Array (Array<T, Alloc>&& a) : m_dimensions (std::move (a.m_dimensions)), m_rep (a.m_rep), m_slice_data (a.m_slice_data), m_slice_len (a.m_slice_len) { @@ -331,7 +356,7 @@ delete m_rep; } - Array<T>& operator = (const Array<T>& a) + Array<T, Alloc>& operator = (const Array<T, Alloc>& a) { if (this != &a) { @@ -349,7 +374,7 @@ return *this; } - Array<T>& operator = (Array<T>&& a) + Array<T, Alloc>& operator = (Array<T, Alloc>&& a) { if (this != &a) { @@ -387,9 +412,9 @@ //@} //! Return the array as a column vector. - Array<T> as_column (void) const + Array<T, Alloc> as_column (void) const { - Array<T> retval (*this); + Array<T, Alloc> retval (*this); if (m_dimensions.ndims () != 2 || m_dimensions(1) != 1) retval.m_dimensions = dim_vector (numel (), 1); @@ -397,9 +422,9 @@ } //! Return the array as a row vector. - Array<T> as_row (void) const + Array<T, Alloc> as_row (void) const { - Array<T> retval (*this); + Array<T, Alloc> retval (*this); if (m_dimensions.ndims () != 2 || m_dimensions(0) != 1) retval.m_dimensions = dim_vector (1, numel ()); @@ -407,9 +432,9 @@ } //! Return the array as a matrix. - Array<T> as_matrix (void) const + Array<T, Alloc> as_matrix (void) const { - Array<T> retval (*this); + Array<T, Alloc> retval (*this); if (m_dimensions.ndims () != 2) retval.m_dimensions = m_dimensions.redim (2); @@ -462,16 +487,17 @@ const dim_vector& dims (void) const { return m_dimensions; } //! Chop off leading singleton dimensions - OCTARRAY_API Array<T> squeeze (void) const; + OCTARRAY_API Array<T, Alloc> squeeze (void) const; OCTARRAY_API octave_idx_type compute_index (octave_idx_type i, octave_idx_type j) const; OCTARRAY_API octave_idx_type compute_index (octave_idx_type i, octave_idx_type j, octave_idx_type k) const; OCTARRAY_API octave_idx_type compute_index (const Array<octave_idx_type>& ra_idx) const; - octave_idx_type compute_index_unchecked (const Array<octave_idx_type>& ra_idx) - const - { return m_dimensions.compute_index (ra_idx.data (), ra_idx.numel ()); } + octave_idx_type compute_index_unchecked (const Array<octave_idx_type>& ra_idx) const + { + return m_dimensions.compute_index (ra_idx.data (), ra_idx.numel ()); + } // No checking, even for multiple references, ever. @@ -517,7 +543,7 @@ { return elem (i, dim2 ()*k+j); } T& elem (const Array<octave_idx_type>& ra_idx) - { return Array<T>::elem (compute_index_unchecked (ra_idx)); } + { return Array<T, Alloc>::elem (compute_index_unchecked (ra_idx)); } T& operator () (octave_idx_type n) { return elem (n); } T& operator () (octave_idx_type i, octave_idx_type j) { return elem (i, j); } @@ -544,7 +570,7 @@ { return xelem (i, j, k); } crefT elem (const Array<octave_idx_type>& ra_idx) const - { return Array<T>::xelem (compute_index_unchecked (ra_idx)); } + { return Array<T, Alloc>::xelem (compute_index_unchecked (ra_idx)); } crefT operator () (octave_idx_type n) const { return elem (n); } crefT operator () (octave_idx_type i, octave_idx_type j) const @@ -558,22 +584,22 @@ // Fast extractors. All of these produce shallow copies. //! Extract column: A(:,k+1). - OCTARRAY_API Array<T> column (octave_idx_type k) const; + OCTARRAY_API Array<T, Alloc> column (octave_idx_type k) const; //! Extract page: A(:,:,k+1). - OCTARRAY_API Array<T> page (octave_idx_type k) const; + OCTARRAY_API Array<T, Alloc> page (octave_idx_type k) const; //! Extract a slice from this array as a column vector: A(:)(lo+1:up). //! Must be 0 <= lo && up <= numel. May be up < lo. - OCTARRAY_API Array<T> linear_slice (octave_idx_type lo, octave_idx_type up) const; + OCTARRAY_API Array<T, Alloc> linear_slice (octave_idx_type lo, octave_idx_type up) const; - Array<T> reshape (octave_idx_type nr, octave_idx_type nc) const - { return Array<T> (*this, dim_vector (nr, nc)); } + Array<T, Alloc> reshape (octave_idx_type nr, octave_idx_type nc) const + { return Array<T, Alloc> (*this, dim_vector (nr, nc)); } - Array<T> reshape (const dim_vector& new_dims) const - { return Array<T> (*this, new_dims); } + Array<T, Alloc> reshape (const dim_vector& new_dims) const + { return Array<T, Alloc> (*this, new_dims); } - OCTARRAY_API Array<T> permute (const Array<octave_idx_type>& vec, bool inv = false) const; - Array<T> ipermute (const Array<octave_idx_type>& vec) const + OCTARRAY_API Array<T, Alloc> permute (const Array<octave_idx_type>& vec, bool inv = false) const; + Array<T, Alloc> ipermute (const Array<octave_idx_type>& vec) const { return permute (vec, true); } bool issquare (void) const { return (dim1 () == dim2 ()); } @@ -584,8 +610,8 @@ bool is_nd_vector (void) const { return m_dimensions.is_nd_vector (); } - OCTARRAY_API Array<T> transpose (void) const; - OCTARRAY_API Array<T> hermitian (T (*fcn) (const T&) = nullptr) const; + OCTARRAY_API Array<T, Alloc> transpose (void) const; + OCTARRAY_API Array<T, Alloc> hermitian (T (*fcn) (const T&) = nullptr) const; const T * data (void) const { return m_slice_data; } @@ -602,11 +628,11 @@ //@{ //! Indexing without resizing. - OCTARRAY_API Array<T> index (const octave::idx_vector& i) const; + OCTARRAY_API Array<T, Alloc> index (const octave::idx_vector& i) const; - OCTARRAY_API Array<T> index (const octave::idx_vector& i, const octave::idx_vector& j) const; + OCTARRAY_API Array<T, Alloc> index (const octave::idx_vector& i, const octave::idx_vector& j) const; - OCTARRAY_API Array<T> index (const Array<octave::idx_vector>& ia) const; + OCTARRAY_API Array<T, Alloc> index (const Array<octave::idx_vector>& ia) const; //@} virtual OCTARRAY_API T resize_fill_value (void) const; @@ -632,24 +658,24 @@ // FIXME: this is really a corner case, that should better be // handled directly in liboctinterp. - OCTARRAY_API Array<T> index (const octave::idx_vector& i, bool resize_ok, const T& rfv) const; - Array<T> index (const octave::idx_vector& i, bool resize_ok) const + OCTARRAY_API Array<T, Alloc> index (const octave::idx_vector& i, bool resize_ok, const T& rfv) const; + Array<T, Alloc> index (const octave::idx_vector& i, bool resize_ok) const { return index (i, resize_ok, resize_fill_value ()); } - OCTARRAY_API Array<T> index (const octave::idx_vector& i, const octave::idx_vector& j, + OCTARRAY_API Array<T, Alloc> index (const octave::idx_vector& i, const octave::idx_vector& j, bool resize_ok, const T& rfv) const; - Array<T> index (const octave::idx_vector& i, const octave::idx_vector& j, + Array<T, Alloc> index (const octave::idx_vector& i, const octave::idx_vector& j, bool resize_ok) const { return index (i, j, resize_ok, resize_fill_value ()); } - OCTARRAY_API Array<T> index (const Array<octave::idx_vector>& ia, bool resize_ok, + OCTARRAY_API Array<T, Alloc> index (const Array<octave::idx_vector>& ia, bool resize_ok, const T& rfv) const; - Array<T> index (const Array<octave::idx_vector>& ia, bool resize_ok) const + Array<T, Alloc> index (const Array<octave::idx_vector>& ia, bool resize_ok) const { return index (ia, resize_ok, resize_fill_value ()); } @@ -657,22 +683,22 @@ //@{ //! Indexed assignment (always with resize & fill). - OCTARRAY_API void assign (const octave::idx_vector& i, const Array<T>& rhs, const T& rfv); - void assign (const octave::idx_vector& i, const Array<T>& rhs) + OCTARRAY_API void assign (const octave::idx_vector& i, const Array<T, Alloc>& rhs, const T& rfv); + void assign (const octave::idx_vector& i, const Array<T, Alloc>& rhs) { assign (i, rhs, resize_fill_value ()); } OCTARRAY_API void assign (const octave::idx_vector& i, const octave::idx_vector& j, - const Array<T>& rhs, + const Array<T, Alloc>& rhs, const T& rfv); - void assign (const octave::idx_vector& i, const octave::idx_vector& j, const Array<T>& rhs) + void assign (const octave::idx_vector& i, const octave::idx_vector& j, const Array<T, Alloc>& rhs) { assign (i, j, rhs, resize_fill_value ()); } - OCTARRAY_API void assign (const Array<octave::idx_vector>& ia, const Array<T>& rhs, const T& rfv); - void assign (const Array<octave::idx_vector>& ia, const Array<T>& rhs) + OCTARRAY_API void assign (const Array<octave::idx_vector>& ia, const Array<T, Alloc>& rhs, const T& rfv); + void assign (const Array<octave::idx_vector>& ia, const Array<T, Alloc>& rhs) { assign (ia, rhs, resize_fill_value ()); } @@ -695,10 +721,10 @@ //! size (a) is [d1 d2 ... dN] and idx is [i1 i2 ... iN], this //! method is equivalent to x(i1:i1+d1-1, i2:i2+d2-1, ... , //! iN:iN+dN-1) = a. - OCTARRAY_API Array<T>& insert (const Array<T>& a, const Array<octave_idx_type>& idx); + OCTARRAY_API Array<T, Alloc>& insert (const Array<T, Alloc>& a, const Array<octave_idx_type>& idx); //! This is just a special case for idx = [r c 0 ...] - OCTARRAY_API Array<T>& insert (const Array<T>& a, octave_idx_type r, octave_idx_type c); + OCTARRAY_API Array<T, Alloc>& insert (const Array<T, Alloc>& a, octave_idx_type r, octave_idx_type c); void maybe_economize (void) { @@ -713,9 +739,9 @@ OCTARRAY_API void print_info (std::ostream& os, const std::string& prefix) const; - OCTARRAY_API Array<T> sort (int dim = 0, sortmode mode = ASCENDING) const; - OCTARRAY_API Array<T> sort (Array<octave_idx_type>& sidx, int dim = 0, - sortmode mode = ASCENDING) const; + OCTARRAY_API Array<T, Alloc> sort (int dim = 0, sortmode mode = ASCENDING) const; + OCTARRAY_API Array<T, Alloc> sort (Array<octave_idx_type>& sidx, int dim = 0, + sortmode mode = ASCENDING) const; //! Ordering is auto-detected or can be specified. OCTARRAY_API sortmode issorted (sortmode mode = UNSORTED) const; @@ -732,8 +758,8 @@ //! Ditto, but for an array of values, specializing on the case when values //! are sorted. NaNs get the value N. - OCTARRAY_API Array<octave_idx_type> lookup (const Array<T>& values, - sortmode mode = UNSORTED) const; + OCTARRAY_API Array<octave_idx_type> lookup (const Array<T, Alloc>& values, + sortmode mode = UNSORTED) const; //! Count nonzero elements. OCTARRAY_API octave_idx_type nnz (void) const; @@ -741,37 +767,37 @@ //! Find indices of (at most n) nonzero elements. If n is specified, //! backward specifies search from backward. OCTARRAY_API Array<octave_idx_type> find (octave_idx_type n = -1, - bool backward = false) const; + bool backward = false) const; //! Returns the n-th element in increasing order, using the same //! ordering as used for sort. n can either be a scalar index or a //! contiguous range. - OCTARRAY_API Array<T> nth_element (const octave::idx_vector& n, int dim = 0) const; + OCTARRAY_API Array<T, Alloc> nth_element (const octave::idx_vector& n, int dim = 0) const; //! Get the kth super or subdiagonal. The zeroth diagonal is the //! ordinary diagonal. - OCTARRAY_API Array<T> diag (octave_idx_type k = 0) const; + OCTARRAY_API Array<T, Alloc> diag (octave_idx_type k = 0) const; - OCTARRAY_API Array<T> diag (octave_idx_type m, octave_idx_type n) const; + OCTARRAY_API Array<T, Alloc> diag (octave_idx_type m, octave_idx_type n) const; //! Concatenation along a specified (0-based) dimension, equivalent //! to cat(). dim = -1 corresponds to dim = 0 and dim = -2 //! corresponds to dim = 1, but apply the looser matching rules of //! vertcat/horzcat. - static OCTARRAY_API Array<T> - cat (int dim, octave_idx_type n, const Array<T> *array_list); + static OCTARRAY_API Array<T, Alloc> + cat (int dim, octave_idx_type n, const Array<T, Alloc> *array_list); - //! Apply function fcn to each element of the Array<T>. This function + //! Apply function fcn to each element of the Array<T, Alloc>. This function //! is optimized with a manually unrolled loop. - template <typename U, typename F> - Array<U> + template <typename U, typename F, typename A = std::allocator<U>> + Array<U, A> map (F fcn) const { octave_idx_type len = numel (); const T *m = data (); - Array<U> result (dims ()); + Array<U, A> result (dims ()); U *p = result.fortran_vec (); octave_idx_type i; @@ -795,15 +821,15 @@ //@{ //! Overloads for function references. - template <typename U> - Array<U> + template <typename U, typename A = std::allocator<U>> + Array<U, A> map (U (&fcn) (T)) const - { return map<U, U (&) (T)> (fcn); } + { return map<U, U (&) (T), A> (fcn); } - template <typename U> - Array<U> + template <typename U, typename A = std::allocator<U>> + Array<U, A> map (U (&fcn) (const T&)) const - { return map<U, U (&) (const T&)> (fcn); } + { return map<U, U (&) (const T&), A> (fcn); } //@} //! Generic any/all test functionality with arbitrary predicate. @@ -839,7 +865,7 @@ { return test<bool (&) (const T&), true> (fcn); } //@} - template <typename U> friend class Array; + template <typename U, typename A> friend class Array; //! Returns true if this->dims () == dv, and if so, replaces this->m_dimensions //! by a shallow copy of dv. This is useful for maintaining several arrays @@ -853,10 +879,10 @@ // We use a variadic template for template template parameter so that // we don't have to specify all the template parameters and limit this // to Container<T>. http://stackoverflow.com/a/20499809/1609556 -template<typename T> +template<typename T, typename Alloc> template<template <typename...> class Container> -Array<T>::Array (const Container<T>& a, const dim_vector& dv) - : m_dimensions (dv), m_rep (new typename Array<T>::ArrayRep (dv.safe_numel ())), +Array<T, Alloc>::Array (const Container<T>& a, const dim_vector& dv) + : m_dimensions (dv), m_rep (new typename Array<T, Alloc>::ArrayRep (dv.safe_numel ())), m_slice_data (m_rep->m_data), m_slice_len (m_rep->m_len) { if (m_dimensions.safe_numel () != octave_idx_type (a.size ())) @@ -875,8 +901,8 @@ m_dimensions.chop_trailing_singletons (); } -template <typename T> +template <typename T, typename Alloc> OCTARRAY_API std::ostream& -operator << (std::ostream& os, const Array<T>& a); +operator << (std::ostream& os, const Array<T, Alloc>& a); #endif