Skip to content

Commit c0e67fc

Browse files
committed
Add Array::with_values() that takes varargs for each value
1 parent b0491cc commit c0e67fc

File tree

2 files changed

+81
-39
lines changed

2 files changed

+81
-39
lines changed

containers/array.h

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
#include <stdint.h>
1818

19+
#include <concepts>
1920
#include <type_traits>
2021
#include <utility> // TODO: replace std::make_index_sequence.
2122

@@ -69,8 +70,7 @@ class Array final {
6970

7071
template <class InitializerFn>
7172
constexpr static Array with_initializer(InitializerFn f) noexcept {
72-
return Array(kWithInitializer, ::sus::move(f),
73-
std::make_index_sequence<N>());
73+
return Array(kWithInitializer, move(f), std::make_index_sequence<N>());
7474
}
7575

7676
constexpr static Array with_value(const T& t) noexcept
@@ -79,18 +79,30 @@ class Array final {
7979
return Array(kWithValue, t, std::make_index_sequence<N>());
8080
}
8181

82-
constexpr const T& get(size_t i) const& noexcept
82+
// Uses convertible_to<T> instead of same_as<T> to accept `sus::into()`
83+
// values. But doesn't use sus::construct::Into<T> to avoid implicit
84+
// conversions.
85+
template <std::convertible_to<T>... Ts>
86+
requires(sizeof...(Ts) == N)
87+
constexpr static Array with_values(Ts... ts) noexcept {
88+
auto a = Array(kWithUninitialized);
89+
init_values(a.as_ptr_mut(), 0, move(ts)...);
90+
return a;
91+
}
92+
93+
public:
94+
constexpr const T& get(/* TODO: usize */ size_t i) const& noexcept
8395
requires(N > 0)
8496
{
85-
::sus::check(i < N);
97+
check(i < N);
8698
return storage_.data_[i];
8799
}
88-
constexpr const T& get(size_t i) && = delete;
100+
constexpr const T& get(/* TODO: usize */ size_t i) && = delete;
89101

90-
constexpr T& get_mut(size_t i) & noexcept
102+
constexpr T& get_mut(/* TODO: usize */ size_t i) & noexcept
91103
requires(N > 0)
92104
{
93-
::sus::check(i < N);
105+
check(i < N);
94106
return storage_.data_[i];
95107
}
96108

@@ -122,14 +134,24 @@ class Array final {
122134
template <size_t... Is>
123135
constexpr Array(WithUninitialized) noexcept {}
124136

137+
template <std::convertible_to<T> T1, std::convertible_to<T>... Ts>
138+
static inline void init_values(T* a, size_t index, T1&& t1, Ts&&... ts) {
139+
new (a + index) T(move(t1));
140+
init_values(a, index + 1, move(ts)...);
141+
}
142+
template <std::convertible_to<T> T1>
143+
static inline void init_values(T* a, size_t index, T1&& t1) {
144+
new (a + index) T(move(t1));
145+
}
146+
125147
// Using a union ensures that the default constructor doesn't initialize
126148
// anything.
127149
union {
128150
::sus::containers::__private::Storage<T, N> storage_;
129151
};
130152

131-
sus_class_trivial_relocatable_value(
132-
unsafe_fn, ::sus::mem::relocate_array_by_memcpy<T>);
153+
sus_class_trivial_relocatable_value(unsafe_fn,
154+
::sus::mem::relocate_array_by_memcpy<T>);
133155
};
134156

135157
} // namespace sus::containers

containers/array_unittest.cc

Lines changed: 50 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,10 @@
1616

1717
#include <type_traits>
1818

19+
#include "construct/into.h"
1920
#include "marker/unsafe.h"
2021
#include "mem/relocate.h"
22+
#include "num/types.h"
2123
#include "third_party/googletest/googletest/include/gtest/gtest.h"
2224

2325
using ::sus::containers::Array;
@@ -30,32 +32,32 @@ struct TriviallyRelocatable {
3032
int i;
3133
};
3234

33-
static_assert(!std::is_trivially_constructible_v<Array<int, 2>>, "");
34-
static_assert(!std::is_trivial_v<Array<int, 2>>, "");
35-
static_assert(!std::is_aggregate_v<Array<int, 2>>, "");
36-
static_assert(std::is_standard_layout_v<Array<int, 2>>, "");
35+
static_assert(!std::is_trivially_constructible_v<Array<int, 2>>);
36+
static_assert(!std::is_trivial_v<Array<int, 2>>);
37+
static_assert(!std::is_aggregate_v<Array<int, 2>>);
38+
static_assert(std::is_standard_layout_v<Array<int, 2>>);
3739

3840
// TODO: This covers a trivially relocatable type, but what about the rest?
3941
// (like Option unit tests.)
4042
namespace trivially_relocatable {
4143
using T = TriviallyRelocatable;
42-
static_assert(std::is_move_constructible_v<Array<T, 2>>, "");
43-
static_assert(std::is_move_assignable_v<Array<T, 2>>, "");
44-
static_assert(std::is_trivially_destructible_v<Array<T, 2>>, "");
45-
static_assert(std::is_trivially_move_constructible_v<Array<T, 2>>, "");
46-
static_assert(std::is_trivially_move_assignable_v<Array<T, 2>>, "");
47-
static_assert(std::is_nothrow_swappable_v<Array<T, 2>>, "");
48-
static_assert(std::is_trivially_copy_constructible_v<Array<T, 2>>, "");
49-
static_assert(std::is_trivially_copy_assignable_v<Array<T, 2>>, "");
50-
static_assert(!std::is_constructible_v<Array<T, 2>, T&&>, "");
51-
static_assert(!std::is_assignable_v<Array<T, 2>, T&&>, "");
52-
static_assert(!std::is_constructible_v<Array<T, 2>, const T&>, "");
53-
static_assert(!std::is_assignable_v<Array<T, 2>, const T&>, "");
54-
static_assert(!std::is_constructible_v<Array<T, 2>, T>, "");
55-
static_assert(!std::is_assignable_v<Array<T, 2>, T>, "");
56-
static_assert(std::is_nothrow_destructible_v<Array<T, 2>>, "");
57-
static_assert(relocate_one_by_memcpy<Array<T, 2>>, "");
58-
static_assert(relocate_array_by_memcpy<Array<T, 2>>, "");
44+
static_assert(std::is_move_constructible_v<Array<T, 2>>);
45+
static_assert(std::is_move_assignable_v<Array<T, 2>>);
46+
static_assert(std::is_trivially_destructible_v<Array<T, 2>>);
47+
static_assert(std::is_trivially_move_constructible_v<Array<T, 2>>);
48+
static_assert(std::is_trivially_move_assignable_v<Array<T, 2>>);
49+
static_assert(std::is_nothrow_swappable_v<Array<T, 2>>);
50+
static_assert(std::is_trivially_copy_constructible_v<Array<T, 2>>);
51+
static_assert(std::is_trivially_copy_assignable_v<Array<T, 2>>);
52+
static_assert(!std::is_constructible_v<Array<T, 2>, T&&>);
53+
static_assert(!std::is_assignable_v<Array<T, 2>, T&&>);
54+
static_assert(!std::is_constructible_v<Array<T, 2>, const T&>);
55+
static_assert(!std::is_assignable_v<Array<T, 2>, const T&>);
56+
static_assert(!std::is_constructible_v<Array<T, 2>, T>);
57+
static_assert(!std::is_assignable_v<Array<T, 2>, T>);
58+
static_assert(std::is_nothrow_destructible_v<Array<T, 2>>);
59+
static_assert(relocate_one_by_memcpy<Array<T, 2>>);
60+
static_assert(relocate_array_by_memcpy<Array<T, 2>>);
5961
} // namespace trivially_relocatable
6062

