blob: 4b1a95d53067ebc17f90c4da05f86b3db8aaec59 [file] [log] [blame]
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
#ifndef USES_ALLOC_TYPES_HPP
#define USES_ALLOC_TYPES_HPP
# include <memory>
# include <cassert>
#include <cstdlib>
#include "test_macros.h"
#include "test_workarounds.h"
#include "type_id.h"
// There are two forms of uses-allocator construction:
// (1) UA_AllocArg: 'T(allocator_arg_t, Alloc const&, Args&&...)'
// (2) UA_AllocLast: 'T(Args&&..., Alloc const&)'
// 'UA_None' represents non-uses allocator construction.
enum class UsesAllocatorType {
UA_None = 0,
UA_AllocArg = 2,
UA_AllocLast = 4
};
constexpr UsesAllocatorType UA_None = UsesAllocatorType::UA_None;
constexpr UsesAllocatorType UA_AllocArg = UsesAllocatorType::UA_AllocArg;
constexpr UsesAllocatorType UA_AllocLast = UsesAllocatorType::UA_AllocLast;
inline const char* toString(UsesAllocatorType UA) {
switch (UA) {
case UA_None:
return "UA_None";
case UA_AllocArg:
return "UA_AllocArg";
case UA_AllocLast:
return "UA_AllocLast";
default:
std::abort();
}
}
#define COMPARE_ALLOC_TYPE(LHS, RHS) CompareVerbose(#LHS, LHS, #RHS, RHS)
inline bool CompareVerbose(const char* LHSString, UsesAllocatorType LHS,
const char* RHSString, UsesAllocatorType RHS) {
if (LHS == RHS)
return true;
std::printf("UsesAllocatorType's don't match:\n%s %s\n----------\n%s %s\n",
LHSString, toString(LHS), RHSString, toString(RHS));
return false;
}
template <class Alloc, std::size_t N>
class UsesAllocatorV1;
// Implements form (1) of uses-allocator construction from the specified
// 'Alloc' type and exactly 'N' additional arguments. It also provides
// non-uses allocator construction from 'N' arguments. This test type
// blows up when form (2) of uses-allocator is even considered.
template <class Alloc, std::size_t N>
class UsesAllocatorV2;
// Implements form (2) of uses-allocator construction from the specified
// 'Alloc' type and exactly 'N' additional arguments. It also provides
// non-uses allocator construction from 'N' arguments.
template <class Alloc, std::size_t N>
class UsesAllocatorV3;
// Implements both form (1) and (2) of uses-allocator construction from
// the specified 'Alloc' type and exactly 'N' additional arguments. It also
// provides non-uses allocator construction from 'N' arguments.
template <class Alloc, std::size_t>
class NotUsesAllocator;
// Implements both form (1) and (2) of uses-allocator construction from
// the specified 'Alloc' type and exactly 'N' additional arguments. It also
// provides non-uses allocator construction from 'N' arguments. However
// 'NotUsesAllocator' never provides a 'allocator_type' typedef so it is
// never automatically uses-allocator constructed.
template <class ...ArgTypes, class TestType>
bool checkConstruct(TestType& value, UsesAllocatorType form,
typename TestType::CtorAlloc const& alloc)
// Check that 'value' was constructed using the specified 'form' of
// construction and with the specified 'ArgTypes...'. Additionally
// check that 'value' was constructed using the specified 'alloc'.
{
if (form == UA_None) {
return value.template checkConstruct<ArgTypes&&...>(form);
} else {
return value.template checkConstruct<ArgTypes&&...>(form, alloc);
}
}
template <class ...ArgTypes, class TestType>
bool checkConstruct(TestType& value, UsesAllocatorType form) {
return value.template checkConstruct<ArgTypes&&...>(form);
}
template <class TestType>
bool checkConstructionEquiv(TestType& T, TestType& U)
// check that 'T' and 'U' where initialized in the exact same manner.
{
return T.checkConstructEquiv(U);
}
////////////////////////////////////////////////////////////////////////////////
namespace detail {
template <bool IsZero, size_t N, class ArgList, class ...Args>
struct TakeNImp;
template <class ArgList, class ...Args>
struct TakeNImp<true, 0, ArgList, Args...> {
typedef ArgList type;
};
template <size_t N, class ...A1, class F, class ...R>
struct TakeNImp<false, N, ArgumentListID<A1...>, F, R...>
: TakeNImp<N-1 == 0, N - 1, ArgumentListID<A1..., F>, R...> {};
template <size_t N, class ...Args>
struct TakeNArgs : TakeNImp<N == 0, N, ArgumentListID<>, Args...> {};
template <class T>
struct Identity { typedef T type; };
template <class T>
using IdentityT = typename Identity<T>::type;
template <bool Value>
using EnableIfB = typename std::enable_if<Value, bool>::type;
} // end namespace detail
// FIXME: UsesAllocatorTestBase needs some special logic to deal with
// polymorphic allocators. However we don't want to include
// <experimental/memory_resource> in this header. Therefore in order
// to inject this behavior later we use a trait.
// See test_memory_resource.hpp for more info.
template <class Alloc>
struct TransformErasedTypeAlloc {
using type = Alloc;
};
using detail::EnableIfB;
struct AllocLastTag {};
template <class Alloc, bool = std::is_default_constructible<Alloc>::value>
struct UsesAllocatorTestBaseStorage {
Alloc allocator;
UsesAllocatorTestBaseStorage() = default;
UsesAllocatorTestBaseStorage(Alloc const& a) : allocator(a) {}
const Alloc* get_allocator() const { return &allocator; }
};
template <class Alloc>
struct UsesAllocatorTestBaseStorage<Alloc, false> {
union {
char dummy;
Alloc alloc;
};
bool has_alloc = false;
UsesAllocatorTestBaseStorage() : dummy(), has_alloc(false) {}
UsesAllocatorTestBaseStorage(Alloc const& a) : alloc(a), has_alloc(true) {}
~UsesAllocatorTestBaseStorage() {
if (has_alloc)
alloc.~Alloc();
}
Alloc const* get_allocator() const {
if (!has_alloc)
return nullptr;
return &alloc;
}
};
template <class Self, class Alloc>
struct UsesAllocatorTestBase {
public:
using CtorAlloc = typename TransformErasedTypeAlloc<Alloc>::type;
template <class ...ArgTypes>
bool checkConstruct(UsesAllocatorType expectType) const {
auto expectArgs = &makeArgumentID<ArgTypes...>();
return COMPARE_ALLOC_TYPE(expectType, constructor_called) &&
COMPARE_TYPEID(args_id, expectArgs);
}
template <class ...ArgTypes>
bool checkConstruct(UsesAllocatorType expectType,
CtorAlloc const& expectAlloc) const {
auto ExpectID = &makeArgumentID<ArgTypes...>() ;
return COMPARE_ALLOC_TYPE(expectType, constructor_called) &&
COMPARE_TYPEID(args_id, ExpectID) &&
has_alloc() && expectAlloc == *get_alloc();
}
bool checkConstructEquiv(UsesAllocatorTestBase& O) const {
if (has_alloc() != O.has_alloc())
return false;
return COMPARE_ALLOC_TYPE(constructor_called, O.constructor_called)
&& COMPARE_TYPEID(args_id, O.args_id)
&& (!has_alloc() || *get_alloc() == *O.get_alloc());
}
protected:
explicit UsesAllocatorTestBase(const TypeID* aid)
: args_id(aid), constructor_called(UA_None), alloc_store()
{}
UsesAllocatorTestBase(UsesAllocatorTestBase const&)
: args_id(&makeArgumentID<Self const&>()), constructor_called(UA_None),
alloc_store()
{}
UsesAllocatorTestBase(UsesAllocatorTestBase&&)
: args_id(&makeArgumentID<Self&&>()), constructor_called(UA_None),
alloc_store()
{}
template <class ...Args>
UsesAllocatorTestBase(std::allocator_arg_t, CtorAlloc const& a, Args&&...)
: args_id(&makeArgumentID<Args&&...>()),
constructor_called(UA_AllocArg),
alloc_store(a)
{}
template <class ...Args, class ArgsIDL = detail::TakeNArgs<sizeof...(Args) - 1, Args&&...>>
UsesAllocatorTestBase(AllocLastTag, Args&&... args)
: args_id(&makeTypeIDImp<typename ArgsIDL::type>()),
constructor_called(UA_AllocLast),
alloc_store(UsesAllocatorTestBase::getAllocatorFromPack(
typename ArgsIDL::type{},
std::forward<Args>(args)...))
{
}
private:
template <class ...LArgs, class ...Args>
static CtorAlloc getAllocatorFromPack(ArgumentListID<LArgs...>, Args&&... args) {
return UsesAllocatorTestBase::getAllocatorFromPackImp<LArgs const&...>(args...);
}
template <class ...LArgs>
static CtorAlloc getAllocatorFromPackImp(
typename detail::Identity<LArgs>::type..., CtorAlloc const& alloc) {
return alloc;
}
bool has_alloc() const { return alloc_store.get_allocator() != nullptr; }
const CtorAlloc *get_alloc() const { return alloc_store.get_allocator(); }
public:
const TypeID* args_id;
UsesAllocatorType constructor_called = UA_None;
UsesAllocatorTestBaseStorage<CtorAlloc> alloc_store;
};
template <class Alloc, size_t Arity>
class UsesAllocatorV1 : public UsesAllocatorTestBase<UsesAllocatorV1<Alloc, Arity>, Alloc>
{
public:
typedef Alloc allocator_type;
using Base = UsesAllocatorTestBase<UsesAllocatorV1, Alloc>;
using CtorAlloc = typename Base::CtorAlloc;
UsesAllocatorV1() : Base(&makeArgumentID<>()) {}
UsesAllocatorV1(UsesAllocatorV1 const&)
: Base(&makeArgumentID<UsesAllocatorV1 const&>()) {}
UsesAllocatorV1(UsesAllocatorV1 &&)
: Base(&makeArgumentID<UsesAllocatorV1 &&>()) {}
// Non-Uses Allocator Ctor
template <class ...Args, EnableIfB<sizeof...(Args) == Arity> = false>
UsesAllocatorV1(Args&&...) : Base(&makeArgumentID<Args&&...>()) {}
// Uses Allocator Arg Ctor
template <class ...Args>
UsesAllocatorV1(std::allocator_arg_t tag, CtorAlloc const & a, Args&&... args)
: Base(tag, a, std::forward<Args>(args)...)
{ }
// BLOWS UP: Uses Allocator Last Ctor
template <class First, class ...Args, EnableIfB<sizeof...(Args) == Arity> Dummy = false>
constexpr UsesAllocatorV1(First&&, Args&&...)
{
static_assert(!std::is_same<First, First>::value, "");
}
};
template <class Alloc, size_t Arity>
class UsesAllocatorV2 : public UsesAllocatorTestBase<UsesAllocatorV2<Alloc, Arity>, Alloc>
{
public:
typedef Alloc allocator_type;
using Base = UsesAllocatorTestBase<UsesAllocatorV2, Alloc>;
using CtorAlloc = typename Base::CtorAlloc;
UsesAllocatorV2() : Base(&makeArgumentID<>()) {}
UsesAllocatorV2(UsesAllocatorV2 const&)
: Base(&makeArgumentID<UsesAllocatorV2 const&>()) {}
UsesAllocatorV2(UsesAllocatorV2 &&)
: Base(&makeArgumentID<UsesAllocatorV2 &&>()) {}
// Non-Uses Allocator Ctor
template <class ...Args, EnableIfB<sizeof...(Args) == Arity> = false>
UsesAllocatorV2(Args&&...) : Base(&makeArgumentID<Args&&...>()) {}
// Uses Allocator Last Ctor
template <class ...Args, EnableIfB<sizeof...(Args) == Arity + 1> = false>
UsesAllocatorV2(Args&&... args)
: Base(AllocLastTag{}, std::forward<Args>(args)...)
{}
};
template <class Alloc, size_t Arity>
class UsesAllocatorV3 : public UsesAllocatorTestBase<UsesAllocatorV3<Alloc, Arity>, Alloc>
{
public:
typedef Alloc allocator_type;
using Base = UsesAllocatorTestBase<UsesAllocatorV3, Alloc>;
using CtorAlloc = typename Base::CtorAlloc;
UsesAllocatorV3() : Base(&makeArgumentID<>()) {}
UsesAllocatorV3(UsesAllocatorV3 const&)
: Base(&makeArgumentID<UsesAllocatorV3 const&>()) {}
UsesAllocatorV3(UsesAllocatorV3 &&)
: Base(&makeArgumentID<UsesAllocatorV3 &&>()) {}
// Non-Uses Allocator Ctor
template <class ...Args, EnableIfB<sizeof...(Args) == Arity> = false>
UsesAllocatorV3(Args&&...) : Base(&makeArgumentID<Args&&...>()) {}
// Uses Allocator Arg Ctor
template <class ...Args>
UsesAllocatorV3(std::allocator_arg_t tag, CtorAlloc const& alloc, Args&&... args)
: Base(tag, alloc, std::forward<Args>(args)...)
{}
// Uses Allocator Last Ctor
template <class ...Args, EnableIfB<sizeof...(Args) == Arity + 1> = false>
UsesAllocatorV3(Args&&... args)
: Base(AllocLastTag{}, std::forward<Args>(args)...)
{}
};
template <class Alloc, size_t Arity>
class NotUsesAllocator : public UsesAllocatorTestBase<NotUsesAllocator<Alloc, Arity>, Alloc>
{
public:
// no allocator_type typedef provided
using Base = UsesAllocatorTestBase<NotUsesAllocator, Alloc>;
using CtorAlloc = typename Base::CtorAlloc;
NotUsesAllocator() : Base(&makeArgumentID<>()) {}
NotUsesAllocator(NotUsesAllocator const&)
: Base(&makeArgumentID<NotUsesAllocator const&>()) {}
NotUsesAllocator(NotUsesAllocator &&)
: Base(&makeArgumentID<NotUsesAllocator &&>()) {}
// Non-Uses Allocator Ctor
template <class ...Args, EnableIfB<sizeof...(Args) == Arity> = false>
NotUsesAllocator(Args&&...) : Base(&makeArgumentID<Args&&...>()) {}
// Uses Allocator Arg Ctor
template <class ...Args>
NotUsesAllocator(std::allocator_arg_t tag, CtorAlloc const& alloc, Args&&... args)
: Base(tag, alloc, std::forward<Args>(args)...)
{}
// Uses Allocator Last Ctor
template <class ...Args, EnableIfB<sizeof...(Args) == Arity + 1> = false>
NotUsesAllocator(Args&&... args)
: Base(AllocLastTag{}, std::forward<Args>(args)...)
{}
};
#endif /* USES_ALLOC_TYPES_HPP */