HiveBrain v1.2.0
Get Started
← Back to all entries
patterncppMinor

Fast variable-length stack allocator for vector<> in C++

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
faststacklengthforallocatorvariablevector

Problem

I wrote a variable-length stack allocator for the vector<> class in C++ 11. In order to be able to allocate size dynamically at runtime I made use of the non-standard alloca() function, which is available in a multitude of C++ implementations, including GCC and Visual Studio.

The purpose of this class is to improve performance of allocation of small arrays on the stack whose size cannot be determined at compile-time while still retaining the helpful features of the vector<> class.

```
#pragma once

#include

template
class stack_allocator {
template friend class stack_allocator;

public:
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef T* pointer;
typedef const T* const_pointer;
typedef T& reference;
typedef const T& const_reference;
typedef T value_type;

template
struct rebind {
typedef stack_allocator other;
};

private:
T* ptr;
size_t currentSize, maxSize;

public:
stack_allocator() noexcept :
ptr(nullptr),
currentSize(0),
maxSize(0) {
}

stack_allocator(T* buffer, size_t size) noexcept :
ptr(buffer),
currentSize(0),
maxSize(size) {
}

template
explicit stack_allocator(const stack_allocator& other) noexcept :
ptr(reinterpret_cast(other.ptr)),
currentSize(other.currentSize),
maxSize(other.maxSize) {
}

T allocate(size_t n, const void hint = nullptr) {
T* pointer = ptr + currentSize;
currentSize += n;
return pointer;
}

void deallocate(T* p, size_t n) {
currentSize -= n;
}

size_t capacity() const noexcept {
return maxSize;
}

size_t max_size() const noexcept {
return maxSize;
}

T* address(T& x) const noexcept {
return &x;
}

const T* address(const T& x) const noexcept {
return &x;
}

T* buffer() const noexcept {
return ptr;
}

template
st

Solution

Well this is going to cause problems:

void deallocate(T* p, size_t n) {
    currentSize -= n;
}


You can't assume that the last allocated object is the one that is de-allocated (I would even say that will never happen).

As a result your next call to allocate is going to re-use that memory even though it is already being used.

T* allocate(size_t n, const void* hint = nullptr) {
    T* pointer = ptr + currentSize;
    currentSize += n;
    return pointer;
}


Your allocator assumes that its memory is allocated with alloca(). But the interface allows any memory to be injected so it has a high likely hood that it is going to be used incorrectly and leak memory. If you are assuming that the memory is going to be free'ed dynamically like that then you need to design the allocator to allocate the appropriate memory.

Also relying on this kind of low level functions is dangerous. What happens when the vector is part of an object? What if the object that holds it is dynamically allocated? There are too many flaws in this design for it be used anywhere apart from your one small use case senario and even then it will need to be well documented to make sure that a future maintainer does not break it.

Code Snippets

void deallocate(T* p, size_t n) {
    currentSize -= n;
}
T* allocate(size_t n, const void* hint = nullptr) {
    T* pointer = ptr + currentSize;
    currentSize += n;
    return pointer;
}

Context

StackExchange Code Review Q#155389, answer score: 6

Revisions (0)

No revisions yet.