Skip to content

Commit

Permalink
[libcxx] Use custom allocator's construct in C++03 when available.
Browse files Browse the repository at this point in the history
Makes libc++ behavior consistent between C++03 and C++11.

Can use `decltype` in C++03 because `include/__config` defines a macro when
`decltype` is not available.

Reviewers: mclow.lists, EricWF, erik.pilkington, ldionne

Reviewed By: ldionne

Subscribers: dexonsmith, cfe-commits, howard.hinnant, ldionne, christof, jkorous, Quuxplusone

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

llvm-svn: 349676
  • Loading branch information
vsapsai committed Dec 19, 2018
1 parent 660860e commit e7652f5
Show file tree
Hide file tree
Showing 5 changed files with 193 additions and 22 deletions.
48 changes: 27 additions & 21 deletions libcxx/include/memory
Expand Up @@ -1460,29 +1460,21 @@ struct __has_select_on_container_copy_construction

#else // _LIBCPP_CXX03_LANG

#ifndef _LIBCPP_HAS_NO_VARIADICS

template <class _Alloc, class _Pointer, class ..._Args>
struct __has_construct
: false_type
{
};
template <class _Alloc, class _Pointer, class _Tp, class = void>
struct __has_construct : std::false_type {};

#else // _LIBCPP_HAS_NO_VARIADICS
template <class _Alloc, class _Pointer, class _Tp>
struct __has_construct<_Alloc, _Pointer, _Tp, typename __void_t<
decltype(_VSTD::declval<_Alloc>().construct(_VSTD::declval<_Pointer>(), _VSTD::declval<_Tp>()))
>::type> : std::true_type {};

template <class _Alloc, class _Pointer, class _Args>
struct __has_construct
: false_type
{
};

#endif // _LIBCPP_HAS_NO_VARIADICS
template <class _Alloc, class _Pointer, class = void>
struct __has_destroy : false_type {};

template <class _Alloc, class _Pointer>
struct __has_destroy
: false_type
{
};
struct __has_destroy<_Alloc, _Pointer, typename __void_t<
decltype(_VSTD::declval<_Alloc>().destroy(_VSTD::declval<_Pointer>()))
>::type> : std::true_type {};

template <class _Alloc>
struct __has_max_size
Expand Down Expand Up @@ -1571,9 +1563,10 @@ struct _LIBCPP_TEMPLATE_VIS allocator_traits
}
template <class _Tp, class _A0>
_LIBCPP_INLINE_VISIBILITY
static void construct(allocator_type&, _Tp* __p, const _A0& __a0)
static void construct(allocator_type& __a, _Tp* __p, const _A0& __a0)
{
::new ((void*)__p) _Tp(__a0);
__construct(__has_construct<allocator_type, _Tp*, const _A0&>(),
__a, __p, __a0);
}
template <class _Tp, class _A0, class _A1>
_LIBCPP_INLINE_VISIBILITY
Expand Down Expand Up @@ -1721,6 +1714,19 @@ private:
{
::new ((void*)__p) _Tp(_VSTD::forward<_Args>(__args)...);
}
#else // _LIBCPP_HAS_NO_VARIADICS
template <class _Tp, class _A0>
_LIBCPP_INLINE_VISIBILITY
static void __construct(true_type, allocator_type& __a, _Tp* __p,
const _A0& __a0)
{__a.construct(__p, __a0);}
template <class _Tp, class _A0>
_LIBCPP_INLINE_VISIBILITY
static void __construct(false_type, allocator_type&, _Tp* __p,
const _A0& __a0)
{
::new ((void*)__p) _Tp(__a0);
}
#endif // _LIBCPP_HAS_NO_VARIADICS

template <class _Tp>
Expand Down
@@ -0,0 +1,54 @@
//===----------------------------------------------------------------------===//
//
// 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.
//
//===----------------------------------------------------------------------===//

// <vector>

// template <class InputIter> vector(InputIter first, InputIter last);

#include <vector>
#include <cassert>

#include "min_allocator.h"

