From b11df184ad642c8601d4073fff54974fd76b70c7 Mon Sep 17 00:00:00 2001 From: Eric Fiselier Date: Sun, 14 Jun 2015 23:30:09 +0000 Subject: [PATCH] Fix std::function allocator constructors in C++03. The C++03 version of function tried to default construct the allocator in the uses allocator constructors when no allocation was performed. These constructors would fail to compile when used with allocators that had no default constructor. llvm-svn: 239708 --- libcxx/include/__functional_03 | 32 +++-- .../func.wrap.func.con/alloc_F.pass.cpp | 128 ++++++++++--------- .../func.wrap.func.con/alloc_function.pass.cpp | 140 ++++++++++++--------- .../func.wrap/func.wrap.func/function_types.h | 57 +++++++++ libcxx/test/support/test_allocator.h | 12 +- 5 files changed, 232 insertions(+), 137 deletions(-) create mode 100644 libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.func/function_types.h diff --git a/libcxx/include/__functional_03 b/libcxx/include/__functional_03 index f048ea3..9935483 100644 --- a/libcxx/include/__functional_03 +++ b/libcxx/include/__functional_03 @@ -333,7 +333,8 @@ template __base<_Rp()>* __func<_Fp, _Alloc, _Rp()>::__clone() const { - typedef typename _Alloc::template rebind<__func>::other _Ap; + typedef allocator_traits<_Alloc> __alloc_traits; + typedef typename __rebind_alloc_helper<__alloc_traits, __func>::type _Ap; _Ap __a(__f_.second()); typedef __allocator_destructor<_Ap> _Dp; unique_ptr<__func, _Dp> __hold(__a.allocate(1), _Dp(__a, 1)); @@ -359,7 +360,8 @@ template void __func<_Fp, _Alloc, _Rp()>::destroy_deallocate() { - typedef typename _Alloc::template rebind<__func>::other _Ap; + typedef allocator_traits<_Alloc> __alloc_traits; + typedef typename __rebind_alloc_helper<__alloc_traits, __func>::type _Ap; _Ap __a(__f_.second()); __f_.~__compressed_pair<_Fp, _Alloc>(); __a.deallocate(this, 1); @@ -417,7 +419,8 @@ template __base<_Rp(_A0)>* __func<_Fp, _Alloc, _Rp(_A0)>::__clone() const { - typedef typename _Alloc::template rebind<__func>::other _Ap; + typedef allocator_traits<_Alloc> __alloc_traits; + typedef typename __rebind_alloc_helper<__alloc_traits, __func>::type _Ap; _Ap __a(__f_.second()); typedef __allocator_destructor<_Ap> _Dp; unique_ptr<__func, _Dp> __hold(__a.allocate(1), _Dp(__a, 1)); @@ -443,7 +446,8 @@ template void __func<_Fp, _Alloc, _Rp(_A0)>::destroy_deallocate() { - typedef typename _Alloc::template rebind<__func>::other _Ap; + typedef allocator_traits<_Alloc> __alloc_traits; + typedef typename __rebind_alloc_helper<__alloc_traits, __func>::type _Ap; _Ap __a(__f_.second()); __f_.~__compressed_pair<_Fp, _Alloc>(); __a.deallocate(this, 1); @@ -501,7 +505,8 @@ template __base<_Rp(_A0, _A1)>* __func<_Fp, _Alloc, _Rp(_A0, _A1)>::__clone() const { - typedef typename _Alloc::template rebind<__func>::other _Ap; + typedef allocator_traits<_Alloc> __alloc_traits; + typedef typename __rebind_alloc_helper<__alloc_traits, __func>::type _Ap; _Ap __a(__f_.second()); typedef __allocator_destructor<_Ap> _Dp; unique_ptr<__func, _Dp> __hold(__a.allocate(1), _Dp(__a, 1)); @@ -527,7 +532,8 @@ template void __func<_Fp, _Alloc, _Rp(_A0, _A1)>::destroy_deallocate() { - typedef typename _Alloc::template rebind<__func>::other _Ap; + typedef allocator_traits<_Alloc> __alloc_traits; + typedef typename __rebind_alloc_helper<__alloc_traits, __func>::type _Ap; _Ap __a(__f_.second()); __f_.~__compressed_pair<_Fp, _Alloc>(); __a.deallocate(this, 1); @@ -585,7 +591,8 @@ template __base<_Rp(_A0, _A1, _A2)>* __func<_Fp, _Alloc, _Rp(_A0, _A1, _A2)>::__clone() const { - typedef typename _Alloc::template rebind<__func>::other _Ap; + typedef allocator_traits<_Alloc> __alloc_traits; + typedef typename __rebind_alloc_helper<__alloc_traits, __func>::type _Ap; _Ap __a(__f_.second()); typedef __allocator_destructor<_Ap> _Dp; unique_ptr<__func, _Dp> __hold(__a.allocate(1), _Dp(__a, 1)); @@ -611,7 +618,8 @@ template void __func<_Fp, _Alloc, _Rp(_A0, _A1, _A2)>::destroy_deallocate() { - typedef typename _Alloc::template rebind<__func>::other _Ap; + typedef allocator_traits<_Alloc> __alloc_traits; + typedef typename __rebind_alloc_helper<__alloc_traits, __func>::type _Ap; _Ap __a(__f_.second()); __f_.~__compressed_pair<_Fp, _Alloc>(); __a.deallocate(this, 1); @@ -794,7 +802,7 @@ function<_Rp()>::function(allocator_arg_t, const _Alloc& __a0, _Fp __f, if (sizeof(_FF) <= sizeof(__buf_)) { __f_ = (__base*)&__buf_; - ::new (__f_) _FF(__f); + ::new (__f_) _FF(__f, __a0); } else { @@ -1091,7 +1099,7 @@ function<_Rp(_A0)>::function(allocator_arg_t, const _Alloc& __a0, _Fp __f, if (sizeof(_FF) <= sizeof(__buf_)) { __f_ = (__base*)&__buf_; - ::new (__f_) _FF(__f); + ::new (__f_) _FF(__f, __a0); } else { @@ -1388,7 +1396,7 @@ function<_Rp(_A0, _A1)>::function(allocator_arg_t, const _Alloc& __a0, _Fp __f, if (sizeof(_FF) <= sizeof(__buf_)) { __f_ = (__base*)&__buf_; - ::new (__f_) _FF(__f); + ::new (__f_) _FF(__f, __a0); } else { @@ -1685,7 +1693,7 @@ function<_Rp(_A0, _A1, _A2)>::function(allocator_arg_t, const _Alloc& __a0, _Fp if (sizeof(_FF) <= sizeof(__buf_)) { __f_ = (__base*)&__buf_; - ::new (__f_) _FF(__f); + ::new (__f_) _FF(__f, __a0); } else { diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.func/func.wrap.func.con/alloc_F.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.func/func.wrap.func.con/alloc_F.pass.cpp index 741a3b9..352ecfc 100644 --- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.func/func.wrap.func.con/alloc_F.pass.cpp +++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.func/func.wrap.func.con/alloc_F.pass.cpp @@ -17,84 +17,90 @@ #include #include "min_allocator.h" +#include "test_allocator.h" +#include "count_new.hpp" +#include "../function_types.h" -class A -{ - int data_[10]; -public: - static int count; +class DummyClass {}; - A() +template +void test_FunctionObject(AllocType& alloc) +{ + assert(globalMemCounter.checkOutstandingNewEq(0)); { - ++count; - for (int i = 0; i < 10; ++i) - data_[i] = i; + FunctionObject target; + assert(FunctionObject::count == 1); + assert(globalMemCounter.checkOutstandingNewEq(0)); + std::function f2(std::allocator_arg, alloc, target); + assert(FunctionObject::count == 2); + assert(globalMemCounter.checkOutstandingNewEq(1)); + assert(f2.template target()); + assert(f2.template target() == 0); + assert(f2.template target() == 0); } + assert(FunctionObject::count == 0); + assert(globalMemCounter.checkOutstandingNewEq(0)); +} - A(const A&) {++count;} - - ~A() {--count;} - int operator()(int i) const +template +void test_FreeFunction(AllocType& alloc) +{ + assert(globalMemCounter.checkOutstandingNewEq(0)); { - for (int j = 0; j < 10; ++j) - i += data_[j]; - return i; + FuncType* target = &FreeFunction; + assert(globalMemCounter.checkOutstandingNewEq(0)); + std::function f2(std::allocator_arg, alloc, target); + assert(globalMemCounter.checkOutstandingNewEq(0)); + assert(f2.template target()); + assert(*f2.template target() == target); + assert(f2.template target() == 0); + assert(f2.template target() == 0); } + assert(globalMemCounter.checkOutstandingNewEq(0)); +} - int foo(int) const {return 1;} -}; +template +void test_MemFunClass(AllocType& alloc) +{ + assert(globalMemCounter.checkOutstandingNewEq(0)); + { + TargetType target = &MemFunClass::foo; + assert(globalMemCounter.checkOutstandingNewEq(0)); + std::function f2(std::allocator_arg, alloc, target); + assert(globalMemCounter.checkOutstandingNewEq(0)); + assert(f2.template target()); + assert(*f2.template target() == target); + assert(f2.template target() == 0); + } + assert(globalMemCounter.checkOutstandingNewEq(0)); +} -int A::count = 0; +template +void test_for_alloc(Alloc& alloc) { + test_FunctionObject(alloc); + test_FunctionObject(alloc); + test_FunctionObject(alloc); + test_FunctionObject(alloc); -int g(int) {return 0;} + test_FreeFunction(alloc); + test_FreeFunction(alloc); + test_FreeFunction(alloc); + test_FreeFunction(alloc); -class Foo { -public: - void bar(int k) { } -}; + test_MemFunClass(alloc); + test_MemFunClass(alloc); + test_MemFunClass(alloc); +} int main() { { - std::function f(std::allocator_arg, bare_allocator(), A()); - assert(A::count == 1); - assert(f.target()); - assert(f.target() == 0); - } - assert(A::count == 0); - { - std::function f(std::allocator_arg, bare_allocator(), g); - assert(f.target()); - assert(f.target() == 0); - } - { - std::function f(std::allocator_arg, bare_allocator(), - (int (*)(int))0); - assert(!f); - assert(f.target() == 0); - assert(f.target() == 0); - } - { - std::function f(std::allocator_arg, - bare_allocator(), - &A::foo); - assert(f); - assert(f.target() != 0); - } -#if __cplusplus >= 201103L - { - Foo f; - std::function fun = std::bind(&Foo::bar, &f, std::placeholders::_1); - fun(10); + bare_allocator bare_alloc; + test_for_alloc(bare_alloc); } -#endif { - std::function fun(std::allocator_arg, - bare_allocator(), - &g); - assert(fun); - assert(fun.target() != 0); - fun(10); + non_default_test_allocator non_default_alloc(42); + test_for_alloc(non_default_alloc); } } diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.func/func.wrap.func.con/alloc_function.pass.cpp b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.func/func.wrap.func.con/alloc_function.pass.cpp index e89636a..371eb98 100644 --- a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.func/func.wrap.func.con/alloc_function.pass.cpp +++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.func/func.wrap.func.con/alloc_function.pass.cpp @@ -20,81 +20,105 @@ #include "min_allocator.h" #include "test_allocator.h" #include "count_new.hpp" +#include "../function_types.h" -class A -{ - int data_[10]; -public: - static int count; - - A() - { - ++count; - for (int i = 0; i < 10; ++i) - data_[i] = i; - } - - A(const A&) {++count;} - - ~A() {--count;} - - int operator()(int i) const - { - for (int j = 0; j < 10; ++j) - i += data_[j]; - return i; - } -}; - -int A::count = 0; +class DummyClass {}; -int g(int) {return 0;} - -int main() +template +void test_FunctionObject(AllocType& alloc) { assert(globalMemCounter.checkOutstandingNewEq(0)); { - std::function f = A(); - assert(A::count == 1); + // Construct function from FunctionObject. + std::function f = FunctionObject(); + assert(FunctionObject::count == 1); assert(globalMemCounter.checkOutstandingNewEq(1)); - assert(f.target()); - assert(f.target() == 0); - std::function f2(std::allocator_arg, bare_allocator(), f); - assert(A::count == 2); + assert(f.template target()); + assert(f.template target() == 0); + assert(f.template target() == 0); + // Copy function with allocator + std::function f2(std::allocator_arg, alloc, f); + assert(FunctionObject::count == 2); assert(globalMemCounter.checkOutstandingNewEq(2)); - assert(f2.target()); - assert(f2.target() == 0); + assert(f2.template target()); + assert(f2.template target() == 0); + assert(f2.template target() == 0); } - assert(A::count == 0); + assert(FunctionObject::count == 0); assert(globalMemCounter.checkOutstandingNewEq(0)); - { - std::function f = g; - assert(globalMemCounter.checkOutstandingNewEq(0)); - assert(f.target()); - assert(f.target() == 0); - std::function f2(std::allocator_arg, bare_allocator(), f); - assert(globalMemCounter.checkOutstandingNewEq(0)); - assert(f2.target()); - assert(f2.target() == 0); - } +} + +template +void test_FreeFunction(AllocType& alloc) +{ assert(globalMemCounter.checkOutstandingNewEq(0)); { + // Construct function from function pointer. + FuncType* target = &FreeFunction; + std::function f = target; assert(globalMemCounter.checkOutstandingNewEq(0)); - non_default_test_allocator > al(1); - std::function f2(std::allocator_arg, al, g); + assert(f.template target()); + assert(*f.template target() == target); + assert(f.template target() == 0); + // Copy function with allocator + std::function f2(std::allocator_arg, alloc, f); assert(globalMemCounter.checkOutstandingNewEq(0)); - assert(f2.target()); - assert(f2.target() == 0); + assert(f2.template target()); + assert(*f2.template target() == target); + assert(f2.template target() == 0); } assert(globalMemCounter.checkOutstandingNewEq(0)); +} + +template +void test_MemFunClass(AllocType& alloc) +{ + assert(globalMemCounter.checkOutstandingNewEq(0)); { - std::function f; + // Construct function from function pointer. + TargetType target = &MemFunClass::foo; + std::function f = target; assert(globalMemCounter.checkOutstandingNewEq(0)); - assert(f.target() == 0); - assert(f.target() == 0); - std::function f2(std::allocator_arg, bare_allocator(), f); + assert(f.template target()); + assert(*f.template target() == target); + assert(f.template target() == 0); + // Copy function with allocator + std::function f2(std::allocator_arg, alloc, f); assert(globalMemCounter.checkOutstandingNewEq(0)); - assert(f2.target() == 0); - assert(f2.target() == 0); + assert(f2.template target()); + assert(*f2.template target() == target); + assert(f2.template target() == 0); } + assert(globalMemCounter.checkOutstandingNewEq(0)); +} + +template +void test_for_alloc(Alloc& alloc) +{ + // Large FunctionObject -- Allocation should occur + test_FunctionObject(alloc); + test_FunctionObject(alloc); + test_FunctionObject(alloc); + test_FunctionObject(alloc); + // Free function -- No allocation should occur + test_FreeFunction(alloc); + test_FreeFunction(alloc); + test_FreeFunction(alloc); + test_FreeFunction(alloc); + // Member function -- No allocation should occur. + test_MemFunClass(alloc); + test_MemFunClass(alloc); + test_MemFunClass(alloc); +} + +int main() +{ + { + bare_allocator alloc; + test_for_alloc(alloc); + } + { + non_default_test_allocator alloc(42); + test_for_alloc(alloc); + } } diff --git a/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.func/function_types.h b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.func/function_types.h new file mode 100644 index 0000000..55eb80f --- /dev/null +++ b/libcxx/test/std/utilities/function.objects/func.wrap/func.wrap.func/function_types.h @@ -0,0 +1,57 @@ +#ifndef FUNCTION_TYPES_H +#define FUNCTION_TYPES_H + + +class FunctionObject +{ + int data_[10]; // dummy variable to prevent small object optimization in + // std::function +public: + static int count; + + FunctionObject() { + ++count; + for (int i = 0; i < 10; ++i) data_[i] = i; + } + + FunctionObject(const FunctionObject&) {++count;} + ~FunctionObject() {--count; ((void)data_); } + + int operator()() const { return 42; } + int operator()(int i) const { return i; } + int operator()(int i, int) const { return i; } + int operator()(int i, int, int) const { return i; } +}; + +int FunctionObject::count = 0; + +class MemFunClass +{ + int data_[10]; // dummy variable to prevent small object optimization in + // std::function +public: + static int count; + + MemFunClass() { + ++count; + for (int i = 0; i < 10; ++i) data_[i] = 0; + } + + MemFunClass(const MemFunClass&) {++count; ((void)data_); } + + ~MemFunClass() {--count;} + + int foo() const { return 42; } + int foo(int i) const { return i; } + int foo(int i, int) const { return i; } + int foo(int i, int, int) const { return i; } +}; + +int MemFunClass::count = 0; + +int FreeFunction() { return 42; } +int FreeFunction(int i) {return i;} +int FreeFunction(int i, int) { return i; } +int FreeFunction(int i, int, int) { return i; } + +#endif // FUNCTION_TYPES_H diff --git a/libcxx/test/support/test_allocator.h b/libcxx/test/support/test_allocator.h index 03bd390..47ef1d5 100644 --- a/libcxx/test/support/test_allocator.h +++ b/libcxx/test/support/test_allocator.h @@ -74,10 +74,10 @@ public: } ++time_to_throw; ++alloc_count; - return (pointer)std::malloc(n * sizeof(T)); + return (pointer)::operator new(n * sizeof(T)); } void deallocate(pointer p, size_type n) - {assert(data_ >= 0); --alloc_count; std::free(p);} + {assert(data_ >= 0); --alloc_count; ::operator delete((void*)p);} size_type max_size() const throw() {return UINT_MAX / sizeof(T);} void construct(pointer p, const T& val) @@ -134,10 +134,10 @@ public: } ++time_to_throw; ++alloc_count; - return (pointer)std::malloc(n * sizeof(T)); + return (pointer)::operator new (n * sizeof(T)); } void deallocate(pointer p, size_type n) - {assert(data_ >= 0); --alloc_count; std::free(p);} + {assert(data_ >= 0); --alloc_count; ::operator delete((void*)p); } size_type max_size() const throw() {return UINT_MAX / sizeof(T);} void construct(pointer p, const T& val) @@ -200,9 +200,9 @@ public: template other_allocator(const other_allocator& a) : data_(a.data_) {} T* allocate(std::size_t n) - {return (T*)std::malloc(n * sizeof(T));} + {return (T*)::operator new(n * sizeof(T));} void deallocate(T* p, std::size_t n) - {std::free(p);} + {::operator delete((void*)p);} other_allocator select_on_container_copy_construction() const {return other_allocator(-2);} -- 2.7.4