Constraints: On the target platform I can neither use dynamic memory allocation nor the C++ Standard Library or any other third-party libraries. The language is restricted to C++11 without the usage of compiler specific extensions.
How to handle arrays (or non-owning views) with variable size without dynamic memory allocation and guarantee of valid memory? The size can not be part of the type (as for example with std::array
). Arrays can be defined statically. The container type does not need to own the elements but can be initialized with (a pointer to) static memory. The main objective is that the provided data / elements are in valid memory. Secondary would be that the container object provides the number of (valid) elements.
For example the following shall be achieved:
struct S { X x; };
Where S
has a fixed size and X
is some kind of container pointing to or composed of a number of elements. The number of elements shall not be part of the type X
(shall be no class template depending on the size) but is constant for each object and may be a const
member.
It must be ensured that the memory pointed to is valid. For example it shall not point to a former automatic variable.
The following things did not work out:
- Usage of
std::vector
(or similar): Would require to implement that container without the use of dynamic memory allocation. - In a similar question it has been suggested to use
boost::container::static_vector
. The caveat is that the maximum capacity has to be statically served for each object, if I understood it correctly. This is not feasible as it can not be estimated what a suitable limit would be. Also allocating superfluous static memory must be avoided as memory is expensive on the target platform. std::array
can not be used as it contains the size in its type.- Usage of
alloca()
is out of question.
I did define a non-owning view over a contiguous sequence of objects (resembling template<class T> std::span<T, std::dynamic_extent>
) and initialize it with an array with static storage duration. Here is a simplified example:
This is the example type:
template<typename ElementType>
struct Container
{
public:
// type containing size can be used to pass size to `Container()` as template argument
template<ElementType * POINTER, std::size_t N> struct Configuration {};
// constructor accepts pointer to object with static storage duration only
template<ElementType * POINTER, std::size_t N>
Container(const Configuration<POINTER, N>&) : data(POINTER), numberOfElements(N) {}
//! @return number of elements
constexpr std::size_t size() const noexcept { return numberOfElements; }
constexpr ElementType& operator[](const std::size_t index) const noexcept { return data[index]; }
private:
ElementType* const data;
const std::size_t numberOfElements;
};
The restriction to static storage duration of the pointer is achieved by the constructor. This needs an object of Container::Configuration
. This in turn requires a pointer as template argument which must have static storage duration (by language definition).
Here is how to use it:
// prints the elements of the container
template<typename T>
void printElements(const Container<T>& container)
{
for(std::size_t i=0; i<container.size(); ++i)
{
std::cout << container[i] << " ";
}
std::cout << std::endl;
}
// array with static storage duration
int globalArray[] = { 1111, 2222, 3333, 4444 };
int main()
{
Container<int>::Configuration<globalArray, sizeof(globalArray)/sizeof(globalArray[0])> arrayConfig;
Container<int> cB(arrayConfig);
printElements(cB);
return 0;
}
It accomplishes that the argument passed to printElements
can not point to invalid memory.
This works so far in simple examples. It also contains the size which is useful compared to a plain pointer. And would be even more handy if local static values would have static storage duration in C++11 (they have apparently in C++17).
Before I require this type to be used on a large scale within the target software I wonder if there may be more suitable ways to achieve the desired safety guarantees.