Skip to content

Instantly share code, notes, and snippets.

@royvandam
Last active February 12, 2020 08:16
Show Gist options
  • Save royvandam/f8656f50e7cd847f5bb6e1ca96ab68c1 to your computer and use it in GitHub Desktop.
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.
#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);
}
#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