patterncppMinor
A working stack allocator
Viewed 0 times
allocatorworkingstack
Problem
Here's an absolutely essential piece of C++ lore, a stack allocator, that will allow you to, say, allocate strings and vectors on the stack. There are 2 stack allocators I know of, here and here.
The trouble was, neither of them were working with gcc-4.8, and both needed fixing. Here's a fixed version of Hinant's allocator. Could there some improvement or fix to still be made?
```
#pragma once
#ifndef STACKALLOCATOR_HPP
# define STACKALLOCATOR_HPP
#include
#include
#include
#include
#include
template
class stack_store
{
public:
stack_store() = default;
stack_store(stack_store const&) = delete;
stack_store& operator=(stack_store const&) = delete;
char* allocate(std::size_t n)
{
assert(pointer_in_buffer(ptr_) &&
"stack_allocator has outlived stack_store");
n = align(n);
if (buf_ + N >= ptr_ + n)
{
auto r(ptr_);
ptr_ += n;
return r;
}
else
{
return static_cast(::operator new(n));
}
}
void deallocate(char* const p, std::size_t n) noexcept
{
assert(pointer_in_buffer(ptr_) &&
"stack_allocator has outlived stack_store");
if (pointer_in_buffer(p))
{
n = align(n);
if (p + n == ptr_)
{
ptr_ = p;
}
// else do nothing
}
else
{
::operator delete(p);
}
}
void reset() noexcept { ptr_ = buf_; }
static constexpr ::std::size_t size() noexcept { return N; }
::std::size_t used() const { return ::std::size_t(ptr_ - buf_); }
private:
static constexpr ::std::size_t align(::std::size_t const n) noexcept
{
return (n + (alignment - 1)) & -alignment;
}
bool pointer_in_buffer(char* const p) noexcept
{
return (buf_
class stack_allocator
{
public:
using store_type = stack_store;
using size_type = ::std::size_t;
using difference_type = ::std::ptrdiff_t;
using pointer = T*;
using const_pointer = T const*;
using reference = T&;
using const_reference = T const&;
using va
The trouble was, neither of them were working with gcc-4.8, and both needed fixing. Here's a fixed version of Hinant's allocator. Could there some improvement or fix to still be made?
```
#pragma once
#ifndef STACKALLOCATOR_HPP
# define STACKALLOCATOR_HPP
#include
#include
#include
#include
#include
template
class stack_store
{
public:
stack_store() = default;
stack_store(stack_store const&) = delete;
stack_store& operator=(stack_store const&) = delete;
char* allocate(std::size_t n)
{
assert(pointer_in_buffer(ptr_) &&
"stack_allocator has outlived stack_store");
n = align(n);
if (buf_ + N >= ptr_ + n)
{
auto r(ptr_);
ptr_ += n;
return r;
}
else
{
return static_cast(::operator new(n));
}
}
void deallocate(char* const p, std::size_t n) noexcept
{
assert(pointer_in_buffer(ptr_) &&
"stack_allocator has outlived stack_store");
if (pointer_in_buffer(p))
{
n = align(n);
if (p + n == ptr_)
{
ptr_ = p;
}
// else do nothing
}
else
{
::operator delete(p);
}
}
void reset() noexcept { ptr_ = buf_; }
static constexpr ::std::size_t size() noexcept { return N; }
::std::size_t used() const { return ::std::size_t(ptr_ - buf_); }
private:
static constexpr ::std::size_t align(::std::size_t const n) noexcept
{
return (n + (alignment - 1)) & -alignment;
}
bool pointer_in_buffer(char* const p) noexcept
{
return (buf_
class stack_allocator
{
public:
using store_type = stack_store;
using size_type = ::std::size_t;
using difference_type = ::std::ptrdiff_t;
using pointer = T*;
using const_pointer = T const*;
using reference = T&;
using const_reference = T const&;
using va
Solution
Fixes required for a conforming Standard Library
There is one big issue that will make custom allocators fragile to work with: incomplete C++11 library support. Since C++11, containers are required to go through
With the Coliru online compiler, I can get Hinnant's stack allocator working with
The first is to put parenthesis around the
The second is to remove the exception specification from the overloaded
Live Example exactly reproducing Hinnant's first test case.
Note that these 2 warnings are rather innocent and do not affect correctness of the program.
Fixes required for a non-conforming Standard Library
Since
That leaves the issues of constructors taking a single allocator argument. At least for
There is one big issue that will make custom allocators fragile to work with: incomplete C++11 library support. Since C++11, containers are required to go through
std::allocator_traits to access construct() and destroy() allocator member functions, as well as to access nested typedefs such as pointer and reference. Furthermore, all containers have a constructor with a single allocator argument. Not every Standard Library implements this for every container.With the Coliru online compiler, I can get Hinnant's stack allocator working with
libstdc++ for a std::vector with only 2 minor modifications running g++ -std=c++11 -O1 -pedantic -Wall -Wconversion -Wsign-conversion -Wsign-promoThe first is to put parenthesis around the
n + (alignment - 1) operand in the align_up()std::size_t
align_up(std::size_t n) noexcept
{return (n + (alignment-1)) & ~(alignment-1);}
^ ^The second is to remove the exception specification from the overloaded
operator newvoid* operator new(std::size_t s) // throw(std::bad_alloc)Live Example exactly reproducing Hinnant's first test case.
Note that these 2 warnings are rather innocent and do not affect correctness of the program.
Fixes required for a non-conforming Standard Library
Since
libstdc++ works fine with all containers if a std::allocator is provided (even when the container implementation directly accesses the allocator, rather than through std::allocator_traits) the safest bet is to provide all the nested typedefs and member functions of std::allocator_traits in your own allocator as well. This includes the various pointer and reference types, as well as the rebind templates. You appear to have done so, and this should resolve at least of all those issues. I still would leave the original naming of Hinnant's version in tact, though.That leaves the issues of constructors taking a single allocator argument. At least for
libstdc++ for g++ 4.8.1, this constructor is missing for std:unordered_map. Apart from patching the standard library headers yourself (possible, but if you have an automatic package updater, that will require constant monitoring) or submit a bug report. Microsoft did have many such issues in Visual C++ 2012 November CTP, which have all been fixed in Visual C++ 2013.Code Snippets
g++ -std=c++11 -O1 -pedantic -Wall -Wconversion -Wsign-conversion -Wsign-promostd::size_t
align_up(std::size_t n) noexcept
{return (n + (alignment-1)) & ~(alignment-1);}
^ ^void* operator new(std::size_t s) // throw(std::bad_alloc)Context
StackExchange Code Review Q#31528, answer score: 8
Revisions (0)
No revisions yet.