Skip to content

Commit 3592b9d

Browse files
authored
Improved API by wrapping more std::vector functions (#12)
* clear/is_empty added * added capacity, reserve, resize * added all_of, any_of, none_of * improved layout * return instance instead of void for more fluent api * Update README.md
1 parent e73fa33 commit 3592b9d

File tree

3 files changed

+293
-11
lines changed

3 files changed

+293
-11
lines changed

README.md

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,3 +144,67 @@ numbers.insert_front(functional_vector({4, -6, 7}));
144144
// numbers -> functional_vector<int>({4, -6, 7, -10, 4, 8, 3, -2, 5, 2, 7, 9, 7, 3});
145145
numbers.insert_back(std::initializer_list({7, 3}));
146146
```
147+
148+
### size/capacity, reserve/resize
149+
```c++
150+
#include "functional_vector.h" // instead of <vector>
151+
152+
// numbers.capacity() = 9
153+
// numbers.size() = 9
154+
functional_vector<int> numbers({1, 4, 2, 5, 8, 3, 1, 7, 1});
155+
156+
// numbers -> functional_vector<int>({1, 4, 2, 5, 8});
157+
// numbers.capacity() = 9
158+
// numbers.size() = 5
159+
numbers.resize(5);
160+
161+
// numbers -> functional_vector<int>({1, 4, 2, 5, 8, 0, 0});
162+
// numbers.capacity() = 9
163+
// numbers.size() = 7
164+
numbers.resize(7);
165+
166+
// empty_numbers.capacity() = 0
167+
// empty_numbers.size() = 0
168+
functional_vector<int> empty_numbers;
169+
170+
// empty_numbers.capacity() = 5
171+
// empty_numbers.size() = 0
172+
empty_numbers.reserve(5);
173+
```
174+
175+
### all_of, any_of, none_of
176+
```c++
177+
#include "functional_vector.h" // instead of <vector>
178+
179+
functional_vector<int> numbers({1, 4, 2, 5, 8, 3, 1, 7, 1});
180+
181+
// returns true
182+
numbers.all_of([](const auto &number) {
183+
return number < 10;
184+
});
185+
186+
// returns false
187+
numbers.all_of([](const auto &number) {
188+
return number > 2;
189+
});
190+
191+
// returns true
192+
numbers.any_of([](const auto &number) {
193+
return number < 5;
194+
});
195+
196+
// returns false
197+
numbers.any_of([](const auto &number) {
198+
return number > 9;
199+
});
200+
201+
// returns true
202+
numbers.none_of([](const auto &number) {
203+
return number < -2;
204+
});
205+
206+
// returns false
207+
numbers.none_of([](const auto &number) {
208+
return number > 7;
209+
});
210+
```

include/functional_vector.h

Lines changed: 132 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,9 @@ class functional_vector
4646
: backing_vector_(vector)
4747
{
4848
}
49-
49+
5050
explicit functional_vector(std::vector<T>&& vector)
51-
: backing_vector_(std::move(vector))
51+
: backing_vector_(std::move(vector))
5252
{
5353
}
5454
explicit functional_vector(std::initializer_list<T> list)
@@ -86,6 +86,72 @@ class functional_vector
8686
return functional_vector<U>(transformed_vector);
8787
}
8888

89+
// Returns true if all elements match the predicate (return true)
90+
//
91+
// example:
92+
// functional_vector<int> numbers({1, 4, 2, 5, 8, 3, 1, 7, 1});
93+
//
94+
// // returns true
95+
// numbers.all_of([](const auto &number) {
96+
// return number < 10;
97+
// });
98+
//
99+
// // returns false
100+
// numbers.all_of([](const auto &number) {
101+
// return number > 2;
102+
// });
103+
template <typename Callable, typename = std::enable_if_t<std::is_invocable_r<bool, Callable, T>::value>>
104+
bool all_of(Callable && unary_predicate) const
105+
{
106+
return std::all_of(cbegin(),
107+
cend(),
108+
std::forward<Callable>(unary_predicate));
109+
}
110+
111+
// Returns true if at least one of the elements matches the predicate (returns true)
112+
//
113+
// example:
114+
// functional_vector<int> numbers({1, 4, 2, 5, 8, 3, 1, 7, 1});
115+
//
116+
// // returns true
117+
// numbers.any_of([](const auto &number) {
118+
// return number < 5;
119+
// });
120+
//
121+
// // returns false
122+
// numbers.any_of([](const auto &number) {
123+
// return number > 9;
124+
// });
125+
template <typename Callable, typename = std::enable_if_t<std::is_invocable_r<bool, Callable, T>::value>>
126+
bool any_of(Callable && unary_predicate) const
127+
{
128+
return std::any_of(cbegin(),
129+
cend(),
130+
std::forward<Callable>(unary_predicate));
131+
}
132+
133+
// Returns true if no element matches the predicate (all return false)
134+
//
135+
// example:
136+
// functional_vector<int> numbers({1, 4, 2, 5, 8, 3, 1, 7, 1});
137+
//
138+
// // returns true
139+
// numbers.none_of([](const auto &number) {
140+
// return number < -2;
141+
// });
142+
//
143+
// // returns false
144+
// numbers.none_of([](const auto &number) {
145+
// return number > 7;
146+
// });
147+
template <typename Callable, typename = std::enable_if_t<std::is_invocable_r<bool, Callable, T>::value>>
148+
bool none_of(Callable && unary_predicate) const
149+
{
150+
return std::none_of(cbegin(),
151+
cend(),
152+
std::forward<Callable>(unary_predicate));
153+
}
154+
89155
// Performs the functional `filter` algorithm, in which all elements of this instance
90156
// which match the given predicate are kept (mutating)
91157
//
@@ -194,7 +260,7 @@ class functional_vector
194260
struct is_valid_iterator {
195261
static bool const value = std::is_constructible_v<deref_type<Iterator>>;
196262
};
197-
263+
198264
// Performs the functional `zip` algorithm, in which every element of the resulting vector is a
199265
// tuple of this instance's element (first) and the second vector's element (second) at the same
200266
// index. The sizes of the two vectors must be equal.
@@ -427,8 +493,8 @@ class functional_vector
427493
[[nodiscard]] std::optional<size_t> find_first_index(const T& element) const
428494
{
429495
if (auto const it = std::find(backing_vector_.cbegin(),
430-
backing_vector_.cend(),
431-
element); it != backing_vector_.cend())
496+
backing_vector_.cend(),
497+
element); it != backing_vector_.cend())
432498
{
433499
return std::distance(backing_vector_.cbegin(), it);
434500
}
@@ -451,8 +517,8 @@ class functional_vector
451517
[[nodiscard]] std::optional<size_t> find_last_index(const T& element) const
452518
{
453519
if (auto const it = std::find(backing_vector_.crbegin(),
454-
backing_vector_.crend(),
455-
element); it != backing_vector_.crend())
520+
backing_vector_.crend(),
521+
element); it != backing_vector_.crend())
456522
{
457523
return std::distance(it, backing_vector_.crend()) - 1;
458524
}
@@ -1056,18 +1122,73 @@ class functional_vector
10561122
return backing_vector_.size();
10571123
}
10581124

1125+
// Clears the vector by removing all elements (mutating)
1126+
functional_vector& clear()
1127+
{
1128+
backing_vector_.clear();
1129+
return *this;
1130+
}
1131+
1132+
// Returns true if the vector has no elements
1133+
bool is_empty() const
1134+
{
1135+
return size() == 0;
1136+
}
1137+
1138+
// Returns the underlying capacity of the vector, which can be larger from its size
1139+
size_t capacity() const
1140+
{
1141+
return backing_vector_.capacity();
1142+
}
1143+
1144+
// Reserves the necessary memory for `count` elements, so that subsequent changes in the
1145+
// vector's size due to addition/removal of elements is more performant
1146+
functional_vector& reserve(size_t count)
1147+
{
1148+
backing_vector_.reserve(count);
1149+
return *this;
1150+
}
1151+
1152+
// Resizes the vector to have given number of elements
1153+
// If `count` is larger than the current `size`, then `count-size` default elements are inserted at the back
1154+
// If `count` is smaller than the current `size`, then the last `size - count` elements are removed
1155+
//
1156+
// example:
1157+
// // numbers.capacity() = 9
1158+
// // numbers.size() = 9
1159+
// functional_vector<int> numbers({1, 4, 2, 5, 8, 3, 1, 7, 1});
1160+
1161+
// // numbers.capacity() = 9
1162+
// // numbers.size() = 5
1163+
// // numbers -> functional_vector<int>({1, 4, 2, 5, 8});
1164+
// numbers.resize(5);
1165+
//
1166+
// // empty_numbers.capacity() = 0
1167+
// // empty_numbers.size() = 0
1168+
// functional_vector<int> empty_numbers;
1169+
//
1170+
// // empty_numbers.capacity() = 5
1171+
// // empty_numbers.size() = 5
1172+
// // empty_numbers -> functional_vector<int>({0, 0, 0, 0, 0});
1173+
// empty_numbers.resize(5);
1174+
functional_vector& resize(size_t count)
1175+
{
1176+
backing_vector_.resize(count);
1177+
return *this;
1178+
}
1179+
10591180
// Returns the begin iterator, useful for other standard library algorithms
10601181
[[nodiscard]] typename std::vector<T>::iterator begin()
10611182
{
10621183
return backing_vector_.begin();
10631184
}
1064-
1185+
10651186
// Returns the const begin iterator, useful for other standard library algorithms
10661187
[[nodiscard]] typename std::vector<T>::const_iterator cbegin() const
10671188
{
10681189
return backing_vector_.begin();
10691190
}
1070-
1191+
10711192
// Returns the end iterator, useful for other standard library algorithms
10721193
[[nodiscard]] typename std::vector<T>::iterator end()
10731194
{
@@ -1160,10 +1281,10 @@ class functional_vector
11601281

11611282
template<typename Iterator, typename = std::enable_if_t<is_valid_iterator<Iterator>::value>>
11621283
[[nodiscard]] auto zip_impl( const Iterator& vec_begin, const Iterator & vec_end) const ->
1163-
functional_vector<functional_pair<deref_type<Iterator>>>
1284+
functional_vector<functional_pair<deref_type<Iterator>>>
11641285
{
11651286
using U = deref_type<Iterator>;
1166-
1287+
11671288
const auto vec_size = std::distance(vec_begin, vec_end);
11681289
assert(backing_vector_.size() == vec_size);
11691290
std::vector<functional_pair<U>> combined_vector;

tests/functional_vector_test.cc

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1012,3 +1012,100 @@ TEST(FunctionalVectorTest, EqualityOperatorEqualVectorsTest)
10121012
EXPECT_TRUE(vec1 == vec2);
10131013
EXPECT_FALSE(vec1 != vec2);
10141014
}
1015+
1016+
TEST(FunctionalVectorTest, ClearEmptyVectorTest)
1017+
{
1018+
functional_vector<int> vector_under_test;
1019+
EXPECT_TRUE(vector_under_test.is_empty());
1020+
vector_under_test.clear();
1021+
EXPECT_EQ(0, vector_under_test.size());
1022+
EXPECT_TRUE(vector_under_test.is_empty());
1023+
}
1024+
1025+
TEST(FunctionalVectorTest, ClearFilledVectorTest)
1026+
{
1027+
functional_vector vector_under_test({5, -3, 4, -9});
1028+
EXPECT_FALSE(vector_under_test.is_empty());
1029+
vector_under_test.clear();
1030+
EXPECT_EQ(0, vector_under_test.size());
1031+
EXPECT_TRUE(vector_under_test.is_empty());
1032+
}
1033+
1034+
TEST(FunctionalVectorTest, CapacityReserveClearTest)
1035+
{
1036+
functional_vector<int> vector_under_test;
1037+
EXPECT_EQ(0, vector_under_test.capacity());
1038+
EXPECT_EQ(0, vector_under_test.size());
1039+
1040+
vector_under_test.reserve(5);
1041+
EXPECT_EQ(5, vector_under_test.capacity());
1042+
EXPECT_EQ(0, vector_under_test.size());
1043+
1044+
vector_under_test.insert_back({1, 4, -5, 2});
1045+
EXPECT_EQ(5, vector_under_test.capacity());
1046+
EXPECT_EQ(4, vector_under_test.size());
1047+
1048+
vector_under_test.clear();
1049+
EXPECT_EQ(5, vector_under_test.capacity());
1050+
EXPECT_EQ(0, vector_under_test.size());
1051+
}
1052+
1053+
TEST(FunctionalVectorTest, ResizeEmptyVectorTest)
1054+
{
1055+
functional_vector<int> vector_under_test;
1056+
vector_under_test.resize(5);
1057+
EXPECT_EQ(functional_vector({ 0, 0, 0, 0, 0 }), vector_under_test);
1058+
EXPECT_EQ(5, vector_under_test.capacity());
1059+
EXPECT_EQ(5, vector_under_test.size());
1060+
}
1061+
1062+
TEST(FunctionalVectorTest, ResizeSmallerThanCurrentSizeTest)
1063+
{
1064+
functional_vector<int> vector_under_test({1, 4, 2, 5, 8, 3, 1, 7, 1});
1065+
EXPECT_EQ(9, vector_under_test.capacity());
1066+
EXPECT_EQ(9, vector_under_test.size());
1067+
1068+
vector_under_test.resize(5);
1069+
EXPECT_EQ(functional_vector({1, 4, 2, 5, 8}), vector_under_test);
1070+
EXPECT_EQ(9, vector_under_test.capacity());
1071+
EXPECT_EQ(5, vector_under_test.size());
1072+
}
1073+
1074+
TEST(FunctionalVectorTest, AllOfFalseTest)
1075+
{
1076+
functional_vector<int> vector_under_test({1, 4, 2, 5, 8, 3, 1, 7, 1});
1077+
EXPECT_FALSE(vector_under_test.all_of([](const auto &number) { return number > 5; }));
1078+
}
1079+
1080+
TEST(FunctionalVectorTest, AllOfTrueTest)
1081+
{
1082+
functional_vector<int> vector_under_test({1, 4, 2, 5, 8, 3, 1, 7, 1});
1083+
vector_under_test.all_of([](const auto &number) {
1084+
return number < 10;
1085+
});
1086+
EXPECT_TRUE(vector_under_test.all_of([](const auto &number) { return number < 10; }));
1087+
}
1088+
1089+
TEST(FunctionalVectorTest, AnyOfFalseTest)
1090+
{
1091+
functional_vector<int> vector_under_test({1, 4, 2, 5, 8, 3, 1, 7, 1});
1092+
EXPECT_FALSE(vector_under_test.any_of([](const auto &number) { return number > 20; }));
1093+
}
1094+
1095+
TEST(FunctionalVectorTest, AnyOfTrueTest)
1096+
{
1097+
functional_vector<int> vector_under_test({1, 4, 2, 5, 8, 3, 1, 7, 1});
1098+
EXPECT_TRUE(vector_under_test.any_of([](const auto &number) { return number >= 7; }));
1099+
}
1100+
1101+
TEST(FunctionalVectorTest, NoneOfFalseTest)
1102+
{
1103+
functional_vector<int> vector_under_test({1, 4, 2, 5, 8, 3, 1, 7, 1});
1104+
EXPECT_FALSE(vector_under_test.none_of([](const auto &number) { return number > 7; }));
1105+
}
1106+
1107+
TEST(FunctionalVectorTest, NoneOfTrueTest)
1108+
{
1109+
functional_vector<int> vector_under_test({1, 4, 2, 5, 8, 3, 1, 7, 1});
1110+
EXPECT_TRUE(vector_under_test.none_of([](const auto &number) { return number < -2; }));
1111+
}

0 commit comments

Comments
 (0)