[libc++] Fix stack_allocator

Summary:
To quote STL the problems with stack allocator are"

>"stack_allocator<T, N> is seriously nonconformant to N4582 17.6.3.5 [allocator.requirements].
> First, it lacks a rebinding constructor. (The nested "struct rebind" isn't sufficient.)
> Second, it lacks templated equality/inequality.
> Third, it completely ignores alignment.
> Finally, and most severely, the Standard forbids its existence. Allocators are forbidden from returning memory "inside themselves". This requirement is implied by the Standard's requirements for rebinding and equality. It's permitted to return memory from a separate buffer object on the stack, though."

This patch attempts to address all of those issues.

First, instead of storing the buffer inside the allocator I've change `stack_allocator` to accept the buffer as an argument.

Second, in order to fix rebinding I changed the parameter list from `<class T, size_t NumElements>` to `<class T, size_t NumBytes>`. This allows allocator rebinding
between types that have different sizes. 

Third, I added copy and rebinding constructors and assignment operators.

And finally I fixed the allocation logic to always return properly aligned storage.



Reviewers: mclow.lists, howard.hinnant, STL_MSFT

Subscribers: cfe-commits

Differential Revision: https://reviews.llvm.org/D25154

git-svn-id: https://llvm.org/svn/llvm-project/libcxx/trunk@283631 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Eric Fiselier
2016-10-08 00:56:22 +00:00
parent 9c4dfbe55c
commit 124ed406e5
32 changed files with 142 additions and 135 deletions

View File

@@ -14,11 +14,11 @@
#include <queue>
#include <cassert>
#include "../../../stack_allocator.h"
#include "test_allocator.h"
int main()
{
std::priority_queue<int, std::vector<int, stack_allocator<int, 10> > > q((std::less<int>()));
std::priority_queue<int, std::vector<int, limited_allocator<int, 10> > > q((std::less<int>()));
assert(q.size() == 0);
q.push(1);
q.push(2);

View File

@@ -14,11 +14,11 @@
#include <queue>
#include <cassert>
#include "../../../stack_allocator.h"
#include "test_allocator.h"
int main()
{
std::priority_queue<int, std::vector<int, stack_allocator<int, 10> > > q;
std::priority_queue<int, std::vector<int, limited_allocator<int, 10> > > q;
assert(q.size() == 0);
q.push(1);
q.push(2);

View File

@@ -14,11 +14,11 @@
#include <queue>
#include <cassert>
#include "../../../stack_allocator.h"
#include "test_allocator.h"
int main()
{
std::queue<int, std::vector<int, stack_allocator<int, 10> > > q;
std::queue<int, std::vector<int, limited_allocator<int, 10> > > q;
assert(q.size() == 0);
q.push(1);
q.push(2);

View File

@@ -15,11 +15,11 @@
#include <vector>
#include <cassert>
#include "../../../stack_allocator.h"
#include "test_allocator.h"
int main()
{
std::stack<int, std::vector<int, stack_allocator<int, 10> > > q;
std::stack<int, std::vector<int, limited_allocator<int, 10> > > q;
assert(q.size() == 0);
q.push(1);
q.push(2);

View File

@@ -14,7 +14,7 @@
#include <deque>
#include <cassert>
#include "../../../stack_allocator.h"
#include "test_allocator.h"
#include "../../../NotConstructible.h"
#include "min_allocator.h"
@@ -33,7 +33,7 @@ test()
int main()
{
test<int, std::allocator<int> >();
test<NotConstructible, stack_allocator<NotConstructible, 1> >();
test<NotConstructible, limited_allocator<NotConstructible, 1> >();
#if TEST_STD_VER >= 11
test<int, min_allocator<int> >();
test<NotConstructible, min_allocator<NotConstructible> >();

View File

@@ -14,7 +14,7 @@
#include <deque>
#include <cassert>
#include "../../../stack_allocator.h"
#include "test_allocator.h"
#include "test_iterators.h"
#include "min_allocator.h"
@@ -55,7 +55,7 @@ int main()
test(forward_iterator<const int*>(ab), forward_iterator<const int*>(an));
test(bidirectional_iterator<const int*>(ab), bidirectional_iterator<const int*>(an));
test(random_access_iterator<const int*>(ab), random_access_iterator<const int*>(an));
test<stack_allocator<int, 4096> >(ab, an);
test<limited_allocator<int, 4096> >(ab, an);
#if TEST_STD_VER >= 11
test<min_allocator<int> >(ab, an);
#endif

View File

@@ -14,7 +14,7 @@
#include <deque>
#include <cassert>
#include "../../../stack_allocator.h"
#include "test_allocator.h"
#include "DefaultOnly.h"
#include "min_allocator.h"
@@ -98,7 +98,7 @@ int main()
test<DefaultOnly, std::allocator<DefaultOnly> >(4096);
test<DefaultOnly, std::allocator<DefaultOnly> >(4097);
test1<DefaultOnly, stack_allocator<DefaultOnly, 4096> >(4095);
test1<DefaultOnly, limited_allocator<DefaultOnly, 4096> >(4095);
#if TEST_STD_VER >= 11
test<DefaultOnly, min_allocator<DefaultOnly> >(4095);

View File

@@ -14,7 +14,7 @@
#include <deque>
#include <cassert>
#include "../../../stack_allocator.h"
#include "test_allocator.h"
#include "min_allocator.h"
template <class T, class Allocator>
@@ -44,7 +44,7 @@ int main()
test<int, std::allocator<int> >(4095, 78);
test<int, std::allocator<int> >(4096, 1165);
test<int, std::allocator<int> >(4097, 157);
test<int, stack_allocator<int, 4096> >(4095, 90);
test<int, limited_allocator<int, 4096> >(4095, 90);
#if TEST_STD_VER >= 11
test<int, min_allocator<int> >(4095, 90);
#endif

View File

@@ -20,7 +20,7 @@
#include "test_macros.h"
#include "test_iterators.h"
#include "MoveOnly.h"
#include "../../../stack_allocator.h"
#include "test_allocator.h"
#include "min_allocator.h"
template <class C>
@@ -270,7 +270,7 @@ int main()
testN<std::deque<int> >(rng[i], rng[j], rng[k]);
testNI<std::deque<int> >(1500, 2000, 1000);
#if TEST_STD_VER >= 11
test_move<std::deque<MoveOnly, stack_allocator<MoveOnly, 2000> > >();
test_move<std::deque<MoveOnly, limited_allocator<MoveOnly, 2000> > >();
#endif
}
#if TEST_STD_VER >= 11

View File

@@ -13,7 +13,7 @@
#include <list>
#include <cassert>
#include "../../../stack_allocator.h"
#include "test_allocator.h"
#include "min_allocator.h"
int main()
@@ -29,7 +29,7 @@ int main()
assert(std::distance(l.begin(), l.end()) == 0);
}
{
std::list<int, stack_allocator<int, 4> > l;
std::list<int, limited_allocator<int, 4> > l;
assert(l.size() == 0);
assert(std::distance(l.begin(), l.end()) == 0);
}

View File

@@ -15,7 +15,7 @@
#include <list>
#include <cassert>
#include "test_iterators.h"
#include "../../../stack_allocator.h"
#include "test_allocator.h"
#include "min_allocator.h"
int main()
@@ -43,7 +43,7 @@ int main()
}
{
int a[] = {0, 1, 2, 3};
std::list<int, stack_allocator<int, sizeof(a)/sizeof(a[0])> > l(input_iterator<const int*>(a),
std::list<int, limited_allocator<int, sizeof(a)/sizeof(a[0])> > l(input_iterator<const int*>(a),
input_iterator<const int*>(a + sizeof(a)/sizeof(a[0])));
assert(l.size() == sizeof(a)/sizeof(a[0]));
assert(std::distance(l.begin(), l.end()) == sizeof(a)/sizeof(a[0]));

View File

@@ -14,7 +14,7 @@
#include <list>
#include <cassert>
#include "DefaultOnly.h"
#include "../../../stack_allocator.h"
#include "test_allocator.h"
#include "min_allocator.h"
template <class T, class Allocator>
@@ -48,7 +48,7 @@ int main()
assert(*i == 0);
}
{
std::list<int, stack_allocator<int, 3> > l(3);
std::list<int, limited_allocator<int, 3> > l(3);
assert(l.size() == 3);
assert(std::distance(l.begin(), l.end()) == 3);
std::list<int>::const_iterator i = l.begin();

View File

@@ -14,7 +14,7 @@
#include <list>
#include <cassert>
#include "DefaultOnly.h"
#include "../../../stack_allocator.h"
#include "test_allocator.h"
#include "min_allocator.h"
int main()
@@ -42,7 +42,7 @@ int main()
assert(*i == 2);
}
{
std::list<int, stack_allocator<int, 3> > l(3, 2);
std::list<int, limited_allocator<int, 3> > l(3, 2);
assert(l.size() == 3);
assert(std::distance(l.begin(), l.end()) == 3);
std::list<int>::const_iterator i = l.begin();

View File

@@ -13,7 +13,7 @@
#include <vector>
#include <cassert>
#include "../../../stack_allocator.h"
#include "test_allocator.h"
#include "min_allocator.h"
#include "asan_testing.h"
@@ -37,7 +37,7 @@ int main()
assert(is_contiguous_container_asan_correct(v));
}
{
std::vector<int, stack_allocator<int, 250> > v(100);
std::vector<int, limited_allocator<int, 250> > v(100);
assert(v.capacity() == 100);
v.reserve(50);
assert(v.size() == 100);

View File

@@ -13,7 +13,7 @@
#include <vector>
#include <cassert>
#include "../../../stack_allocator.h"
#include "test_allocator.h"
#include "MoveOnly.h"
#include "min_allocator.h"
#include "asan_testing.h"
@@ -33,7 +33,7 @@ int main()
assert(is_contiguous_container_asan_correct(v));
}
{
std::vector<MoveOnly, stack_allocator<MoveOnly, 300> > v(100);
std::vector<MoveOnly, limited_allocator<MoveOnly, 300> > v(100);
v.resize(50);
assert(v.size() == 50);
assert(v.capacity() == 100);
@@ -56,7 +56,7 @@ int main()
assert(is_contiguous_container_asan_correct(v));
}
{
std::vector<int, stack_allocator<int, 300> > v(100);
std::vector<int, limited_allocator<int, 300> > v(100);
v.resize(50);
assert(v.size() == 50);
assert(v.capacity() == 100);

View File

@@ -13,7 +13,7 @@
#include <vector>
#include <cassert>
#include "../../../stack_allocator.h"
#include "test_allocator.h"
#include "min_allocator.h"
#include "asan_testing.h"
@@ -35,7 +35,7 @@ int main()
assert(v[i] == 1);
}
{
std::vector<int, stack_allocator<int, 300> > v(100);
std::vector<int, limited_allocator<int, 300> > v(100);
v.resize(50, 1);
assert(v.size() == 50);
assert(v.capacity() == 100);

View File

@@ -13,7 +13,7 @@
#include <vector>
#include <cassert>
#include "../../../stack_allocator.h"
#include "test_allocator.h"
#include "min_allocator.h"
#include "asan_testing.h"
@@ -29,7 +29,7 @@ int main()
assert(is_contiguous_container_asan_correct(v));
}
{
std::vector<int, stack_allocator<int, 401> > v(100);
std::vector<int, limited_allocator<int, 401> > v(100);
v.push_back(1);
assert(is_contiguous_container_asan_correct(v));
v.shrink_to_fit();
@@ -39,7 +39,7 @@ int main()
}
#ifndef _LIBCPP_NO_EXCEPTIONS
{
std::vector<int, stack_allocator<int, 400> > v(100);
std::vector<int, limited_allocator<int, 400> > v(100);
v.push_back(1);
assert(is_contiguous_container_asan_correct(v));
v.shrink_to_fit();

View File

@@ -18,7 +18,7 @@
#include "test_macros.h"
#include "test_allocator.h"
#include "../../../NotConstructible.h"
#include "../../../stack_allocator.h"
#include "test_allocator.h"
#include "min_allocator.h"
#include "asan_testing.h"
@@ -71,7 +71,7 @@ int main()
(test_allocator<NotConstructible>(5));
}
{
std::vector<int, stack_allocator<int, 10> > v;
std::vector<int, limited_allocator<int, 10> > v;
assert(v.empty());
}
#if TEST_STD_VER >= 11

View File

@@ -16,7 +16,7 @@
#include "test_macros.h"
#include "test_iterators.h"
#include "../../../stack_allocator.h"
#include "test_allocator.h"
#include "min_allocator.h"
#include "asan_testing.h"
@@ -42,11 +42,11 @@ int main()
test<std::vector<int> >(random_access_iterator<const int*>(a), random_access_iterator<const int*>(an));
test<std::vector<int> >(a, an);
test<std::vector<int, stack_allocator<int, 63> > >(input_iterator<const int*>(a), input_iterator<const int*>(an));
test<std::vector<int, stack_allocator<int, 18> > >(forward_iterator<const int*>(a), forward_iterator<const int*>(an));
test<std::vector<int, stack_allocator<int, 18> > >(bidirectional_iterator<const int*>(a), bidirectional_iterator<const int*>(an));
test<std::vector<int, stack_allocator<int, 18> > >(random_access_iterator<const int*>(a), random_access_iterator<const int*>(an));
test<std::vector<int, stack_allocator<int, 18> > >(a, an);
test<std::vector<int, limited_allocator<int, 63> > >(input_iterator<const int*>(a), input_iterator<const int*>(an));
test<std::vector<int, limited_allocator<int, 18> > >(forward_iterator<const int*>(a), forward_iterator<const int*>(an));
test<std::vector<int, limited_allocator<int, 18> > >(bidirectional_iterator<const int*>(a), bidirectional_iterator<const int*>(an));
test<std::vector<int, limited_allocator<int, 18> > >(random_access_iterator<const int*>(a), random_access_iterator<const int*>(an));
test<std::vector<int, limited_allocator<int, 18> > >(a, an);
#if TEST_STD_VER >= 11
test<std::vector<int, min_allocator<int>> >(input_iterator<const int*>(a), input_iterator<const int*>(an));
test<std::vector<int, min_allocator<int>> >(forward_iterator<const int*>(a), forward_iterator<const int*>(an));

View File

@@ -17,7 +17,7 @@
#include "test_macros.h"
#include "test_iterators.h"
#include "../../../stack_allocator.h"
#include "test_allocator.h"
#include "min_allocator.h"
#include "asan_testing.h"

View File

@@ -15,7 +15,7 @@
#include <cassert>
#include "test_macros.h"
#include "../../../stack_allocator.h"
#include "test_allocator.h"
#include "min_allocator.h"
#include "asan_testing.h"
@@ -34,7 +34,7 @@ test(typename C::size_type n, const typename C::value_type& x)
int main()
{
test<std::vector<int> >(50, 3);
test<std::vector<int, stack_allocator<int, 50> > >(50, 5);
test<std::vector<int, limited_allocator<int, 50> > >(50, 5);
#if TEST_STD_VER >= 11
test<std::vector<int, min_allocator<int>> >(50, 3);
#endif

View File

@@ -17,7 +17,7 @@
#include <vector>
#include <cassert>
#include "../../../stack_allocator.h"
#include "test_allocator.h"
#include "min_allocator.h"
#include "asan_testing.h"
@@ -88,8 +88,8 @@ int main()
assert(is_contiguous_container_asan_correct(c));
}
{
std::vector<A, stack_allocator<A, 7> > c;
std::vector<A, stack_allocator<A, 7> >::iterator i = c.emplace(c.cbegin(), 2, 3.5);
std::vector<A, limited_allocator<A, 7> > c;
std::vector<A, limited_allocator<A, 7> >::iterator i = c.emplace(c.cbegin(), 2, 3.5);
assert(i == c.begin());
assert(c.size() == 1);
assert(c.front().geti() == 2);

View File

@@ -15,7 +15,7 @@
#include <vector>
#include <cassert>
#include "../../../stack_allocator.h"
#include "test_allocator.h"
#include "min_allocator.h"
#include "test_allocator.h"
#include "asan_testing.h"
@@ -72,7 +72,7 @@ int main()
assert(is_contiguous_container_asan_correct(c));
}
{
std::vector<A, stack_allocator<A, 4> > c;
std::vector<A, limited_allocator<A, 4> > c;
A& r1 = c.emplace_back(2, 3.5);
assert(c.size() == 1);
assert(&r1 == &c.back());

View File

@@ -18,7 +18,7 @@
#include <vector>
#include <cassert>
#include "../../../stack_allocator.h"
#include "test_allocator.h"
#include "test_iterators.h"
#include "min_allocator.h"
#include "asan_testing.h"
@@ -96,7 +96,7 @@ int main()
assert(v[j] == 0);
}
{
std::vector<int, stack_allocator<int, 308> > v(100);
std::vector<int, limited_allocator<int, 308> > v(100);
int a[] = {1, 2, 3, 4, 5};
const int N = sizeof(a)/sizeof(a[0]);
std::vector<int>::iterator i = v.insert(v.cbegin() + 10, input_iterator<const int*>(a),
@@ -113,7 +113,7 @@ int main()
assert(v[j] == 0);
}
{
std::vector<int, stack_allocator<int, 300> > v(100);
std::vector<int, limited_allocator<int, 300> > v(100);
int a[] = {1, 2, 3, 4, 5};
const int N = sizeof(a)/sizeof(a[0]);
std::vector<int>::iterator i = v.insert(v.cbegin() + 10, forward_iterator<const int*>(a),

View File

@@ -17,7 +17,7 @@
#include <vector>
#include <cassert>
#include "../../../stack_allocator.h"
#include "test_allocator.h"
#include "MoveOnly.h"
#include "min_allocator.h"
#include "asan_testing.h"
@@ -39,8 +39,8 @@ int main()
assert(v[j] == MoveOnly());
}
{
std::vector<MoveOnly, stack_allocator<MoveOnly, 300> > v(100);
std::vector<MoveOnly, stack_allocator<MoveOnly, 300> >::iterator i = v.insert(v.cbegin() + 10, MoveOnly(3));
std::vector<MoveOnly, limited_allocator<MoveOnly, 300> > v(100);
std::vector<MoveOnly, limited_allocator<MoveOnly, 300> >::iterator i = v.insert(v.cbegin() + 10, MoveOnly(3));
assert(v.size() == 101);
assert(is_contiguous_container_asan_correct(v));
assert(i == v.begin() + 10);

View File

@@ -17,7 +17,7 @@
#include <vector>
#include <cassert>
#include "../../../stack_allocator.h"
#include "test_allocator.h"
#include "min_allocator.h"
#include "asan_testing.h"
@@ -70,8 +70,8 @@ int main()
assert(v[j] == 0);
}
{
std::vector<int, stack_allocator<int, 300> > v(100);
std::vector<int, stack_allocator<int, 300> >::iterator i = v.insert(v.cbegin() + 10, 5, 1);
std::vector<int, limited_allocator<int, 300> > v(100);
std::vector<int, limited_allocator<int, 300> >::iterator i = v.insert(v.cbegin() + 10, 5, 1);
assert(v.size() == 105);
assert(is_contiguous_container_asan_correct(v));
assert(i == v.begin() + 10);

View File

@@ -17,7 +17,7 @@
#include <vector>
#include <cassert>
#include "../../../stack_allocator.h"
#include "test_allocator.h"
#include "min_allocator.h"
#include "asan_testing.h"
@@ -68,8 +68,8 @@ int main()
assert(v[j] == 0);
}
{
std::vector<int, stack_allocator<int, 300> > v(100);
std::vector<int, stack_allocator<int, 300> >::iterator i = v.insert(v.cbegin() + 10, 1);
std::vector<int, limited_allocator<int, 300> > v(100);
std::vector<int, limited_allocator<int, 300> >::iterator i = v.insert(v.cbegin() + 10, 1);
assert(v.size() == 101);
assert(is_contiguous_container_asan_correct(v));
assert(i == v.begin() + 10);

View File

@@ -17,7 +17,7 @@
#include <vector>
#include <cassert>
#include "../../../stack_allocator.h"
#include "test_allocator.h"
#include "min_allocator.h"
#if _LIBCPP_DEBUG >= 1

View File

@@ -13,7 +13,7 @@
#include <vector>
#include <cassert>
#include "../../../stack_allocator.h"
#include "test_allocator.h"
#include "min_allocator.h"
#include "asan_testing.h"
@@ -48,7 +48,7 @@ int main()
assert(c[j] == j);
}
{
std::vector<int, stack_allocator<int, 15> > c;
std::vector<int, limited_allocator<int, 15> > c;
c.push_back(0);
assert(c.size() == 1);
assert(is_contiguous_container_asan_correct(c));

View File

@@ -14,7 +14,7 @@
#include <vector>
#include <cassert>
#include "MoveOnly.h"
#include "../../../stack_allocator.h"
#include "test_allocator.h"
#include "min_allocator.h"
#include "asan_testing.h"
@@ -50,7 +50,7 @@ int main()
assert(c[j] == MoveOnly(j));
}
{
std::vector<MoveOnly, stack_allocator<MoveOnly, 15> > c;
std::vector<MoveOnly, limited_allocator<MoveOnly, 15> > c;
c.push_back(MoveOnly(0));
assert(c.size() == 1);
assert(is_contiguous_container_asan_correct(c));

View File

@@ -1,66 +0,0 @@
//===----------------------------------------------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is dual licensed under the MIT and the University of Illinois Open
// Source Licenses. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef STACK_ALLOCATOR_H
#define STACK_ALLOCATOR_H
#include <cstddef>
#include <new>
template <class T, std::size_t N>
class stack_allocator
{
char buf_[sizeof(T)*N];
char* ptr_;
public:
typedef T value_type;
typedef value_type* pointer;
typedef const value_type* const_pointer;
typedef value_type& reference;
typedef const value_type& const_reference;
typedef std::size_t size_type;
typedef std::ptrdiff_t difference_type;
template <class U> struct rebind {typedef stack_allocator<U, N> other;};
stack_allocator() : ptr_(buf_) {}
private:
stack_allocator(const stack_allocator&);// = delete;
stack_allocator& operator=(const stack_allocator&);// = delete;
public:
pointer allocate(size_type n, const void* = 0)
{
if (n > N - (ptr_ - buf_) / sizeof(value_type)) {
#ifndef _LIBCPP_NO_EXCEPTIONS
throw std::bad_alloc();
#else
std::terminate();
#endif
}
pointer r = (T*)ptr_;
ptr_ += n * sizeof(T);
return r;
}
void deallocate(pointer p, size_type n)
{
if ((char*)(p + n) == ptr_)
ptr_ = (char*)p;
}
size_type max_size() const {return N;}
};
template <class T, std::size_t N>
inline
void
swap(stack_allocator<T, N>& x, stack_allocator<T, N>& y) {}
#endif // STACK_ALLOCATOR_H

View File

@@ -302,5 +302,78 @@ operator!=(const TaggingAllocator<T>&, const TaggingAllocator<U>&)
{ return false; }
#endif
template <std::size_t MaxAllocs>
struct limited_alloc_handle {
std::size_t outstanding_;
void* last_alloc_;
limited_alloc_handle() : outstanding_(0), last_alloc_(nullptr) {}
template <class T>
T *allocate(std::size_t N) {
if (N + outstanding_ > MaxAllocs)
TEST_THROW(std::bad_alloc());
last_alloc_ = ::operator new(N*sizeof(T));
outstanding_ += N;
return static_cast<T*>(last_alloc_);
}
void deallocate(void* ptr, std::size_t N) {
if (ptr == last_alloc_) {
last_alloc_ = nullptr;
assert(outstanding_ >= N);
outstanding_ -= N;
}
::operator delete(ptr);
}
};
template <class T, std::size_t N>
class limited_allocator
{
typedef limited_alloc_handle<N> BuffT;
std::shared_ptr<BuffT> handle_;
public:
typedef T value_type;
typedef value_type* pointer;
typedef const value_type* const_pointer;
typedef value_type& reference;
typedef const value_type& const_reference;
typedef std::size_t size_type;
typedef std::ptrdiff_t difference_type;
template <class U> struct rebind { typedef limited_allocator<U, N> other; };
limited_allocator() : handle_(new BuffT) {}
limited_allocator(limited_allocator const& other) : handle_(other.handle_) {}
template <class U>
explicit limited_allocator(limited_allocator<U, N> const& other)
: handle_(other.handle_) {}
private:
limited_allocator& operator=(const limited_allocator&);// = delete;
public:
pointer allocate(size_type n) { return handle_->template allocate<T>(n); }
void deallocate(pointer p, size_type n) { handle_->deallocate(p, n); }
size_type max_size() const {return N;}
BuffT* getHandle() const { return handle_.get(); }
};
template <class T, class U, std::size_t N>
inline bool operator==(limited_allocator<T, N> const& LHS,
limited_allocator<U, N> const& RHS) {
return LHS.getHandle() == RHS.getHandle();
}
template <class T, class U, std::size_t N>
inline bool operator!=(limited_allocator<T, N> const& LHS,
limited_allocator<U, N> const& RHS) {
return !(LHS == RHS);
}
#endif // TEST_ALLOCATOR_H