adt: allow choice between static and dynamic optional vector types

master
Francisco Paisana 3 years ago
parent 1596fcf349
commit d616e46936

@ -22,6 +22,8 @@ template <typename T>
class optional class optional
{ {
public: public:
using value_type = T;
optional() : has_val_(false) {} optional() : has_val_(false) {}
optional(const T& t) : has_val_(true) { storage.emplace(t); } optional(const T& t) : has_val_(true) { storage.emplace(t); }
optional(T&& t) : has_val_(true) { storage.emplace(std::move(t)); } optional(T&& t) : has_val_(true) { storage.emplace(std::move(t)); }

@ -19,16 +19,15 @@
namespace srsran { namespace srsran {
/** namespace detail {
* Array of optional items. The iteration is in order of indexes and correctly skips non-present items
* Pointer/References/Iterators remain valid throughout the object lifetime template <typename Vec>
* NOTE: The sorted iteration and pointer validation guarantees add some overhead if the array is very fragmented class base_optional_vector
* @tparam T type of objects
* @tparam N static size of max nof items
*/
template <typename T, size_t N>
class optional_array
{ {
using base_t = base_optional_vector<Vec>;
using T = typename Vec::value_type::value_type;
protected:
template <typename Obj> template <typename Obj>
class iterator_impl class iterator_impl
{ {
@ -42,22 +41,22 @@ class optional_array
using reference = Obj&; using reference = Obj&;
iterator_impl() = default; iterator_impl() = default;
iterator_impl(optional_array<T, N>* parent_, size_t idx_) : parent(parent_), idx(idx_) iterator_impl(base_t* parent_, size_t idx_) : parent(parent_), idx(idx_)
{ {
if (idx < parent->capacity() and not parent->contains(idx)) { if (idx < parent->vec.size() and not parent->contains(idx)) {
++(*this); ++(*this);
} }
} }
It& operator++() It& operator++()
{ {
while (++idx < parent->capacity() and not parent->contains(idx)) { while (++idx < parent->vec.size() and not parent->contains(idx)) {
} }
return *this; return *this;
} }
It& operator--() It& operator--()
{ {
while (--idx < parent->capacity() and not parent->contains(idx)) { while (--idx < parent->vec.size() and not parent->contains(idx)) {
} }
return *this; return *this;
} }
@ -69,24 +68,27 @@ class optional_array
bool operator!=(const It& other) const { return not(*this == other); } bool operator!=(const It& other) const { return not(*this == other); }
protected: protected:
friend class optional_array<T, N>; friend base_t;
optional_array<T, N>* parent = nullptr; base_t* parent = nullptr;
size_t idx = N; size_t idx = std::numeric_limits<size_t>::max();
}; };
size_t nof_elems = 0;
Vec vec;
public: public:
using iterator = iterator_impl<T>; using iterator = iterator_impl<T>;
using const_iterator = iterator_impl<const T>; using const_iterator = iterator_impl<const T>;
optional_array() = default; base_optional_vector() = default;
optional_array(const optional_array&) = default; base_optional_vector(const base_optional_vector&) = default;
optional_array(optional_array&& other) noexcept : vec(std::move(other.vec)), nof_elems(other.nof_elems) base_optional_vector(base_optional_vector&& other) noexcept : vec(std::move(other.vec)), nof_elems(other.nof_elems)
{ {
other.nof_elems = 0; other.nof_elems = 0;
} }
optional_array& operator=(const optional_array&) = default; base_optional_vector& operator=(const base_optional_vector&) = default;
optional_array& operator =(optional_array&& other) noexcept base_optional_vector& operator =(base_optional_vector&& other) noexcept
{ {
vec = std::move(other.vec); vec = std::move(other.vec);
nof_elems = other.nof_elems; nof_elems = other.nof_elems;
@ -97,22 +99,19 @@ public:
// Find first position that is empty // Find first position that is empty
size_t find_first_empty(size_t start_guess = 0) size_t find_first_empty(size_t start_guess = 0)
{ {
if (nof_elems == capacity()) { if (nof_elems == vec.size()) {
return N; return vec.size();
} }
for (size_t i = start_guess; i < N; ++i) { for (size_t i = start_guess; i < vec.size(); ++i) {
if (not vec[i].has_value()) { if (not vec[i].has_value()) {
return i; return i;
} }
} }
return N; return vec.size();
}
template <typename U>
void insert(size_t idx, U&& u)
{
nof_elems += contains(idx) ? 0 : 1;
vec[idx] = std::forward<U>(u);
} }
bool contains(size_t idx) const { return idx < vec.size() and vec[idx].has_value(); }
void erase(size_t idx) void erase(size_t idx)
{ {
if (contains(idx)) { if (contains(idx)) {
@ -129,23 +128,62 @@ public:
} }
} }
bool contains(size_t idx) const { return idx < N and vec[idx].has_value(); }
T& operator[](size_t idx) { return *vec[idx]; } T& operator[](size_t idx) { return *vec[idx]; }
const T& operator[](size_t idx) const { return *vec[idx]; } const T& operator[](size_t idx) const { return *vec[idx]; }
bool empty() const { return nof_elems == 0; } bool empty() const { return nof_elems == 0; }
size_t size() const { return nof_elems; } size_t size() const { return nof_elems; }
static size_t capacity() { return N; }
iterator begin() { return iterator{this, 0}; } iterator begin() { return iterator{this, 0}; }
iterator end() { return iterator{this, N}; } iterator end() { return iterator{this, vec.size()}; }
const_iterator begin() const { return const_iterator{this, 0}; } const_iterator begin() const { return const_iterator{this, 0}; }
const_iterator end() const { return const_iterator{this, N}; } const_iterator end() const { return const_iterator{this, vec.size()}; }
};
private: } // namespace detail
size_t nof_elems = 0;
std::array<optional<T>, N> vec; /**
* Array of optional items. The iteration is in order of indexes and correctly skips non-present items
* Pointer/References/Iterators remain valid throughout the object lifetime
* NOTE: The sorted iteration and pointer validation guarantees add some overhead if the array is very fragmented
* @tparam T type of objects
* @tparam N static size of max nof items
*/
template <typename T, size_t N>
class optional_array : public detail::base_optional_vector<std::array<optional<T>, N> >
{
using base_t = detail::base_optional_vector<std::array<optional<T>, N> >;
public:
template <typename U>
void insert(size_t idx, U&& u)
{
this->nof_elems += this->contains(idx) ? 0 : 1;
this->vec[idx] = std::forward<U>(u);
}
};
/**
* Contrarily to optional_array, this class may allocate and cause pointer/reference/iterator invalidation.
* However, the indexes will remain valid.
* @tparam T
*/
template <typename T>
class optional_vector : public detail::base_optional_vector<std::vector<optional<T> > >
{
using base_t = detail::base_optional_vector<std::vector<optional<T> > >;
public:
/// May allocate and cause pointer invalidation
template <typename U>
void insert(size_t idx, U&& u)
{
if (not this->contains(idx)) {
this->nof_elems++;
this->vec.resize(std::max(idx + 1, this->vec.size()));
}
this->vec[idx] = std::forward<U>(u);
}
}; };
} // namespace srsran } // namespace srsran

@ -15,7 +15,7 @@
namespace srsran { namespace srsran {
void test_slot_table() void test_optional_array()
{ {
optional_array<int, 5> table1; optional_array<int, 5> table1;
TESTASSERT(table1.size() == 0 and table1.empty()); TESTASSERT(table1.size() == 0 and table1.empty());
@ -42,6 +42,33 @@ void test_slot_table()
TESTASSERT(table1.size() == 1); TESTASSERT(table1.size() == 1);
} }
void test_optional_vector()
{
optional_vector<int> table1;
TESTASSERT(table1.size() == 0 and table1.empty());
TESTASSERT(not table1.contains(0));
table1.insert(0, 5);
TESTASSERT(table1.size() == 1 and not table1.empty());
table1.erase(0);
TESTASSERT(table1.size() == 0 and table1.empty());
table1.insert(1, 3);
table1.insert(4, 2);
TESTASSERT(table1.size() == 2);
TESTASSERT(table1[4] == 2 and table1[1] == 3);
size_t count = 0;
int array[] = {3, 2};
for (int e : table1) {
TESTASSERT(array[count++] == e);
}
auto it = table1.begin();
TESTASSERT(*it == 3);
table1.erase(it);
TESTASSERT(table1.size() == 1);
}
} // namespace srsran } // namespace srsran
int main(int argc, char** argv) int main(int argc, char** argv)
@ -51,7 +78,8 @@ int main(int argc, char** argv)
srsran::test_init(argc, argv); srsran::test_init(argc, argv);
srsran::test_slot_table(); srsran::test_optional_array();
srsran::test_optional_vector();
printf("Success\n"); printf("Success\n");
return SRSRAN_SUCCESS; return SRSRAN_SUCCESS;

Loading…
Cancel
Save