fprime/Fw/DataStructures/ExternalArray.hpp
Rob Bocchino 9871fd5608
Call destructors when ExternalArray releases memory for which it has called constructors (#4143)
* Revise SDD for ExternalArray

Add element destructor calls

* Revise ExternalArray

Destroy array elements allocated as bare memory

* Refactor ExternalArray

* Refactor ExternalArray

* Revise commment

* Revise comment

* Revise SDD for ExternalArray
2025-09-11 19:16:45 -07:00

200 lines
6.7 KiB
C++

// ======================================================================
// \file ExternalArray.hpp
// \author bocchino
// \brief A bounds-checked array with external memory
// ======================================================================
#ifndef Fw_ExternalArray_HPP
#define Fw_ExternalArray_HPP
#include <cstdint>
#include <new>
#include <type_traits>
#include "Fw/FPrimeBasicTypes.hpp"
#include "Fw/Types/Assert.hpp"
#include "Fw/Types/ByteArray.hpp"
namespace Fw {
template <typename T>
class ExternalArray final {
// ----------------------------------------------------------------------
// Static assertions
// ----------------------------------------------------------------------
static_assert(std::is_assignable<T&, T>::value, "T must be assignable to T&");
public:
// ----------------------------------------------------------------------
// Public constructors and destructors
// ----------------------------------------------------------------------
//! Zero-argument constructor
ExternalArray() {}
//! Constructor providing typed backing storage.
//! elements must point to at least size elements of type T.
ExternalArray(T* elements, //!< The elements
FwSizeType size //!< The array size
)
: m_elements(elements), m_size(size) {}
//! Constructor providing untyped backing storage.
//! data must be aligned according to getByteArrayAlignment().
//! data must contain at least getByteArraySize(size) bytes.
ExternalArray(ByteArray data, //!< The data
FwSizeType size //!< The array size
) {
this->setStorage(data, size);
}
//! Copy constructor
ExternalArray(const ExternalArray<T>& a) : m_elements(a.m_elements), m_size(a.m_size) {}
//! Destructor
~ExternalArray() { this->releaseStorage(); }
public:
// ----------------------------------------------------------------------
// Public member functions
// ----------------------------------------------------------------------
//! Subscript operator
//! \return The element at index i
T& operator[](const FwSizeType i //!< The subscript index
) {
FW_ASSERT(this->m_elements != nullptr);
FW_ASSERT(i < this->m_size, static_cast<FwAssertArgType>(i));
return this->m_elements[i];
}
//! Const subscript operator
//! \return The element at index i
const T& operator[](const FwSizeType i //!< The subscript index
) const {
FW_ASSERT(this->m_elements != nullptr);
FW_ASSERT(i < this->m_size, static_cast<FwAssertArgType>(i));
return this->m_elements[i];
}
//! Copy assignment operator
//! \return *this
ExternalArray<T>& operator=(const ExternalArray<T>& a) {
if (&a != this) {
this->m_elements = a.m_elements;
this->m_size = a.m_size;
}
return *this;
}
//! Copy the data from a
void copyDataFrom(const ExternalArray<T>& a) {
const FwSizeType size = FW_MIN(this->m_size, a.m_size);
for (FwSizeType i = 0; i < size; i++) {
(*this)[i] = a[i];
}
}
//! Get a mutable pointer to the elements
//! \return A mutable pointer to the elements
T* getElements() { return this->m_elements; }
//! Get a const pointer to the elements
//! \return A const pointer to the elements
const T* getElements() const { return this->m_elements; }
//! Get the size
//! \return The size
FwSizeType getSize() const { return this->m_size; }
//! Set the backing storage (typed data)
void setStorage(T* elements, //!< The array elements
FwSizeType size //!< The size
) {
this->releaseStorage();
this->m_elements = elements;
this->m_size = size;
this->m_destroyElementsOnRelease = false;
}
//! Set the backing storage (untyped data)
//! Data must be aligned for T and must contain at least getByteArraySize(size) bytes.
void setStorage(ByteArray data, //!< The data
FwSizeType size //!< The array size
) {
// Check that data.bytes is not null
FW_ASSERT(data.bytes != nullptr);
// Check that data.bytes is properly aligned
FW_ASSERT(reinterpret_cast<uintptr_t>(data.bytes) % alignof(T) == 0);
// Check that data.size is large enough to hold the array
FW_ASSERT(size * sizeof(T) <= data.size);
// Release the backing storage
this->releaseStorage();
// Initialize the array members
this->m_elements = reinterpret_cast<T*>(data.bytes);
// Construct the array members in place
// This step ensures that each array element holds a valid object
// into which we can assign data
for (FwSizeType i = 0; i < size; i++) {
// This code trips an alignment check in clang-tidy
// However the alignment has been checked by FW_ASSERT above
(void)new (&this->m_elements[i]) T(); // NOLINT
}
// Set the size
this->m_size = size;
// Destroy elements on release of storage
this->m_destroyElementsOnRelease = true;
}
public:
// ----------------------------------------------------------------------
// Public static functions
// ----------------------------------------------------------------------
//! Get the alignment of the storage for an ExternalArray
//! \return The alignment
static constexpr U8 getByteArrayAlignment() { return alignof(T); }
//! Get the size of the storage for an ExternalArray of the specified size,
//! as a byte array
//! \return The byte array size
static constexpr FwSizeType getByteArraySize(FwSizeType size //!< The size
) {
return size * sizeof(T);
}
private:
// ----------------------------------------------------------------------
// Private member functions
// ----------------------------------------------------------------------
//! Release the backing storage
void releaseStorage() {
if ((this->m_elements != nullptr) && this->m_destroyElementsOnRelease) {
for (FwSizeType i = 0; i < this->m_size; i++) {
this->m_elements[i].~T();
}
this->m_destroyElementsOnRelease = false;
}
}
private:
// ----------------------------------------------------------------------
// Private member variables
// ----------------------------------------------------------------------
//! The array elements
T* m_elements = nullptr;
//! The size
FwSizeType m_size = 0;
//! Whether to destroy the array elements when the backing storage is released
bool m_destroyElementsOnRelease = false;
};
} // namespace Fw
#endif