std::ptrdiff_t
is a signed std::size_t
.
I use that. I have heard arguments that is what the standard should have used, at the cost of a single bit of maximum size.
Writing a stride-friendly iterator is a bit of a pain. Here is a sketch:
template<class T, class Stride=std::integral_constant<std::size_t, 1>>
struct stride_iterator {
using difference_type = std::ptrdiff_t;
using value_type = T;
using reference= T&;
private:
T* dataptr = nullptr;
Stride datastride = {};
T* data() const { return dataptr; }
T*& data() { return dataptr; }
Stride stride() const { return datastride; }
public:
explicit stride_iterator( T* ptr, Stride s ):
dataptr(ptr),
datastride{ std::move(s) }
{}
explicit stride_iterator( T* ptr ):
dataptr(ptr)
{}
stride_iterator():stride_iterator(nullptr) {}
stride_iterator(stride_iterator const&)=default;
stride_iterator& operator=(stride_iterator const&)& =default;
stride_iterator(stride_iterator &&)=default;
stride_iterator& operator=(stride_iterator &&)& =default;
T& operator*() const { return *data(); }
T* operator->() const { return data(); }
T& operator[](std::ptrdiff_t n) const {
return *(*this+n);
}
stride_iterator& operator+=( std::ptrdiff_t n )& {
data() += (n*stride());
return *this;
}
stride_iterator& operator-=( std::ptrdiff_t n )& {
data() -= (n*stride());
return *this;
}
friend stride_iterator operator+( std::ptrdiff_t rhs, stride_iterator lhs ) {
return std::move(lhs)+rhs;
}
friend stride_iterator operator+( stride_iterator lhs, std::ptrdiff_t rhs ) {
lhs += rhs;
return lhs;
}
friend stride_iterator operator-( stride_iterator lhs, std::ptrdiff_t rhs ) {
lhs += rhs;
return lhs;
}
stride_iterator& operator++() {
*this += 1;
return *this;
}
stride_iterator& operator--() {
*this -= 1;
return *this;
}
stride_iterator operator++(int) {
auto r = *this;
++*this;
return r;
}
stride_iterator operator--(int) {
auto r = *this;
--*this;
return r;
}
friend std::ptrdiff_t operator-( stride_iterator const& lhs, stride_iterator const& rhs ) {
return (lhs.data()-rhs.data())/stride();
}
friend bool operator<( stride_iterator const& lhs, stride_iterator const& rhs ) {
return lhs.data() < rhs.data();
}
friend bool operator<=( stride_iterator const& lhs, stride_iterator const& rhs ) {
return lhs.data() <= rhs.data();
}
friend bool operator>( stride_iterator const& lhs, stride_iterator const& rhs ) {
return lhs.data() > rhs.data();
}
friend bool operator>=( stride_iterator const& lhs, stride_iterator const& rhs ) {
return lhs.data() >= rhs.data();
}
friend bool operator==( stride_iterator const& lhs, stride_iterator const& rhs ) {
return lhs.data() == rhs.data();
}
friend bool operator!=( stride_iterator const& lhs, stride_iterator const& rhs ) {
return lhs.data() != rhs.data();
}
};
template<class It>
struct range {
It b, e;
It begin() const { return b; }
It end() const { return e; }
decltype(auto) operator[]( std::ptrdiff_t i ) const {
return b[i];
}
std::size_t size() const { return end()-begin(); }
bool empty() const { return end()==begin(); }
};
struct toy_matrix {
std::ptrdiff_t width = 1;
std::ptrdiff_t height = 1;
std::vector<int> data = std::vector<int>( 1 );
toy_matrix() = default;
toy_matrix( std::size_t w, std::size_t h ):width(w),height(h), data(w*h) {}
toy_matrix( std::size_t w, std::size_t h, std::initializer_list<int> il ):width(w),height(h), data(il) {
data.resize(w*h);
}
range<stride_iterator<int>> row( std::ptrdiff_t i ) {
int* ptr = data.data();
ptr += height * i;
return { stride_iterator<int>{ ptr }, stride_iterator<int>{ ptr + width } };
}
range<stride_iterator<int, std::ptrdiff_t>> column( std::ptrdiff_t i ) {
int* ptr = data.data();
ptr += i;
return { stride_iterator<int, std::ptrdiff_t>{ ptr, width }, stride_iterator<int, std::ptrdiff_t>{ ptr + height * width, width } };
}
range<stride_iterator<int>> operator[]( std::ptrdiff_t i ) {
return row(i);
}
int& operator()( std::ptrdiff_t x, std::ptrdiff_t y ) {
return (*this)[x][y];
}
};
Test code:
toy_matrix m{ 5, 4, {
1,2,3,4,5,
10,20,30,40,50,
100,200,300,400,500,
1000,2000,3000,4000,5000,
}};
for (auto x : m.column(0)) {
std::cout << x << "\n";
}
for (auto x : m.column(1)) {
std::cout << x << "\n";
}
for (auto x : m.column(2)) {
std::cout << x << "\n";
}
output:
1
10
100
1000
2
20
200
2000
3
30
300
3000
Live example.