Lumiera  0.pre.03
»edit your freedom«
uninitialised-storage.hpp File Reference

Go to the source code of this file.

Description

A raw memory block with proper alignment and array access.

This is a building block for custom containers and memory management schemes. Regular containers in C++ always ensure invocation of any object's constructors and destructors – which is a boon and prevents a lot of consistency problems. When constructing custom management schemes however, this automatic initialisation can be problematic; some objects require constructor arguments, preventing mass initialisation; and initialising a large memory block has considerable cost, which may be wasted it the intended usage is to plant objects into that space later, on demand. The std::vector::reserve(size) function can often be used to circumvent those problems — yet unfortunately std::vector has the hard requirement on the content objects to be at least movable to support automatic storage growth.

A traditional workaround is to exploit a programming trick relying on a forced cast: Storage is declared as char[] without initialiser; since char is a primitive type, C++ will not default-initialise the storage then for sake of plain-old-C compatibility. A special accessor function will then force-cast into the desired target type. However well established, this practice is riddled with various problems

  • it requires very precise setup and any mistake leads to memory corruption
  • according to the C++ standard, such an access involves undefined behaviour
  • compilers are getting better and increasingly aggressive with performing global optimisation and may miss the secondary hidden access under some circumstances.
  • when objects with const data fields are placed into such a buffer, the compiler may perform constant folding based on the current/initial object values and thus fail to propagate changes due to other object instances being placed into storage.

Attempts were made to codify this kind of usage properly into the standard; these turned out to be problematic however and were deprecated again. Starting with C++17, fortunately there is now a way to express this kind of raw unprotected access through standard conformant ways and marked clearly to prevent overly zealous optimisation. Since the typical use is for allocating a series of storage slots with a well defined target type, this implementation relies on std::array as »front-end« for access.

  • target type T and size are given as template parameters
  • the storage is defined as std::byte to express its very purpose
  • the secondary access is marked by std::launder to prevent optimisation
  • an implicit conversion path to the corresponding array type is provided
  • subscript operators enable direct access to the raw storage
  • helper functions allow for placement new and delete.
See also
extent-family.hpp
vault::gear::TestChainLoad::Rule where this setup matters

Definition in file uninitialised-storage.hpp.

#include <cstddef>
#include <utility>
#include <array>
#include <new>

Classes

class  UninitialisedDynBlock< T >
 Managed uninitialised Heap-allocated storage with array like access. More...
 
class  UninitialisedStorage< T, cnt >
 Block of raw uninitialised storage with array like access. More...
 

Namespaces

 lib
 Implementation namespace for support and library code.