void test_ctor_under_alloc() {
int arr1[] = {42};
int arr2[] = {1, 101, 42};
{
typedef std::vector<int, cpp03_allocator<int> > C;
typedef C::allocator_type Alloc;
{
Alloc::construct_called = false;
C v(arr1, arr1 + 1);
assert(Alloc::construct_called);
}
{
Alloc::construct_called = false;
C v(arr2, arr2 + 3);
assert(Alloc::construct_called);
}
}
{
typedef std::vector<int, cpp03_overload_allocator<int> > C;
typedef C::allocator_type Alloc;
{
Alloc::construct_called = false;
C v(arr1, arr1 + 1);
assert(Alloc::construct_called);
}
{
Alloc::construct_called = false;
C v(arr2, arr2 + 3);
assert(Alloc::construct_called);
}
}
}

int main() {
test_ctor_under_alloc();
}
@@ -0,0 +1,57 @@
//===----------------------------------------------------------------------===//
//
// 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.
//
//===----------------------------------------------------------------------===//

// <vector>

// template <class InputIter> vector(InputIter first, InputIter last,
// const allocator_type& a);

#include <vector>
#include <cassert>

#include "min_allocator.h"

void test_ctor_under_alloc() {
int arr1[] = {42};
int arr2[] = {1, 101, 42};
{
typedef std::vector<int, cpp03_allocator<int> > C;
typedef C::allocator_type Alloc;
Alloc a;
{
Alloc::construct_called = false;
C v(arr1, arr1 + 1, a);
assert(Alloc::construct_called);
}
{
Alloc::construct_called = false;
C v(arr2, arr2 + 3, a);
assert(Alloc::construct_called);
}
}
{
typedef std::vector<int, cpp03_overload_allocator<int> > C;
typedef C::allocator_type Alloc;
Alloc a;
{
Alloc::construct_called = false;
C v(arr1, arr1 + 1, a);
assert(Alloc::construct_called);
}
{
Alloc::construct_called = false;
C v(arr2, arr2 + 3, a);
assert(Alloc::construct_called);
}
}
}

int main() {
test_ctor_under_alloc();
}
Expand Up @@ -73,7 +73,7 @@ int main()
std::aligned_storage<sizeof(VT)>::type store;
std::allocator_traits<Alloc>::destroy(a, (VT*)&store);
}
#if TEST_STD_VER >= 11
#if defined(_LIBCPP_VERSION) || TEST_STD_VER >= 11
{
A0::count = 0;
b_destroy = 0;
Expand Down
54 changes: 54 additions & 0 deletions libcxx/test/support/min_allocator.h
Expand Up @@ -14,6 +14,7 @@
#include <cstdlib>
#include <cstddef>
#include <cassert>
#include <climits>

#include "test_macros.h"

Expand Down Expand Up @@ -131,6 +132,59 @@ class malloc_allocator : public malloc_allocator_base
friend bool operator!=(malloc_allocator x, malloc_allocator y) {return !(x == y);}
};

template <class T>
struct cpp03_allocator : bare_allocator<T>
{
typedef T value_type;
typedef value_type* pointer;

static bool construct_called;

// Returned value is not used but it's not prohibited.
pointer construct(pointer p, const value_type& val)
{
::new(p) value_type(val);
construct_called = true;
return p;
}

std::size_t max_size() const
{
return UINT_MAX / sizeof(T);
}
};
template <class T> bool cpp03_allocator<T>::construct_called = false;

template <class T>
struct cpp03_overload_allocator : bare_allocator<T>
{
typedef T value_type;
typedef value_type* pointer;

static bool construct_called;

void construct(pointer p, const value_type& val)
{
construct(p, val, std::is_class<T>());
}
void construct(pointer p, const value_type& val, std::true_type)
{
::new(p) value_type(val);
construct_called = true;
}
void construct(pointer p, const value_type& val, std::false_type)
{
::new(p) value_type(val);
construct_called = true;
}

std::size_t max_size() const
{
return UINT_MAX / sizeof(T);
}
};
template <class T> bool cpp03_overload_allocator<T>::construct_called = false;


#if TEST_STD_VER >= 11

Expand Down

0 comments on commit e7652f5

Please sign in to comment.