Last active
February 12, 2020 08:16
-
-
Save royvandam/f8656f50e7cd847f5bb6e1ca96ab68c1 to your computer and use it in GitHub Desktop.
array_view<T> a C style pointer array wrapper class with bounds checking, similar to std::span which is introduced in C++20.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#pragma once | |
#include <algorithm> | |
#include <iterator> | |
#include <stdexcept> | |
template <typename T> | |
class ArrayView | |
{ | |
public: | |
typedef T value_type; | |
typedef std::size_t size_type; | |
typedef const T& const_reference; | |
typedef const T* const_pointer; | |
typedef const T* const_iterator; | |
typedef std::reverse_iterator<const_iterator> const_reverse_iterator; | |
ArrayView() : data_(nullptr), size_(0) {} | |
ArrayView(const T* data, size_type size) : data_(data), size_(size) { | |
if (data_ == nullptr && size_ != 0) { | |
throw std::invalid_argument("pointer to array may not be null if the size != 0"); | |
} | |
} | |
ArrayView(std::initializer_list<T> init) : data_(init.begin()), size_(init.size()) {} | |
size_type size() const { return size_; } | |
const_pointer data() const { return data_; } | |
const_reference front() const { return data_[0]; } | |
const_reference back() const { return data_[size_ - 1]; } | |
const_iterator begin() const { return &data_[0]; } | |
const_iterator cbegin() const { return &data_[0]; } | |
const_iterator end() const { return &data_[size_]; } | |
const_iterator cend() const { return &data_[size_]; } | |
const_reverse_iterator rbegin() const { return const_reverse_iterator(&data_[size_]); } | |
const_reverse_iterator crbegin() const { return const_reverse_iterator(&data_[size_]); } | |
const_reverse_iterator rend() const { return const_reverse_iterator(&data_[0]); } | |
const_reverse_iterator crend() const { return const_reverse_iterator(&data_[0]); } | |
const_reference operator[](size_type i) const { return data_[i]; } | |
const_reference at(size_t i) const { | |
if (i >= size_) { | |
throw std::out_of_range("invalid access to pointer array"); | |
} | |
return data_[i]; | |
} | |
ArrayView<T> sub(size_type offset) const { return sub(offset, size_ - offset); } | |
ArrayView<T> sub(size_type offset, size_type length) const { | |
if (offset + length - 1 >= size_) { | |
throw std::out_of_range("invalid slice of pointer array"); | |
} | |
return ArrayView<T>(data_ + offset, length); | |
} | |
protected: | |
const T* data_; | |
const size_type size_; | |
}; | |
template <typename T> | |
bool | |
operator==(const ArrayView<T>& lhs, const ArrayView<T>& rhs) { | |
return (lhs.size() == rhs.size()) && std::equal(lhs.begin(), lhs.end(), rhs.begin()); | |
} | |
template <typename T> | |
bool | |
operator!=(const ArrayView<T>& lhs, const ArrayView<T>& rhs) { | |
return !(lhs == rhs); | |
} | |
template <typename T> | |
bool | |
operator<(const ArrayView<T>& lhs, const ArrayView<T>& rhs) { | |
return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); | |
} | |
template <typename T> | |
bool | |
operator>(const ArrayView<T>& lhs, const ArrayView<T>& rhs) { | |
return rhs < lhs; | |
} | |
template <typename T> | |
bool | |
operator<=(const ArrayView<T>& lhs, const ArrayView<T>& rhs) { | |
return !(lhs > lhs); | |
} | |
template <typename T> | |
bool | |
operator>=(const ArrayView<T>& lhs, const ArrayView<T>& rhs) { | |
return !(lhs < lhs); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <array_view.hpp> | |
#include <gtest/gtest.h> | |
namespace { | |
class ArrayViewTestFixture : public ::testing::Test | |
{ | |
protected: | |
void SetUp() override { | |
array.assign({0, 1, 2, 3, 4, 5}); | |
shorter.assign(array.begin(), array.end() - 1); | |
longer.assign(array.begin(), array.end()); | |
longer.push_back(7); | |
inverse.assign(array.rbegin(), array.rend()); | |
} | |
void TearDown() override { | |
array.clear(); | |
shorter.clear(); | |
longer.clear(); | |
inverse.clear(); | |
} | |
std::vector<uint8_t> array; | |
std::vector<uint8_t> shorter; | |
std::vector<uint8_t> longer; | |
std::vector<uint8_t> inverse; | |
}; | |
TEST_F(ArrayViewTestFixture, ConstructionValid) { | |
auto av = ArrayView<uint8_t>(array.data(), array.size()); | |
EXPECT_EQ(array.data(), av.data()); | |
EXPECT_EQ(array.size(), av.size()); | |
auto av_empty = ArrayView<uint8_t>(); | |
EXPECT_EQ(nullptr, av_empty.data()); | |
EXPECT_EQ(0, av_empty.size()); | |
auto av_init = ArrayView<uint8_t>({0, 1, 2, 3, 4}); | |
EXPECT_NE(nullptr, av_init.data()); | |
EXPECT_EQ(5, av_init.size()); | |
EXPECT_EQ(2, av_init.at(2)); | |
} | |
TEST_F(ArrayViewTestFixture, ConstructionEmpty) { | |
auto av_empty = ArrayView<uint8_t>(); | |
EXPECT_EQ(nullptr, av_empty.data()); | |
EXPECT_EQ(0, av_empty.size()); | |
auto av_init = ArrayView<uint8_t>({}); | |
EXPECT_EQ(0, av_init.size()); | |
EXPECT_NO_THROW(ArrayView<uint8_t>(nullptr, 0)); | |
EXPECT_THROW(ArrayView<uint8_t>(nullptr, 42), std::invalid_argument); | |
} | |
TEST_F(ArrayViewTestFixture, Access) { | |
auto av = ArrayView<uint8_t>(array.data(), array.size()); | |
EXPECT_EQ(array.front(), av.front()); | |
EXPECT_EQ(array.back(), av.back()); | |
EXPECT_EQ(array[3], av[3]); | |
EXPECT_EQ(array.at(3), av.at(3)); | |
EXPECT_THROW(av.at(42), std::out_of_range); | |
} | |
TEST_F(ArrayViewTestFixture, Iterate) { | |
auto av = ArrayView<uint8_t>(array.data(), array.size()); | |
std::size_t index = 0; | |
for (auto& v : av) { | |
EXPECT_EQ(array.at(index), v); | |
index++; | |
} | |
EXPECT_EQ(array.size(), index); | |
} | |
TEST_F(ArrayViewTestFixture, ReverseIterate) { | |
auto av = ArrayView<uint8_t>(array.data(), array.size()); | |
std::size_t index = 0; | |
for (auto it = av.rbegin(); it != av.rend(); it++) { | |
EXPECT_EQ(array.at(array.size() - index - 1), *it); | |
index++; | |
} | |
EXPECT_EQ(array.size(), index); | |
} | |
TEST_F(ArrayViewTestFixture, Slice) { | |
auto av = ArrayView<uint8_t>(array.data(), array.size()); | |
std::size_t offset = 3; | |
std::size_t length = 2; | |
auto av_slice = av.sub(offset, length); | |
EXPECT_EQ(av_slice.size(), length); | |
EXPECT_EQ(av.at(offset), av_slice.front()); | |
EXPECT_EQ(av.at(offset + length - 1), av_slice.back()); | |
std::size_t index = 0; | |
for (auto& v : av_slice) { | |
EXPECT_EQ(array.at(index + offset), v); | |
index++; | |
} | |
EXPECT_EQ(av_slice.size(), index); | |
} | |
TEST_F(ArrayViewTestFixture, SliceTillEnd) { | |
auto av = ArrayView<uint8_t>(array.data(), array.size()); | |
std::size_t offset = 2; | |
std::size_t length = av.size() - offset; | |
auto av_slice = av.sub(offset); | |
EXPECT_EQ(av_slice.size(), length); | |
EXPECT_EQ(av.at(offset), av_slice.front()); | |
EXPECT_EQ(av.back(), av_slice.back()); | |
std::size_t index = 0; | |
for (auto& v : av_slice) { | |
EXPECT_EQ(array.at(index + offset), v); | |
index++; | |
} | |
EXPECT_EQ(av_slice.size(), index); | |
} | |
TEST_F(ArrayViewTestFixture, SliceOutOfRange) { | |
auto av = ArrayView<uint8_t>(array.data(), array.size()); | |
std::size_t offset = 3; | |
std::size_t length = (av.size() - offset) + 1; | |
EXPECT_THROW(av.sub(offset, length), std::out_of_range); | |
} | |
TEST_F(ArrayViewTestFixture, RelationalOperatorsEqual) { | |
auto rhs = ArrayView<uint8_t>(array.data(), array.size()); | |
auto lhs = ArrayView<uint8_t>(array.data(), array.size()); | |
EXPECT_TRUE(lhs == rhs); | |
} | |
TEST_F(ArrayViewTestFixture, RelationalOperatorsNotEqualLength) { | |
auto lhs = ArrayView<uint8_t>(array.data(), array.size()); | |
auto rhs = ArrayView<uint8_t>(shorter.data(), shorter.size()); | |
EXPECT_TRUE(lhs != rhs); | |
} | |
TEST_F(ArrayViewTestFixture, RelationalOperatorsNotEqualContent) { | |
auto lhs = ArrayView<uint8_t>(array.data(), array.size()); | |
auto rhs = ArrayView<uint8_t>(shorter.data(), inverse.size()); | |
EXPECT_TRUE(lhs != rhs); | |
} | |
TEST_F(ArrayViewTestFixture, RelationalOperatorsLexigographical) { | |
auto s = ArrayView<uint8_t>(shorter.data(), shorter.size()); | |
auto r = ArrayView<uint8_t>(array.data(), array.size()); | |
auto l = ArrayView<uint8_t>(longer.data(), longer.size()); | |
EXPECT_TRUE(s < r); | |
EXPECT_TRUE(r < l); | |
} | |
} // namespace |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment