Skip to content

Commit 533b029

Browse files
committed
initial implementation of constexpr
1 parent 2c9fdfc commit 533b029

File tree

3 files changed

+200
-17
lines changed

3 files changed

+200
-17
lines changed

include/beman/inplace_vector/inplace_vector.hpp

Lines changed: 47 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
22

33
#include <algorithm>
4+
#include <array>
45
#include <cassert>
56
#include <compare>
67
#include <concepts>
@@ -42,13 +43,43 @@ using inplace_vector_internal_size_type =
4243
If<Capacity <= std::numeric_limits<uint32_t>::max(), uint32_t,
4344
uint64_t>>>;
4445

46+
// array based storage is used so that we can satisfy constexpr requirement
47+
//
48+
// Selecting this storage type implies: std::is_trivial_v<T>
49+
template <typename T, std::size_t Capacity>
50+
struct inplace_vector_array_based_storage {
51+
std::array<T, Capacity> elems;
52+
53+
constexpr T *begin() { return elems.data(); }
54+
constexpr const T *begin() const { return elems.data(); }
55+
};
56+
57+
// byte array based storage is used for non-constexpr environment, where default
58+
// initialization may not be available.
59+
//
60+
// Selecting this storage type implies: !std::is_trivial_v<T>
61+
template <typename T, std::size_t Capacity>
62+
struct inplace_vector_bytes_based_storage {
63+
alignas(T) unsigned char elems[Capacity * sizeof(T)];
64+
65+
T *begin() { return std::launder(reinterpret_cast<const T *>(elems)); }
66+
const T *begin() const {
67+
return std::launder(reinterpret_cast<const T *>(elems));
68+
}
69+
};
70+
4571
// Base class for inplace_vector
4672
template <typename T, std::size_t Capacity>
4773
struct inplace_vector_destruct_base {
4874
using size_type = std::size_t;
4975
using internal_size_type = inplace_vector_internal_size_type<Capacity>;
5076

51-
alignas(T) unsigned char elems[Capacity * sizeof(T)] = {};
77+
using internal_storage_type =
78+
std::conditional_t<std::is_trivial_v<T>,
79+
inplace_vector_array_based_storage<T, Capacity>,
80+
inplace_vector_bytes_based_storage<T, Capacity>>;
81+
82+
internal_storage_type elems;
5283
internal_size_type size_{0};
5384

5485
// [containers.sequences.inplace.vector.cons], construct/copy/destroy
@@ -162,28 +193,27 @@ struct inplace_vector_base : public inplace_vector_destruct_base<T, Capacity> {
162193
constexpr bool empty() const noexcept { return this->size_ == 0; }
163194

164195
// [containers.sequences.inplace.vector.data], data access
165-
constexpr T *data() noexcept {
166-
return std::launder(reinterpret_cast<T *>(this->elems));
167-
}
168-
constexpr const T *data() const noexcept {
169-
return std::launder(reinterpret_cast<const T *>(this->elems));
170-
}
196+
constexpr T *data() noexcept { return this->elems.begin(); }
197+
constexpr const T *data() const noexcept { return this->elems.begin(); }
171198

172199
// [containers.sequences.inplace.vector.iterators] iterators
173-
iterator begin() noexcept {
174-
return std::launder(reinterpret_cast<T *>(this->elems));
200+
constexpr iterator begin() noexcept {
201+
if constexpr (Capacity == 0)
202+
return nullptr;
203+
else
204+
return this->elems.begin();
175205
}
176-
const_iterator begin() const noexcept {
177-
return std::launder(reinterpret_cast<const T *>(this->elems));
206+
constexpr const_iterator begin() const noexcept {
207+
if constexpr (Capacity == 0)
208+
return nullptr;
209+
else
210+
return this->elems.begin();
178211
}
179212

180-
iterator end() noexcept {
181-
return std::launder(reinterpret_cast<T *>(this->elems) + this->size());
182-
}
213+
constexpr iterator end() noexcept { return begin() + this->size(); }
183214

184-
const_iterator end() const noexcept {
185-
return std::launder(reinterpret_cast<const T *>(this->elems) +
186-
this->size());
215+
constexpr const_iterator end() const noexcept {
216+
return begin() + this->size();
187217
}
188218

189219
// [containers.sequences.inplace.vector.modifiers], modifiers

src/beman/inplace_vector/tests/CMakeLists.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,8 @@ add_executable(beman.inplace_vector.test inplace_vector.test.cpp)
99
target_link_libraries(beman.inplace_vector.test PRIVATE beman.inplace_vector)
1010

1111
add_test(NAME beman.inplace_vector.test COMMAND beman.inplace_vector.test)
12+
13+
# constexpr test
14+
add_executable(beman.inplace_vector.constexpr_test constexpr.test.cpp)
15+
target_link_libraries(beman.inplace_vector.constexpr_test PRIVATE beman.inplace_vector)
16+
add_test(NAME beman.inplace_vector.constexpr_test COMMAND beman.inplace_vector.constexpr_test)
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
#include <beman/inplace_vector/inplace_vector.hpp>
2+
#include <functional>
3+
#include <type_traits>
4+
5+
using namespace beman::inplace_vector;
6+
7+
#define S_ASSERT(EXP) \
8+
do { \
9+
if (!(EXP)) { \
10+
return false; \
11+
} \
12+
} while (0)
13+
14+
struct NonTrivial {
15+
int z = 0;
16+
};
17+
static_assert(!std::is_trivial_v<NonTrivial>);
18+
19+
static_assert(std::invoke([]() {
20+
inplace_vector<int, 0> vec;
21+
22+
// sizes
23+
S_ASSERT(vec.max_size() == 0);
24+
S_ASSERT(vec.capacity() == 0);
25+
S_ASSERT(vec.size() == 0);
26+
S_ASSERT(vec.empty());
27+
28+
// itr
29+
S_ASSERT(vec.begin() == vec.end());
30+
S_ASSERT(vec.cbegin() == vec.cend());
31+
S_ASSERT(vec.rbegin() == vec.rend());
32+
S_ASSERT(vec.crbegin() == vec.crend());
33+
34+
// push_back
35+
S_ASSERT(vec.try_push_back(0) == nullptr);
36+
S_ASSERT(vec.try_emplace_back(0) == nullptr);
37+
38+
return true;
39+
}),
40+
"Should be constexpr for all 0 capacity for trivial type");
41+
42+
static_assert(std::invoke([]() {
43+
inplace_vector<NonTrivial, 0> vec;
44+
45+
// sizes
46+
S_ASSERT(vec.max_size() == 0);
47+
S_ASSERT(vec.capacity() == 0);
48+
S_ASSERT(vec.size() == 0);
49+
S_ASSERT(vec.empty());
50+
51+
// itr
52+
S_ASSERT(vec.begin() == vec.end());
53+
S_ASSERT(vec.cbegin() == vec.cend());
54+
S_ASSERT(vec.rbegin() == vec.rend());
55+
S_ASSERT(vec.crbegin() == vec.crend());
56+
57+
S_ASSERT(vec.try_push_back({}) == nullptr);
58+
59+
return true;
60+
}),
61+
"Should be constexpr for all 0 capacity for non-trivial type");
62+
63+
static_assert(std::invoke([]() {
64+
// sizes
65+
{
66+
inplace_vector<int, 1> vec;
67+
vec.push_back(1);
68+
69+
S_ASSERT(vec.max_size() == 1);
70+
S_ASSERT(vec.capacity() == 1);
71+
S_ASSERT(vec.size() == 1);
72+
S_ASSERT(!vec.empty());
73+
}
74+
75+
// Access
76+
{
77+
78+
inplace_vector<int, 1> vec;
79+
vec.push_back(1);
80+
81+
S_ASSERT(vec[0] == 1);
82+
S_ASSERT(vec.front() == 1);
83+
S_ASSERT(vec.back() == 1);
84+
}
85+
86+
// forward itr
87+
{
88+
inplace_vector<int, 1> vec;
89+
vec.push_back(1);
90+
91+
auto itr = vec.begin();
92+
S_ASSERT(*itr == 1);
93+
itr += 1;
94+
S_ASSERT(itr == vec.end());
95+
}
96+
97+
// backward itr
98+
{
99+
inplace_vector<int, 1> vec;
100+
vec.push_back(1);
101+
102+
auto itr = vec.rbegin();
103+
S_ASSERT(*itr == 1);
104+
itr += 1;
105+
S_ASSERT(itr == vec.rend());
106+
}
107+
108+
// try variant
109+
{
110+
inplace_vector<int, 1> vec;
111+
S_ASSERT(*vec.try_push_back(1) == 1);
112+
S_ASSERT(vec.try_push_back(2) == nullptr);
113+
}
114+
115+
return true;
116+
}),
117+
"Single push_back");
118+
119+
static_assert(std::invoke([]() {
120+
// push pop back
121+
{
122+
inplace_vector<int, 5> vec;
123+
vec.push_back(1);
124+
vec.pop_back();
125+
126+
S_ASSERT(vec.empty());
127+
}
128+
129+
// resize
130+
{
131+
inplace_vector<int, 5> vec;
132+
vec.push_back(1);
133+
vec.push_back(2);
134+
135+
S_ASSERT(vec.size() == 2);
136+
137+
vec.resize(1);
138+
S_ASSERT(vec.size() == 1);
139+
S_ASSERT(vec.back() == 1);
140+
}
141+
142+
return true;
143+
}),
144+
"Single push_back");
145+
146+
int main() {
147+
// compile means pass
148+
}

0 commit comments

Comments
 (0)