util/TerminatedArray: new class
This commit is contained in:
parent
3d995bba5f
commit
9210705598
|
@ -0,0 +1,128 @@
|
||||||
|
// SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
// author: Max Kellermann <max.kellermann@gmail.com>
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <iterator> // for std::iterator_traits
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A container-like class which allows iterating over an array
|
||||||
|
* terminated by sentinel value. Most commonly, this is an array of
|
||||||
|
* pointers terminated by nullptr, but null-terminated C strings can
|
||||||
|
* also be used.
|
||||||
|
*
|
||||||
|
* @param T the type of an item in the aray
|
||||||
|
* @param Sentinel the sentinel value
|
||||||
|
*/
|
||||||
|
template<typename T, T Sentinel>
|
||||||
|
class TerminatedArray {
|
||||||
|
T *head;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A special type for the end iterator which is always equal
|
||||||
|
* the iterator that points to the #Sentinel value.
|
||||||
|
*/
|
||||||
|
struct sentinel {
|
||||||
|
using Traits = std::iterator_traits<T *>;
|
||||||
|
|
||||||
|
using iterator_category = typename Traits::iterator_category;
|
||||||
|
using difference_type = typename Traits::difference_type;
|
||||||
|
using value_type = typename Traits::value_type;
|
||||||
|
using pointer = typename Traits::pointer;
|
||||||
|
using reference = typename Traits::reference;
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
using value_type = T;
|
||||||
|
|
||||||
|
explicit constexpr TerminatedArray(T *_head) noexcept
|
||||||
|
:head(_head) {}
|
||||||
|
|
||||||
|
class iterator {
|
||||||
|
using Traits = std::iterator_traits<T *>;
|
||||||
|
|
||||||
|
T *cursor;
|
||||||
|
|
||||||
|
public:
|
||||||
|
using iterator_category = typename Traits::iterator_category;
|
||||||
|
using difference_type = typename Traits::difference_type;
|
||||||
|
using value_type = typename Traits::value_type;
|
||||||
|
using pointer = typename Traits::pointer;
|
||||||
|
using reference = typename Traits::reference;
|
||||||
|
|
||||||
|
explicit constexpr iterator(T *_cursor) noexcept
|
||||||
|
:cursor(_cursor) {}
|
||||||
|
|
||||||
|
constexpr bool operator==(const iterator &other) const noexcept {
|
||||||
|
return cursor == other.cursor;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr bool operator==(const sentinel &) const noexcept {
|
||||||
|
return *cursor == Sentinel;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr auto &operator++() noexcept {
|
||||||
|
++cursor;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr auto operator++(int) noexcept {
|
||||||
|
auto old = *this;
|
||||||
|
++cursor;
|
||||||
|
return old;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr auto &operator+=(difference_type n) noexcept {
|
||||||
|
cursor += n;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr auto operator+(difference_type n) const noexcept {
|
||||||
|
return iterator{cursor + n};
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr auto &operator--() noexcept {
|
||||||
|
--cursor;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr auto operator--(int) noexcept {
|
||||||
|
auto old = *this;
|
||||||
|
--cursor;
|
||||||
|
return old;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr auto &operator-=(difference_type n) noexcept {
|
||||||
|
cursor -= n;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr auto operator-(difference_type n) const noexcept {
|
||||||
|
return iterator{cursor - n};
|
||||||
|
}
|
||||||
|
|
||||||
|
reference operator*() const noexcept {
|
||||||
|
return *cursor;
|
||||||
|
}
|
||||||
|
|
||||||
|
pointer operator->() const noexcept {
|
||||||
|
return cursor;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr iterator begin() const noexcept {
|
||||||
|
return iterator{head};
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr sentinel end() const noexcept {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr iterator cbegin() const noexcept {
|
||||||
|
return begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr sentinel cend() const noexcept {
|
||||||
|
return end();
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,34 @@
|
||||||
|
// SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
// author: Max Kellermann <max.kellermann@gmail.com>
|
||||||
|
|
||||||
|
#include "util/TerminatedArray.hxx"
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
TEST(TerminatedArray, PointerArray)
|
||||||
|
{
|
||||||
|
const char *const raw_array[] = {"foo", "bar", nullptr};
|
||||||
|
|
||||||
|
TerminatedArray<const char *const, nullptr> array{raw_array};
|
||||||
|
EXPECT_STREQ(*array.begin(), "foo");
|
||||||
|
EXPECT_STREQ(*std::next(array.begin()), "bar");
|
||||||
|
EXPECT_EQ(std::prev(std::next(array.begin())), array.begin());
|
||||||
|
EXPECT_NE(array.begin(), array.end());
|
||||||
|
EXPECT_NE(std::next(array.begin()), array.end());
|
||||||
|
EXPECT_EQ(std::next(array.begin(), 2), array.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TerminatedArray, CSTring)
|
||||||
|
{
|
||||||
|
const char raw_array[] = "abc";
|
||||||
|
|
||||||
|
TerminatedArray<const char, '\0'> array{raw_array};
|
||||||
|
EXPECT_EQ(*array.begin(), 'a');
|
||||||
|
EXPECT_EQ(*std::next(array.begin()), 'b');
|
||||||
|
EXPECT_EQ(*std::next(array.begin(), 2), 'c');
|
||||||
|
EXPECT_EQ(std::prev(std::next(array.begin())), array.begin());
|
||||||
|
EXPECT_NE(array.begin(), array.end());
|
||||||
|
EXPECT_NE(std::next(array.begin()), array.end());
|
||||||
|
EXPECT_NE(std::next(array.begin(), 2), array.end());
|
||||||
|
EXPECT_EQ(std::next(array.begin(), 3), array.end());
|
||||||
|
}
|
|
@ -14,6 +14,7 @@ test(
|
||||||
'TestSplitString.cxx',
|
'TestSplitString.cxx',
|
||||||
'TestStringStrip.cxx',
|
'TestStringStrip.cxx',
|
||||||
'TestTemplateString.cxx',
|
'TestTemplateString.cxx',
|
||||||
|
'TestTerminatedArray.cxx',
|
||||||
'TestUriExtract.cxx',
|
'TestUriExtract.cxx',
|
||||||
'TestUriQueryParser.cxx',
|
'TestUriQueryParser.cxx',
|
||||||
'TestUriRelative.cxx',
|
'TestUriRelative.cxx',
|
||||||
|
|
Loading…
Reference in New Issue