2
\$\begingroup\$

Here is a thirdfollow up on Reusable storage for array of objects, Reusable storage for array of objects V2 and Reusable storage for array of objects V3, taking into account the provided answers.
The following code should be compliant at least with gcc, clang, msvc and for C++14 and beyond.
In this version, I leave only the core code for lisibility and provide a testbench on Compiler Explorer.

#include <cstddef>
#include <cstdint>
// @ adding forgotten cstdlib
#include <cstdlib>
#include <limits>
#include <new>
#include <type_traits>

// type-punning reusable buffer
// holds a non-typed buffer (actually a char*) that can be used to store any
// types, according to user needs
class Buffer {
   private:
    // destructing functor storage
    // required to call the correct object destructors
    void (*Destructors)(Buffer*) noexcept = nullptr;
    // storage address
    unsigned char* Storage = nullptr;
    // objects address
    unsigned char* Objects = nullptr;
    // number of stored elements
    std::size_t CountOfStoredObjects = 0;
    // buffer size in bytes
    std::size_t StorageSizeInBytes = 0;

    // computes the smallest positive offset in bytes that must be applied to
    // Storage in order to have alignment a Alignment is supposed to be a valid
    // alignment
    static std::size_t OffsetForAlignment(unsigned char const* const Ptr,
                                          std::size_t Alignment) noexcept {
        std::size_t Res = static_cast<std::size_t>(
            reinterpret_cast<std::uintptr_t>(Ptr) % Alignment);
        if (Res) {
            return Alignment - Res;
        } else {
            return 0;
        }
    }

   public:
    // allocates a char buffer large enough for Counts object of type T and
    // default-construct them
    template <typename T>
    T* DefaultAllocate(const std::size_t Count) {
        if (Count > (std::numeric_limits<std::size_t>::max() - alignof(T)) /
                        sizeof(T)) {
            throw std::bad_alloc();
        }
        // Destroy previously stored objects
        if (Destructors) {
            Destructors(this);
        }
        std::size_t RequiredSize = sizeof(T) * Count + alignof(T);
        if (StorageSizeInBytes < RequiredSize) {
            // Creating a storage of RequiredSize bytes
            unsigned char* NewStorage = reinterpret_cast<unsigned char*>(
                std::realloc(Storage, RequiredSize));
            if (NewStorage == nullptr) {
                throw std::bad_alloc();
            }
            Storage = NewStorage;
            StorageSizeInBytes = RequiredSize;
        }
        unsigned char* AlignedStorage =
            Storage + OffsetForAlignment(Storage, alignof(T));
        // initializing an dynamic array of T on the storage
        T* Tmp = ::new (AlignedStorage) T[Count];
        Objects = reinterpret_cast<unsigned char*>(Tmp);
        // update nb of objects
        CountOfStoredObjects = Count;

        // create destructors functor
        Destructors = [](Buffer* PBuffer) noexcept {
            PBuffer->Destructors = nullptr;
            T* ToDestruct = reinterpret_cast<T*>(PBuffer->Objects);
            // Delete elements in reverse order of creation
            while (PBuffer->CountOfStoredObjects > 0) {
                --(PBuffer->CountOfStoredObjects);
                ToDestruct[(PBuffer->CountOfStoredObjects)].~T();
            }
            ::operator delete[](ToDestruct, ToDestruct);
            PBuffer->CountOfStoredObjects = 0;
            PBuffer->Objects = nullptr;
        };
        return Tmp;
    }

    ~Buffer() noexcept {
        // Ending objects lifetime
        if (Destructors) {
            Destructors(this);
        }
        // Releasing storage
        std::free(Storage);
    }
};

[EDIT] pinpointing fixes with // @ ... and replacing ```std::function``` by a simplier function pointer

\$\endgroup\$
2
  • 1
    \$\begingroup\$ Sorry if I missed some, I thought I did. Give me time to go other it again. I will post a comment when I'm done. \$\endgroup\$
    – Oersted
    Commented Sep 7, 2023 at 10:26
  • \$\begingroup\$ @G.Sliepen I'm utterly sorry, I didn't find what issue I may have forgotten (perhaps I didn't fix them correctly). I made an edit to pinpoint where I addressed the mentioned problems. Btw I also managed to remove std::function. I had an issue with msvc/C++14 that was merely due to a missing compiler option. \$\endgroup\$
    – Oersted
    Commented Sep 7, 2023 at 16:31

0