6163
struct NonAggregate {
@@ -64,18 +66,18 @@ struct NonAggregate {
6466

6567
TEST(Array, WithDefault) {
6668
auto a = Array<int, 5>::with_default();
67-
static_assert(sizeof(a) == sizeof(int[5]), "");
69+
static_assert(sizeof(a) == sizeof(int[5]));
6870
for (size_t i = 0; i < 5; ++i) {
6971
EXPECT_EQ(a.get(i), 0);
7072
}
7173

72-
static_assert(sizeof(Array<int, 5>::with_default()) == sizeof(int[5]), "");
74+
static_assert(sizeof(Array<int, 5>::with_default()) == sizeof(int[5]));
7375
}
7476

7577
TEST(Array, Zero) {
7678
auto a = Array<int, 0>::with_default();
77-
static_assert(sizeof(a) == 1, "");
78-
static_assert(sizeof(Array<int, 0>::with_default()) == 1, "");
79+
static_assert(sizeof(a) == 1);
80+
static_assert(sizeof(Array<int, 0>::with_default()) == 1);
7981
}
8082

8183
TEST(Array, WithUninitialized) {
@@ -86,7 +88,7 @@ TEST(Array, WithUninitialized) {
8688

8789
TEST(Array, WithInitializer) {
8890
auto a = Array<int, 5>::with_initializer([i = 1]() mutable { return i++; });
89-
static_assert(sizeof(a) == sizeof(int[5]), "");
91+
static_assert(sizeof(a) == sizeof(int[5]));
9092
for (size_t i = 0; i < 5; ++i) {
9193
EXPECT_EQ(a.get(i), i + 1);
9294
}
@@ -101,14 +103,14 @@ TEST(Array, WithInitializer) {
101103
"");
102104
auto b = Array<NotTriviallyDefaultConstructible, 5>::with_initializer(
103105
[i = 1]() mutable -> NotTriviallyDefaultConstructible { return {i++}; });
104-
static_assert(sizeof(b) == sizeof(NotTriviallyDefaultConstructible[5]), "");
106+
static_assert(sizeof(b) == sizeof(NotTriviallyDefaultConstructible[5]));
105107
for (size_t i = 0; i < 5; ++i) {
106108
EXPECT_EQ(b.get(i).i, i + 1);
107109
}
108110

109111
auto lvalue = [i = 1]() mutable { return i++; };
110112
auto c = Array<int, 5>::with_initializer(lvalue);
111-
static_assert(sizeof(c) == sizeof(int[5]), "");
113+
static_assert(sizeof(c) == sizeof(int[5]));
112114
for (size_t i = 0; i < 5; ++i) {
113115
EXPECT_EQ(c.get(i), i + 1);
114116
}
@@ -120,12 +122,31 @@ TEST(Array, WithInitializer) {
120122

121123
TEST(Array, WithValue) {
122124
auto a = Array<int, 5>::with_value(3);
123-
static_assert(sizeof(a) == sizeof(int[5]), "");
125+
static_assert(sizeof(a) == sizeof(int[5]));
124126
for (size_t i = 0; i < 5; ++i) {
125127
EXPECT_EQ(a.get(i), 3);
126128
}
127129
}
128130

131+
TEST(Array, WithValues) {
132+
{
133+
auto a = Array<int, 5>::with_values(3, 4, 5, 6, 7);
134+
static_assert(sizeof(a) == sizeof(int[5]));
135+
for (size_t i = 0; i < 5; ++i) {
136+
EXPECT_EQ(a.get(i), 3 + i);
137+
}
138+
}
139+
{
140+
auto a = Array<u8, 5>::with_values(sus::into(3), sus::into(4), sus::into(5),
141+
sus::into(6), sus::into(7));
142+
static_assert(sizeof(a) == sizeof(u8[5]));
143+
for (auto i = 0_u8; i < 5_u8; i += 1_u8) {
144+
EXPECT_EQ(a.get(/* TODO: usize */ usize::from(i).primitive_value),
145+
3_u8 + i);
146+
}
147+
}
148+
}
149+
129150
TEST(Array, Get) {
130151
{
131152
constexpr auto r = []() constexpr {
@@ -161,7 +182,6 @@ TEST(Array, GetMut) {
161182
}
162183
}
163184

164-
165185
TEST(Array, AsPtr) {
166186
auto a = Array<int, 5>::with_initializer([i = 0]() mutable { return ++i; });
167187
auto r = a.as_ptr();

0 commit comments

Comments
 (0)