2014-01-17 00:58:45 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
//
|
2019-01-19 18:56:40 +08:00
|
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
2014-01-17 00:58:45 +08:00
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2010-05-12 03:42:16 +08:00
|
|
|
#ifndef TEST_ALLOCATOR_H
|
|
|
|
#define TEST_ALLOCATOR_H
|
|
|
|
|
|
|
|
#include <type_traits>
|
|
|
|
#include <new>
|
2016-10-08 08:57:56 +08:00
|
|
|
#include <memory>
|
2016-12-11 11:41:12 +08:00
|
|
|
#include <utility>
|
2016-10-08 08:57:56 +08:00
|
|
|
#include <cstddef>
|
|
|
|
#include <cstdlib>
|
2010-05-12 03:42:16 +08:00
|
|
|
#include <climits>
|
2013-12-03 08:18:10 +08:00
|
|
|
#include <cassert>
|
2010-05-12 03:42:16 +08:00
|
|
|
|
2015-06-04 03:56:43 +08:00
|
|
|
#include "test_macros.h"
|
|
|
|
|
2016-11-23 09:18:56 +08:00
|
|
|
template <class Alloc>
|
2016-11-23 17:16:12 +08:00
|
|
|
inline typename std::allocator_traits<Alloc>::size_type
|
|
|
|
alloc_max_size(Alloc const &a) {
|
2016-11-23 09:18:56 +08:00
|
|
|
typedef std::allocator_traits<Alloc> AT;
|
|
|
|
return AT::max_size(a);
|
|
|
|
}
|
|
|
|
|
2010-05-12 03:42:16 +08:00
|
|
|
class test_alloc_base
|
|
|
|
{
|
|
|
|
protected:
|
2013-12-03 08:18:10 +08:00
|
|
|
static int time_to_throw;
|
2010-05-12 03:42:16 +08:00
|
|
|
public:
|
|
|
|
static int throw_after;
|
2013-12-03 08:18:10 +08:00
|
|
|
static int count;
|
|
|
|
static int alloc_count;
|
2018-06-06 06:32:52 +08:00
|
|
|
static int copied;
|
|
|
|
static int moved;
|
|
|
|
static int converted;
|
|
|
|
|
|
|
|
const static int destructed_value = -1;
|
|
|
|
const static int default_value = 0;
|
|
|
|
const static int moved_value = INT_MAX;
|
|
|
|
|
|
|
|
static void clear() {
|
|
|
|
assert(count == 0 && "clearing leaking allocator data?");
|
|
|
|
count = 0;
|
|
|
|
time_to_throw = 0;
|
|
|
|
alloc_count = 0;
|
|
|
|
throw_after = INT_MAX;
|
|
|
|
clear_ctor_counters();
|
|
|
|
}
|
|
|
|
|
|
|
|
static void clear_ctor_counters() {
|
|
|
|
copied = 0;
|
|
|
|
moved = 0;
|
|
|
|
converted = 0;
|
|
|
|
}
|
2010-05-12 03:42:16 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
int test_alloc_base::count = 0;
|
2013-12-03 08:18:10 +08:00
|
|
|
int test_alloc_base::time_to_throw = 0;
|
|
|
|
int test_alloc_base::alloc_count = 0;
|
2010-05-12 03:42:16 +08:00
|
|
|
int test_alloc_base::throw_after = INT_MAX;
|
2018-06-06 06:32:52 +08:00
|
|
|
int test_alloc_base::copied = 0;
|
|
|
|
int test_alloc_base::moved = 0;
|
|
|
|
int test_alloc_base::converted = 0;
|
2010-05-12 03:42:16 +08:00
|
|
|
|
|
|
|
template <class T>
|
|
|
|
class test_allocator
|
|
|
|
: public test_alloc_base
|
|
|
|
{
|
2016-12-11 11:41:12 +08:00
|
|
|
int data_; // participates in equality
|
|
|
|
int id_; // unique identifier, doesn't participate in equality
|
2010-05-12 03:42:16 +08:00
|
|
|
template <class U> friend class test_allocator;
|
|
|
|
public:
|
|
|
|
|
|
|
|
typedef unsigned size_type;
|
|
|
|
typedef int difference_type;
|
|
|
|
typedef T value_type;
|
|
|
|
typedef value_type* pointer;
|
|
|
|
typedef const value_type* const_pointer;
|
|
|
|
typedef typename std::add_lvalue_reference<value_type>::type reference;
|
|
|
|
typedef typename std::add_lvalue_reference<const value_type>::type const_reference;
|
|
|
|
|
|
|
|
template <class U> struct rebind {typedef test_allocator<U> other;};
|
|
|
|
|
2016-12-11 11:41:12 +08:00
|
|
|
test_allocator() TEST_NOEXCEPT : data_(0), id_(0) {++count;}
|
|
|
|
explicit test_allocator(int i, int id = 0) TEST_NOEXCEPT : data_(i), id_(id)
|
|
|
|
{++count;}
|
2018-06-06 06:32:52 +08:00
|
|
|
test_allocator(const test_allocator& a) TEST_NOEXCEPT : data_(a.data_),
|
|
|
|
id_(a.id_) {
|
|
|
|
++count;
|
|
|
|
++copied;
|
|
|
|
assert(a.data_ != destructed_value && a.id_ != destructed_value &&
|
|
|
|
"copying from destroyed allocator");
|
|
|
|
}
|
|
|
|
#if TEST_STD_VER >= 11
|
|
|
|
test_allocator(test_allocator&& a) TEST_NOEXCEPT : data_(a.data_),
|
|
|
|
id_(a.id_) {
|
|
|
|
++count;
|
|
|
|
++moved;
|
|
|
|
assert(a.data_ != destructed_value && a.id_ != destructed_value &&
|
|
|
|
"moving from destroyed allocator");
|
|
|
|
a.data_ = moved_value;
|
|
|
|
a.id_ = moved_value;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
template <class U>
|
|
|
|
test_allocator(const test_allocator<U>& a) TEST_NOEXCEPT : data_(a.data_),
|
|
|
|
id_(a.id_) {
|
|
|
|
++count;
|
|
|
|
++converted;
|
|
|
|
}
|
2016-12-11 11:41:12 +08:00
|
|
|
~test_allocator() TEST_NOEXCEPT {
|
|
|
|
assert(data_ >= 0); assert(id_ >= 0);
|
2018-06-06 06:32:52 +08:00
|
|
|
--count;
|
|
|
|
data_ = destructed_value;
|
|
|
|
id_ = destructed_value;
|
2016-12-11 11:41:12 +08:00
|
|
|
}
|
2010-05-12 03:42:16 +08:00
|
|
|
pointer address(reference x) const {return &x;}
|
|
|
|
const_pointer address(const_reference x) const {return &x;}
|
|
|
|
pointer allocate(size_type n, const void* = 0)
|
|
|
|
{
|
2013-12-03 08:18:10 +08:00
|
|
|
assert(data_ >= 0);
|
|
|
|
if (time_to_throw >= throw_after) {
|
2016-09-25 11:34:28 +08:00
|
|
|
#ifndef TEST_HAS_NO_EXCEPTIONS
|
2010-05-12 03:42:16 +08:00
|
|
|
throw std::bad_alloc();
|
2013-03-24 01:27:16 +08:00
|
|
|
#else
|
|
|
|
std::terminate();
|
|
|
|
#endif
|
|
|
|
}
|
2013-12-03 08:18:10 +08:00
|
|
|
++time_to_throw;
|
|
|
|
++alloc_count;
|
2015-06-15 07:30:09 +08:00
|
|
|
return (pointer)::operator new(n * sizeof(T));
|
2010-05-12 03:42:16 +08:00
|
|
|
}
|
2016-04-28 11:17:56 +08:00
|
|
|
void deallocate(pointer p, size_type)
|
2015-06-15 07:30:09 +08:00
|
|
|
{assert(data_ >= 0); --alloc_count; ::operator delete((void*)p);}
|
2016-12-11 10:47:36 +08:00
|
|
|
size_type max_size() const TEST_NOEXCEPT
|
2010-05-12 03:42:16 +08:00
|
|
|
{return UINT_MAX / sizeof(T);}
|
[libcxx] [test] In test/support/test_allocator.h, fix construct() to avoid moving immovable types.
Summary:
In test/support/test_allocator.h, fix construct() to avoid moving immovable types.
This improves the allocator's conformance, and fixes compiler errors with MSVC's STL. The scenario is when the allocator is asked to construct an object of type X that's immovable (deleted copy/move ctors), but implicitly constructible from an argument type A. When perfectly forwarded, X can be (explicitly) constructed from A, and everything is fine. That's std::allocator's behavior, and the Standard's default when a user allocator's construct() doesn't exist. The previous implementation of construct() here mishandled this scenario. Passing A to this construct() would implicitly construct an X temporary, bound to (non-templated) T&&. Then construct() would attempt to move-construct X from that X temporary, but X is immovable, boom.
Reviewers: mclow.lists, EricWF
Subscribers: cfe-commits
Differential Revision: http://reviews.llvm.org/D21094
llvm-svn: 272747
2016-06-15 09:53:32 +08:00
|
|
|
#if TEST_STD_VER < 11
|
2010-05-12 03:42:16 +08:00
|
|
|
void construct(pointer p, const T& val)
|
[libcxx] [test] In test/support/test_allocator.h, fix construct() to avoid moving immovable types.
Summary:
In test/support/test_allocator.h, fix construct() to avoid moving immovable types.
This improves the allocator's conformance, and fixes compiler errors with MSVC's STL. The scenario is when the allocator is asked to construct an object of type X that's immovable (deleted copy/move ctors), but implicitly constructible from an argument type A. When perfectly forwarded, X can be (explicitly) constructed from A, and everything is fine. That's std::allocator's behavior, and the Standard's default when a user allocator's construct() doesn't exist. The previous implementation of construct() here mishandled this scenario. Passing A to this construct() would implicitly construct an X temporary, bound to (non-templated) T&&. Then construct() would attempt to move-construct X from that X temporary, but X is immovable, boom.
Reviewers: mclow.lists, EricWF
Subscribers: cfe-commits
Differential Revision: http://reviews.llvm.org/D21094
llvm-svn: 272747
2016-06-15 09:53:32 +08:00
|
|
|
{::new(static_cast<void*>(p)) T(val);}
|
|
|
|
#else
|
|
|
|
template <class U> void construct(pointer p, U&& val)
|
|
|
|
{::new(static_cast<void*>(p)) T(std::forward<U>(val));}
|
|
|
|
#endif
|
2016-06-22 09:02:08 +08:00
|
|
|
void destroy(pointer p)
|
2016-10-08 02:51:33 +08:00
|
|
|
{p->~T();}
|
2010-05-12 03:42:16 +08:00
|
|
|
friend bool operator==(const test_allocator& x, const test_allocator& y)
|
|
|
|
{return x.data_ == y.data_;}
|
|
|
|
friend bool operator!=(const test_allocator& x, const test_allocator& y)
|
|
|
|
{return !(x == y);}
|
2016-12-11 11:41:12 +08:00
|
|
|
|
|
|
|
int get_data() const { return data_; }
|
|
|
|
int get_id() const { return id_; }
|
2010-05-12 03:42:16 +08:00
|
|
|
};
|
|
|
|
|
2014-04-19 01:23:36 +08:00
|
|
|
template <class T>
|
|
|
|
class non_default_test_allocator
|
|
|
|
: public test_alloc_base
|
|
|
|
{
|
|
|
|
int data_;
|
|
|
|
|
|
|
|
template <class U> friend class non_default_test_allocator;
|
|
|
|
public:
|
|
|
|
|
|
|
|
typedef unsigned size_type;
|
|
|
|
typedef int difference_type;
|
|
|
|
typedef T value_type;
|
|
|
|
typedef value_type* pointer;
|
|
|
|
typedef const value_type* const_pointer;
|
|
|
|
typedef typename std::add_lvalue_reference<value_type>::type reference;
|
|
|
|
typedef typename std::add_lvalue_reference<const value_type>::type const_reference;
|
|
|
|
|
|
|
|
template <class U> struct rebind {typedef non_default_test_allocator<U> other;};
|
|
|
|
|
2016-12-11 10:47:36 +08:00
|
|
|
// non_default_test_allocator() TEST_NOEXCEPT : data_(0) {++count;}
|
|
|
|
explicit non_default_test_allocator(int i) TEST_NOEXCEPT : data_(i) {++count;}
|
|
|
|
non_default_test_allocator(const non_default_test_allocator& a) TEST_NOEXCEPT
|
2014-04-19 01:23:36 +08:00
|
|
|
: data_(a.data_) {++count;}
|
2016-12-11 10:47:36 +08:00
|
|
|
template <class U> non_default_test_allocator(const non_default_test_allocator<U>& a) TEST_NOEXCEPT
|
2014-04-19 01:23:36 +08:00
|
|
|
: data_(a.data_) {++count;}
|
2016-12-11 10:47:36 +08:00
|
|
|
~non_default_test_allocator() TEST_NOEXCEPT {assert(data_ >= 0); --count; data_ = -1;}
|
2014-04-19 01:23:36 +08:00
|
|
|
pointer address(reference x) const {return &x;}
|
|
|
|
const_pointer address(const_reference x) const {return &x;}
|
|
|
|
pointer allocate(size_type n, const void* = 0)
|
|
|
|
{
|
|
|
|
assert(data_ >= 0);
|
|
|
|
if (time_to_throw >= throw_after) {
|
2016-09-25 11:34:28 +08:00
|
|
|
#ifndef TEST_HAS_NO_EXCEPTIONS
|
2014-04-19 01:23:36 +08:00
|
|
|
throw std::bad_alloc();
|
|
|
|
#else
|
|
|
|
std::terminate();
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
++time_to_throw;
|
|
|
|
++alloc_count;
|
2015-06-15 07:30:09 +08:00
|
|
|
return (pointer)::operator new (n * sizeof(T));
|
2014-04-19 01:23:36 +08:00
|
|
|
}
|
2016-04-28 11:17:56 +08:00
|
|
|
void deallocate(pointer p, size_type)
|
2015-06-15 07:30:09 +08:00
|
|
|
{assert(data_ >= 0); --alloc_count; ::operator delete((void*)p); }
|
2016-12-11 10:47:36 +08:00
|
|
|
size_type max_size() const TEST_NOEXCEPT
|
2014-04-19 01:23:36 +08:00
|
|
|
{return UINT_MAX / sizeof(T);}
|
[libcxx] [test] In test/support/test_allocator.h, fix construct() to avoid moving immovable types.
Summary:
In test/support/test_allocator.h, fix construct() to avoid moving immovable types.
This improves the allocator's conformance, and fixes compiler errors with MSVC's STL. The scenario is when the allocator is asked to construct an object of type X that's immovable (deleted copy/move ctors), but implicitly constructible from an argument type A. When perfectly forwarded, X can be (explicitly) constructed from A, and everything is fine. That's std::allocator's behavior, and the Standard's default when a user allocator's construct() doesn't exist. The previous implementation of construct() here mishandled this scenario. Passing A to this construct() would implicitly construct an X temporary, bound to (non-templated) T&&. Then construct() would attempt to move-construct X from that X temporary, but X is immovable, boom.
Reviewers: mclow.lists, EricWF
Subscribers: cfe-commits
Differential Revision: http://reviews.llvm.org/D21094
llvm-svn: 272747
2016-06-15 09:53:32 +08:00
|
|
|
#if TEST_STD_VER < 11
|
2014-04-19 01:23:36 +08:00
|
|
|
void construct(pointer p, const T& val)
|
[libcxx] [test] In test/support/test_allocator.h, fix construct() to avoid moving immovable types.
Summary:
In test/support/test_allocator.h, fix construct() to avoid moving immovable types.
This improves the allocator's conformance, and fixes compiler errors with MSVC's STL. The scenario is when the allocator is asked to construct an object of type X that's immovable (deleted copy/move ctors), but implicitly constructible from an argument type A. When perfectly forwarded, X can be (explicitly) constructed from A, and everything is fine. That's std::allocator's behavior, and the Standard's default when a user allocator's construct() doesn't exist. The previous implementation of construct() here mishandled this scenario. Passing A to this construct() would implicitly construct an X temporary, bound to (non-templated) T&&. Then construct() would attempt to move-construct X from that X temporary, but X is immovable, boom.
Reviewers: mclow.lists, EricWF
Subscribers: cfe-commits
Differential Revision: http://reviews.llvm.org/D21094
llvm-svn: 272747
2016-06-15 09:53:32 +08:00
|
|
|
{::new(static_cast<void*>(p)) T(val);}
|
|
|
|
#else
|
|
|
|
template <class U> void construct(pointer p, U&& val)
|
|
|
|
{::new(static_cast<void*>(p)) T(std::forward<U>(val));}
|
|
|
|
#endif
|
2014-04-19 01:23:36 +08:00
|
|
|
void destroy(pointer p) {p->~T();}
|
|
|
|
|
|
|
|
friend bool operator==(const non_default_test_allocator& x, const non_default_test_allocator& y)
|
|
|
|
{return x.data_ == y.data_;}
|
|
|
|
friend bool operator!=(const non_default_test_allocator& x, const non_default_test_allocator& y)
|
|
|
|
{return !(x == y);}
|
|
|
|
};
|
|
|
|
|
2013-12-03 08:18:10 +08:00
|
|
|
template <>
|
|
|
|
class test_allocator<void>
|
|
|
|
: public test_alloc_base
|
|
|
|
{
|
|
|
|
int data_;
|
2016-12-11 11:41:12 +08:00
|
|
|
int id_;
|
2013-12-03 08:18:10 +08:00
|
|
|
|
|
|
|
template <class U> friend class test_allocator;
|
|
|
|
public:
|
|
|
|
|
|
|
|
typedef unsigned size_type;
|
|
|
|
typedef int difference_type;
|
|
|
|
typedef void value_type;
|
|
|
|
typedef value_type* pointer;
|
|
|
|
typedef const value_type* const_pointer;
|
|
|
|
|
|
|
|
template <class U> struct rebind {typedef test_allocator<U> other;};
|
|
|
|
|
2016-12-11 11:41:12 +08:00
|
|
|
test_allocator() TEST_NOEXCEPT : data_(0), id_(0) {}
|
|
|
|
explicit test_allocator(int i, int id = 0) TEST_NOEXCEPT : data_(i), id_(id) {}
|
2016-12-11 10:47:36 +08:00
|
|
|
test_allocator(const test_allocator& a) TEST_NOEXCEPT
|
2016-12-11 11:41:12 +08:00
|
|
|
: data_(a.data_), id_(a.id_) {}
|
2016-12-11 10:47:36 +08:00
|
|
|
template <class U> test_allocator(const test_allocator<U>& a) TEST_NOEXCEPT
|
2016-12-11 11:41:12 +08:00
|
|
|
: data_(a.data_), id_(a.id_) {}
|
|
|
|
~test_allocator() TEST_NOEXCEPT {data_ = -1; id_ = -1; }
|
|
|
|
|
|
|
|
int get_id() const { return id_; }
|
|
|
|
int get_data() const { return data_; }
|
2013-12-03 08:18:10 +08:00
|
|
|
|
|
|
|
friend bool operator==(const test_allocator& x, const test_allocator& y)
|
|
|
|
{return x.data_ == y.data_;}
|
|
|
|
friend bool operator!=(const test_allocator& x, const test_allocator& y)
|
|
|
|
{return !(x == y);}
|
|
|
|
};
|
|
|
|
|
2010-05-12 03:42:16 +08:00
|
|
|
template <class T>
|
|
|
|
class other_allocator
|
|
|
|
{
|
|
|
|
int data_;
|
|
|
|
|
|
|
|
template <class U> friend class other_allocator;
|
|
|
|
|
|
|
|
public:
|
|
|
|
typedef T value_type;
|
|
|
|
|
|
|
|
other_allocator() : data_(-1) {}
|
|
|
|
explicit other_allocator(int i) : data_(i) {}
|
|
|
|
template <class U> other_allocator(const other_allocator<U>& a)
|
|
|
|
: data_(a.data_) {}
|
|
|
|
T* allocate(std::size_t n)
|
2015-06-15 07:30:09 +08:00
|
|
|
{return (T*)::operator new(n * sizeof(T));}
|
2016-04-28 11:17:56 +08:00
|
|
|
void deallocate(T* p, std::size_t)
|
2015-06-15 07:30:09 +08:00
|
|
|
{::operator delete((void*)p);}
|
2010-05-12 03:42:16 +08:00
|
|
|
|
|
|
|
other_allocator select_on_container_copy_construction() const
|
|
|
|
{return other_allocator(-2);}
|
|
|
|
|
|
|
|
friend bool operator==(const other_allocator& x, const other_allocator& y)
|
|
|
|
{return x.data_ == y.data_;}
|
|
|
|
friend bool operator!=(const other_allocator& x, const other_allocator& y)
|
|
|
|
{return !(x == y);}
|
|
|
|
|
|
|
|
typedef std::true_type propagate_on_container_copy_assignment;
|
|
|
|
typedef std::true_type propagate_on_container_move_assignment;
|
|
|
|
typedef std::true_type propagate_on_container_swap;
|
|
|
|
|
2016-09-25 11:34:28 +08:00
|
|
|
#if TEST_STD_VER < 11
|
2010-05-12 03:42:16 +08:00
|
|
|
std::size_t max_size() const
|
|
|
|
{return UINT_MAX / sizeof(T);}
|
2016-09-25 11:34:28 +08:00
|
|
|
#endif
|
2010-05-12 03:42:16 +08:00
|
|
|
|
|
|
|
};
|
|
|
|
|
2016-07-12 05:38:08 +08:00
|
|
|
#if TEST_STD_VER >= 11
|
|
|
|
|
|
|
|
struct Ctor_Tag {};
|
|
|
|
|
|
|
|
template <typename T> class TaggingAllocator;
|
|
|
|
|
|
|
|
struct Tag_X {
|
|
|
|
// All constructors must be passed the Tag type.
|
|
|
|
|
|
|
|
// DefaultInsertable into vector<X, TaggingAllocator<X>>,
|
|
|
|
Tag_X(Ctor_Tag) {}
|
|
|
|
// CopyInsertable into vector<X, TaggingAllocator<X>>,
|
|
|
|
Tag_X(Ctor_Tag, const Tag_X&) {}
|
|
|
|
// MoveInsertable into vector<X, TaggingAllocator<X>>, and
|
|
|
|
Tag_X(Ctor_Tag, Tag_X&&) {}
|
|
|
|
|
|
|
|
// EmplaceConstructible into vector<X, TaggingAllocator<X>> from args.
|
|
|
|
template<typename... Args>
|
|
|
|
Tag_X(Ctor_Tag, Args&&...) { }
|
|
|
|
|
|
|
|
// not DefaultConstructible, CopyConstructible or MoveConstructible.
|
|
|
|
Tag_X() = delete;
|
|
|
|
Tag_X(const Tag_X&) = delete;
|
|
|
|
Tag_X(Tag_X&&) = delete;
|
|
|
|
|
|
|
|
// CopyAssignable.
|
|
|
|
Tag_X& operator=(const Tag_X&) { return *this; }
|
|
|
|
|
|
|
|
// MoveAssignable.
|
|
|
|
Tag_X& operator=(Tag_X&&) { return *this; }
|
|
|
|
|
|
|
|
private:
|
|
|
|
// Not Destructible.
|
|
|
|
~Tag_X() { }
|
|
|
|
|
|
|
|
// Erasable from vector<X, TaggingAllocator<X>>.
|
|
|
|
friend class TaggingAllocator<Tag_X>;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
class TaggingAllocator {
|
|
|
|
public:
|
|
|
|
using value_type = T;
|
|
|
|
TaggingAllocator() = default;
|
|
|
|
|
|
|
|
template<typename U>
|
|
|
|
TaggingAllocator(const TaggingAllocator<U>&) { }
|
|
|
|
|
|
|
|
T* allocate(std::size_t n) { return std::allocator<T>{}.allocate(n); }
|
|
|
|
|
|
|
|
void deallocate(T* p, std::size_t n) { std::allocator<T>{}.deallocate(p, n); }
|
|
|
|
|
|
|
|
template<typename... Args>
|
|
|
|
void construct(Tag_X* p, Args&&... args)
|
|
|
|
{ ::new((void*)p) Tag_X(Ctor_Tag{}, std::forward<Args>(args)...); }
|
|
|
|
|
|
|
|
template<typename U, typename... Args>
|
|
|
|
void construct(U* p, Args&&... args)
|
|
|
|
{ ::new((void*)p) U(std::forward<Args>(args)...); }
|
|
|
|
|
|
|
|
template<typename U, typename... Args>
|
|
|
|
void destroy(U* p)
|
2016-10-08 02:51:33 +08:00
|
|
|
{ p->~U(); }
|
2016-07-12 05:38:08 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
template<typename T, typename U>
|
|
|
|
bool
|
|
|
|
operator==(const TaggingAllocator<T>&, const TaggingAllocator<U>&)
|
|
|
|
{ return true; }
|
|
|
|
|
|
|
|
template<typename T, typename U>
|
|
|
|
bool
|
|
|
|
operator!=(const TaggingAllocator<T>&, const TaggingAllocator<U>&)
|
|
|
|
{ return false; }
|
|
|
|
#endif
|
|
|
|
|
[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
llvm-svn: 283631
2016-10-08 08:56:22 +08:00
|
|
|
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
|
|
|
|
{
|
2016-10-12 19:35:37 +08:00
|
|
|
template <class U, std::size_t UN> friend class limited_allocator;
|
[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
llvm-svn: 283631
2016-10-08 08:56:22 +08:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2016-07-12 05:38:08 +08:00
|
|
|
|
2010-08-22 08:15:28 +08:00
|
|
|
#endif // TEST_ALLOCATOR_H
|