Skip to content
4 changes: 4 additions & 0 deletions BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ cc_library(
srcs = ["indirect.cc"],
hdrs = ["indirect.h"],
copts = ["-Iexternal/value_types/"],
defines = [],
visibility = ["//visibility:public"],
deps = ["feature_check"],
)
Expand Down Expand Up @@ -91,7 +92,9 @@ cc_library(
srcs = ["polymorphic.cc"],
hdrs = ["polymorphic.h"],
copts = ["-Iexternal/value_types/"],
defines = [],
visibility = ["//visibility:public"],
deps = ["feature_check"],
)

cc_test(
Expand All @@ -115,6 +118,7 @@ cc_library(
copts = ["-Iexternal/value_types/"],
defines = ["XYZ_POLYMORPHIC_CXX_14"],
visibility = ["//visibility:public"],
deps = ["feature_check"],
)

cc_test(
Expand Down
14 changes: 14 additions & 0 deletions feature_check.h
Original file line number Diff line number Diff line change
Expand Up @@ -95,4 +95,18 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#endif //(__cplusplus >= 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG >=
// 202002L)

//
// XYZ_HAS_STD_TYPE_IDENTITY
// The macro is defined when std::type_identity_t<T> is available.
//

#ifdef XYZ_HAS_STD_TYPE_IDENTITY
#error "XYZ_HAS_STD_TYPE_IDENTITY is already defined"
#endif // XYZ_HAS_STD_TYPE_IDENTITY

#if (__cplusplus >= 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L)
#define XYZ_HAS_STD_TYPE_IDENTITY
#endif //(__cplusplus >= 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG >=
// 202002L)

#endif // XYZ_FEATURE_CHECK_H
25 changes: 21 additions & 4 deletions indirect.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,20 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

namespace xyz {

#ifndef XYZ_TYPE_IDENTITY_DEFINED
#define XYZ_TYPE_IDENTITY_DEFINED
#ifdef XYZ_HAS_STD_TYPE_IDENTITY
using std::type_identity_t;
#else
template <class T>
struct type_identity {
using type = T;
};
template <class T>
using type_identity_t = typename type_identity<T>::type;
#endif // XYZ_HAS_STD_TYPE_IDENTITY
#endif // XYZ_TYPE_IDENTITY_DEFINED

#ifndef XYZ_UNREACHABLE_DEFINED
#define XYZ_UNREACHABLE_DEFINED

Expand Down Expand Up @@ -182,7 +196,7 @@ class indirect {
p_ = construct_from(alloc_, ilist, std::forward<Us>(us)...);
}

constexpr indirect(std::allocator_arg_t, const A& alloc,
constexpr indirect(std::allocator_arg_t, const xyz::type_identity_t<A>& alloc,
const indirect& other)
: alloc_(alloc) {
static_assert(std::copy_constructible<T>);
Expand All @@ -195,7 +209,7 @@ class indirect {
}

constexpr indirect(
std::allocator_arg_t, const A& alloc,
std::allocator_arg_t, const xyz::type_identity_t<A>& alloc,
indirect&& other) noexcept(allocator_traits::is_always_equal::value)
: p_(nullptr), alloc_(alloc) {
static_assert(std::move_constructible<T>);
Expand Down Expand Up @@ -466,8 +480,11 @@ template <typename Value>
indirect(Value) -> indirect<Value>;

template <typename Alloc, typename Value>
indirect(std::allocator_arg_t, Alloc, Value) -> indirect<
Value, typename std::allocator_traits<Alloc>::template rebind_alloc<Value>>;
indirect(std::allocator_arg_t, Alloc, Value) -> indirect<Value, Alloc>;

template <typename Alloc, typename Alloc2, typename Value>
indirect(std::allocator_arg_t, Alloc2, indirect<Value, Alloc>)
-> indirect<Value, Alloc>;

} // namespace xyz

Expand Down
21 changes: 19 additions & 2 deletions indirect_cxx14.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,20 @@ namespace xyz {
struct in_place_t {};
#endif // XYZ_IN_PLACE_DEFINED

#ifndef XYZ_TYPE_IDENTITY_DEFINED
#define XYZ_TYPE_IDENTITY_DEFINED
#ifdef XYZ_HAS_STD_TYPE_IDENTITY
using std::type_identity_t;
#else
template <class T>
struct type_identity {
using type = T;
};
template <class T>
using type_identity_t = typename type_identity<T>::type;
#endif // XYZ_HAS_STD_TYPE_IDENTITY
#endif // XYZ_TYPE_IDENTITY_DEFINED

#ifndef XYZ_UNREACHABLE_DEFINED
#define XYZ_UNREACHABLE_DEFINED

Expand Down Expand Up @@ -575,8 +589,11 @@ template <typename Value>
indirect(Value) -> indirect<Value>;

template <typename Alloc, typename Value>
indirect(std::allocator_arg_t, Alloc, Value) -> indirect<
Value, typename std::allocator_traits<Alloc>::template rebind_alloc<Value>>;
indirect(std::allocator_arg_t, Alloc, Value) -> indirect<Value, Alloc>;

template <typename Alloc, typename Alloc2, typename Value>
indirect(std::allocator_arg_t, Alloc2, indirect<Value, Alloc>)
-> indirect<Value, Alloc>;
#endif // XYZ_HAS_TEMPLATE_ARGUMENT_DEDUCTION

} // namespace xyz
Expand Down
36 changes: 32 additions & 4 deletions indirect_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -120,13 +120,41 @@ TEST(IndirectTest, AllocatorExtendedInitializerListConstructor) {

#ifdef XYZ_HAS_TEMPLATE_ARGUMENT_DEDUCTION
TEST(IndirectTest, TemplateArgumentDeduction) {
xyz::indirect a(42);
EXPECT_EQ(*a, 42);
xyz::indirect i(42);
EXPECT_EQ(*i, 42);
}

TEST(IndirectTest, TemplateArgumentDeductionCopy) {
xyz::indirect i(42);
xyz::indirect ii(i);

static_assert(std::is_same_v<decltype(i), decltype(ii)>);
EXPECT_EQ(*ii, 42);
}

TEST(IndirectTest, TemplateArgumentDeductionWithAllocator) {
xyz::indirect a(std::allocator_arg, std::allocator<int>{}, 42);
EXPECT_EQ(*a, 42);
xyz::indirect i(std::allocator_arg, xyz::TaggedAllocator<int>(1), 42);

static_assert(
std::is_same_v<decltype(i.get_allocator()), xyz::TaggedAllocator<int>>);
EXPECT_EQ(*i, 42);
}

TEST(IndirectTest, TemplateArgumentDeductionWithDeducedAllocatorAndCopy) {
xyz::indirect i(std::allocator_arg, xyz::TaggedAllocator<int>(1), 42);
xyz::indirect ii(std::allocator_arg, 2, i);

static_assert(std::is_same_v<decltype(i.get_allocator()),
decltype(ii.get_allocator())>);
EXPECT_EQ(*ii, 42);
EXPECT_EQ(ii.get_allocator().tag, 2);
}

TEST(IndirectTest, TemplateArgumentDeductionWithDeducedAllocatorAndMove) {
xyz::indirect i(std::allocator_arg, xyz::TaggedAllocator<int>(1), 42);
xyz::indirect ii(std::allocator_arg, 2, std::move(i));
EXPECT_EQ(*ii, 42);
EXPECT_EQ(ii.get_allocator().tag, 2);
}
#endif // XYZ_HAS_TEMPLATE_ARGUMENT_DEDUCTION

Expand Down
32 changes: 30 additions & 2 deletions polymorphic.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,20 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

namespace xyz {

#ifndef XYZ_TYPE_IDENTITY_DEFINED
#define XYZ_TYPE_IDENTITY_DEFINED
#ifdef XYZ_HAS_STD_TYPE_IDENTITY
using std::type_identity_t;
#else
template <class T>
struct type_identity {
using type = T;
};
template <class T>
using type_identity_t = typename type_identity<T>::type;
#endif // XYZ_HAS_STD_TYPE_IDENTITY
#endif // XYZ_TYPE_IDENTITY_DEFINED

#ifndef XYZ_UNREACHABLE_DEFINED
#define XYZ_UNREACHABLE_DEFINED

Expand Down Expand Up @@ -116,6 +130,9 @@ class direct_control_block final : public control_block<T, A> {

} // namespace detail

template <class T, class A>
class polymorphic;

template <class T, class A = std::allocator<T>>
class polymorphic {
using cblock_t = detail::control_block<T, A>;
Expand Down Expand Up @@ -241,7 +258,8 @@ class polymorphic {
cb_ = create_control_block<U>(ilist, std::forward<Ts>(ts)...);
}

constexpr polymorphic(std::allocator_arg_t, const A& alloc,
constexpr polymorphic(std::allocator_arg_t,
const xyz::type_identity_t<A>& alloc,
const polymorphic& other)
: alloc_(alloc) {
if (!other.valueless_after_move()) {
Expand All @@ -252,7 +270,7 @@ class polymorphic {
}

constexpr polymorphic(
std::allocator_arg_t, const A& alloc,
std::allocator_arg_t, const xyz::type_identity_t<A>& alloc,
polymorphic&& other) noexcept(allocator_traits::is_always_equal::value)
: alloc_(alloc) {
if constexpr (allocator_traits::is_always_equal::value) {
Expand Down Expand Up @@ -405,6 +423,16 @@ class polymorphic {
}
};

template <typename Value>
polymorphic(Value) -> polymorphic<Value>;

template <typename Alloc, typename Value>
polymorphic(std::allocator_arg_t, Alloc, Value) -> polymorphic<Value, Alloc>;

template <typename Alloc, typename Alloc2, typename Value>
polymorphic(std::allocator_arg_t, Alloc2, polymorphic<Value, Alloc>)
-> polymorphic<Value, Alloc>;

} // namespace xyz

#endif // XYZ_POLYMORPHIC_H_
35 changes: 33 additions & 2 deletions polymorphic_cxx14.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include <type_traits>
#include <utility>

#include "feature_check.h"

#ifndef XYZ_POLYMORPHIC_HAS_EXTENDED_CONSTRUCTORS
#define XYZ_POLYMORPHIC_HAS_EXTENDED_CONSTRUCTORS 1
#endif // XYZ_POLYMORPHIC_HAS_EXTENDED_CONSTRUCTORS
Expand All @@ -40,6 +42,22 @@ struct in_place_type_t {};
} // namespace xyz
#endif // XYZ_IN_PLACE_TYPE_DEFINED

#ifndef XYZ_TYPE_IDENTITY_DEFINED
#define XYZ_TYPE_IDENTITY_DEFINED
namespace xyz {
#ifdef XYZ_HAS_STD_TYPE_IDENTITY
using std::type_identity_t;
#else
template <class T>
struct type_identity {
using type = T;
};
template <class T>
using type_identity_t = typename type_identity<T>::type;
#endif // XYZ_HAS_STD_TYPE_IDENTITY
} // namespace xyz
#endif // XYZ_TYPE_IDENTITY_DEFINED

#ifndef XYZ_UNREACHABLE_DEFINED
#define XYZ_UNREACHABLE_DEFINED

Expand Down Expand Up @@ -319,7 +337,8 @@ class polymorphic : private detail::empty_base_optimization<A> {
typename std::remove_reference<U>::type>::type>{},
std::forward<U>(u)) {}

polymorphic(std::allocator_arg_t, const A& alloc, const polymorphic& other)
polymorphic(std::allocator_arg_t, const xyz::type_identity_t<A>& alloc,
const polymorphic& other)
: alloc_base(alloc) {
if (!other.valueless_after_move()) {
cb_ = other.cb_->clone(alloc_base::get());
Expand All @@ -335,7 +354,7 @@ class polymorphic : private detail::empty_base_optimization<A> {
other) {}

polymorphic(
std::allocator_arg_t, const A& alloc,
std::allocator_arg_t, const xyz::type_identity_t<A>& alloc,
polymorphic&& other) noexcept(allocator_traits::is_always_equal::value)
: alloc_base(alloc) {
if (allocator_traits::propagate_on_container_copy_assignment::value) {
Expand Down Expand Up @@ -476,6 +495,18 @@ class polymorphic : private detail::empty_base_optimization<A> {
}
};

#ifdef XYZ_HAS_TEMPLATE_ARGUMENT_DEDUCTION
template <typename Value>
polymorphic(Value) -> polymorphic<Value>;

template <typename Alloc, typename Value>
polymorphic(std::allocator_arg_t, Alloc, Value) -> polymorphic<Value, Alloc>;

template <typename Alloc, typename Alloc2, typename Value>
polymorphic(std::allocator_arg_t, Alloc2, polymorphic<Value, Alloc>)
-> polymorphic<Value, Alloc>;
#endif // XYZ_HAS_TEMPLATE_ARGUMENT_DEDUCTION

} // namespace xyz

#endif // XYZ_POLYMORPHIC_H_
44 changes: 44 additions & 0 deletions polymorphic_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -869,4 +869,48 @@ TEST(PolymorphicTest, TaggedAllocatorsNotEqualMoveConstructFromValueless) {
iii.valueless_after_move()); // NOLINT(clang-analyzer-cplusplus.Move)
}

#ifdef XYZ_HAS_TEMPLATE_ARGUMENT_DEDUCTION
TEST(PolymorphicTest, TemplateArgumentDeduction) {
xyz::polymorphic p(Derived(4));

EXPECT_EQ(p->value(), 4);
}

TEST(PolymorphicTest, TemplateArgumentDeductionCopy) {
xyz::polymorphic p(Derived(4));
xyz::polymorphic pp(p);

static_assert(std::is_same_v<decltype(p), decltype(pp)>);

EXPECT_EQ(pp->value(), 4);
}

TEST(PolymorphicTest, TemplateArgumentDeductionWithAllocator) {
xyz::TaggedAllocator<Derived> a(1);
xyz::polymorphic p(std::allocator_arg, a, Derived(4));

EXPECT_EQ(p->value(), 4);
EXPECT_EQ(p.get_allocator().tag, 1);
}

TEST(PolymorphicTest, TemplateArgumentDeductionWithDeducedAllocatorAndCopy) {
xyz::TaggedAllocator<Derived> a(1);
xyz::polymorphic p(std::allocator_arg, a, Derived(4));
xyz::polymorphic pp(std::allocator_arg, 2, p);

EXPECT_EQ(pp->value(), 4);
EXPECT_EQ(pp.get_allocator().tag, 2);
}

TEST(PolymorphicTest, TemplateArgumentDeductionWithDeducedAllocatorAndMove) {
xyz::TaggedAllocator<Derived> a(1);
xyz::polymorphic p(std::allocator_arg, a, Derived(4));
xyz::polymorphic pp(std::allocator_arg, 2, std::move(p));

EXPECT_EQ(pp->value(), 4);
EXPECT_EQ(pp.get_allocator().tag, 2);
}

#endif // XYZ_HAS_TEMPLATE_ARGUMENT_DEDUCTION

} // namespace
Loading