Create a multi-dimensional array with a number of n dimensions - c++

I've been thinking if it is possible to actually let the user decide how many "dimensions" an array should have, based on a number given.
Let n be the number of dimensions, the user will type in its value.
It will create an array with n dimensions.
Example: for n=5, it will create an array called list, like that: int list[size1][size2][size3][size4][size5].
size variables will still be mentioned by the user, but that's actually part 2.
I want to know if I can add more dimensions to an array, after I have declared it. And if not, I want to find a solution to this problem.

The C++ language does not have provision for variable-sized or variable-dimensioned arrays.
You can, however, create a class to encapsulate these behaviors.
The important characteristic is the dimensions. You can use a std::vector<int> to track the number of elements per dimension; for example, {3, 4, 5} to represent a three-dimensional matrix where the rank of the innermost dimension is 3, the middle 4, and the outer 5.
Use a templated vector or deque to allocate space for the elements. The number of elements required is the product of the dimension ranks. (You can use std::accumulate with a multiplication operator to compute this over your ranks vector.)
Next, you'll need a method that takes some object (say, a vector of int) that provides all the indices into the MD-array necessary to access an element. You can provide overloads that take a variable number of arguments using some fancy template metaprogramming.
All of this is overkill outside of some very specialized uses, such as: you are writing Mathematica-like software that allows users to play with these things.

You may be interested in an array class I implemented a few months ago that aims to provide a syntax for arrays that mimics that of matlab arrays. It utilizes initilizer_list syntax to allow for arbitrary dimensional arrays to be created using
Array<double> array({10, 20, 30});
You can then access and modify individual elements using
double d = array[{1, 2, 3}];
array[{1, 2, 3}] = 10;
And even slice the matrix up into pieces using
array.getSlice({___, 3, 4});
where "___" is used as a wildcard.
See more on: http://www.second-quantization.com/Array.html
Implementation: https://github.com/dafer45/TBTK/blob/master/Lib/include/Utilities/TBTK/Array.h

Solution for object let's the user choose the number of dimensions. A little robust, my C++ maybe is not the best, but it was fun implementing. nvector<T> represents resizable (in dimensions and count of elements in each dimension) array of element of T type, although only some resize functions are implemented. narray<T> is the same, but the number of dimensions is not resizable. This works around the idea of recalculating index position of a multidimensional array using a single continuous array.
#include <cstdio>
#include <vector>
#include <iostream>
#include <cstddef>
#include <cstdarg>
#include <algorithm>
#include <numeric>
#include <cassert>
#include <memory>
#include <cstring>
using namespace std;
template<typename T>
class narray {
public:
static size_t compute_size(initializer_list<size_t>& dims) {
return accumulate(dims.begin(), dims.end(), 1, multiplies<size_t>());
}
static size_t compute_size(vector<size_t>& dims) {
return accumulate(dims.begin(), dims.end(), 1, multiplies<size_t>());
}
static size_t compute_distance(vector<size_t>& dims) {
return dims.size() > 1 ? dims[1] : 1;
}
static vector<size_t> remove_one_dim(vector<size_t> dims_) {
return vector<size_t>(dims_.begin() + 1, dims_.end());
}
narray(initializer_list<size_t> dims, T* data) :
dims_(dims), data_(data) {}
narray(vector<size_t> dims, T* data) :
dims_(dims), data_(data) {}
T operator*() {
return *data_;
}
T* operator&() {
return data_;
}
void operator=(T v) {
if (dims_.size() != 0)
throw runtime_error(__PRETTY_FUNCTION__);
*data_ = v;
}
void operator=(initializer_list<T> v) {
if (v.size() > size())
throw runtime_error(__PRETTY_FUNCTION__);
copy(v.begin(), v.end(), data_);
}
T* data() {
return data_;
}
T* data_last() {
return &data()[compute_size(dims_)];
}
size_t size() {
return compute_size(dims_);
}
size_t size(size_t idx) {
return dims_[idx];
}
narray<T> operator[](size_t idx) {
if (dims_.size() == 0)
throw runtime_error(__PRETTY_FUNCTION__);
return narray<T>(remove_one_dim(dims_),
&data_[idx * compute_distance(dims_)]);
}
class iterator {
public:
iterator(initializer_list<size_t>& dims, T* data) :
dims_(dims), data_(data) { }
iterator(vector<size_t>& dims, T* data) :
dims_(dims), data_(data) { }
iterator operator++() {
iterator i = *this;
data_ += compute_distance(dims_);
return i;
}
narray<T> operator*() {
return narray<T>(remove_one_dim(dims_), data_);
}
bool operator!=(const iterator& rhs) {
if (dims_ != rhs.dims_)
throw runtime_error(__PRETTY_FUNCTION__);
return data_ != rhs.data_;
}
private:
vector<size_t> dims_;
T* data_;
};
iterator begin() {
return iterator(dims_, data());
}
iterator end() {
return iterator(dims_, data_last());
}
private:
vector<size_t> dims_;
T* data_;
};
template<typename T>
class nvector {
public:
nvector(initializer_list<size_t> dims) :
dims_(dims), data_(narray<T>::compute_size(dims)) {}
nvector(vector<size_t> dims) :
dims_(dims), data_(narray<T>::compute_size(dims)) {}
nvector(initializer_list<size_t> dims, T* data) :
dims_(dims), data_(data) {}
nvector(vector<size_t> dims, T* data) :
dims_(dims), data_(data) {}
T* data() {
return data_.data();
}
T* data_last() {
return &data()[narray<T>::compute_size(dims_)];
}
size_t size() {
return narray<T>::compute_size(dims_);
}
narray<T> operator&() {
return narray<T>(dims_, data());
}
narray<T> operator[](size_t idx) {
if (dims_.size() == 0)
throw runtime_error(__PRETTY_FUNCTION__);
return narray<T>(narray<T>::remove_one_dim(dims_),
&data()[idx * narray<T>::compute_distance(dims_)]);
}
void operator=(initializer_list<T> v) {
if (v.size() > size())
throw runtime_error(__PRETTY_FUNCTION__);
copy(v.begin(), v.end(), data_.begin());
}
auto begin() {
return typename narray<T>::iterator(dims_, data());
}
auto end() {
return typename narray<T>::iterator(dims_, data_last());
}
// add and remove dimensions
void dimension_push_back(size_t dimsize) {
dims_.push_back(dimsize);
data_.resize(size());
}
void dimension_pop_back() {
dims_.pop_back();
data_.resize(size());
}
// TODO: resize dimension of index idx?
private:
vector<size_t> dims_;
vector<T> data_;
};
int main()
{
nvector<int> A({2, 3});
A = { 1,2,3, 4,5,6 };
assert(A.size() == 6);
assert(&A[0] == &A.data()[0]);
assert(&A[0][0] == &A.data()[0]);
assert(&A[1] == &A.data()[3]);
assert(&A[0][1] == &A.data()[1]);
assert(&A[1][1] == &A.data()[4]);
cout << "Currently array has " << A.size() << " elements: " << endl;
for(narray<int> arr1 : A) { // we iterate over arrays/dimensions
for(narray<int> arr2 : arr1) { // the last array has no dimensions
cout << "elem: " << *arr2 << endl;
}
}
cout << endl;
// assigment example
cout << "Now it is 4: " << *A[1][0] << endl;
A[1][0] = 10;
cout << "Now it is 10: " << *A[1][0] << endl;
return 0;
}
This code needs still much more work. It works only as a simple example. Maybe use shared_ptr in narray? Implement better exceptions?
So creating an array of n=5 dimensions with sizes size1, size2, size3, size4 and size5 would like this:
narray<int> arr({size1, size2, size3, size4, size5});
arr[0][1][2][3][4] = 5; // yay!

Related

2D stack allocated vector implementation

I'm looking for an implementation of a stack allocated 2d array (array of arrays) which supports O(1) reads. You can guess what I mean from the below picture. Black are filled entries, white are possible gaps from erasure.
The implementation should allow fast random access O(1) on each element but also allow insertion and erase operations which do not shift around too many elements (there might be string objects located there).
Information for each array is held in an object like this:
struct array
{
iterator begin_;
iterator end_;
array* next_;
array* prev_;
};
It contains information on where this particular array starts and the memory neighbours (prev_ and next_).
I'm looking for a proven battle hardened algorithm for insertion and erasing that I can rely on. I tried constructing a few on my own but they tend to become very complicated very quickly.
Hurdles:
When arrays are shifted, each updated array needs to somehow receive the memo (adapt begin and end pointers).
Array objects will be themselves located in an array. This means that with every additional data member of struct array, the memory requirements of the whole thing will grow by member_size * 2d_array_size.
I'm open for all suggestions!
I am thinking of an idea, where we can segment the storage into different segments of size n. entire buffer size will be the multiple of n.
When a new array is to be initialized, we allocate a segment to it. normal array operations can be performed there. when it needs more space, it request for one more segment, and if more segment space available we allocate it to them to extend it.
In this case, Minimum length of an array cannot go below segment size n. this size n can be fine tuned as per requirement for better space efficiency and utilization.
Each segment is numbered, so we can calculate the index of an element and fetch it in O(1).
Sample program (In Python):
class segment:
def __init__(self,number):
self.number=number
class storage:
def __init__(self):
self.size=100
self.default_value=None
self.array=[self.default_value]*self.size
self.segment_size=5
self.number_of_segments =len(self.array)//self.segment_size
self.segment_map=[None]*self.number_of_segments
def store(self,index,value):
if index<self.size and index>=0:
self.array[index]=value
def get(self,index):
return self.array[index]
def get_next_segment(self):
new_seg=None
for i,seg in enumerate(self.segment_map):
if seg == self.default_value:
new_seg= segment(i)
break
self.occupy_segment(new_seg)
self.clean_segment(new_seg)
return new_seg
def occupy_segment(self,seg):
self.segment_map[seg.number]=True
def free_segment(self,seg):
self.segment_map[seg.number]=self.default_value
def destroy_segment(self,seg):
self.clean_segment(seg)
self.free_segment(seg)
def clean_segment(self,segment):
if segment==None:
return
segment_start_index=((segment.number) * (self.segment_size)) + 0
segment_end_index=((segment.number) * (self.segment_size)) + self.segment_size
for i in range(segment_start_index,segment_end_index):
self.store(i,self.default_value)
class array:
def __init__(self,storage):
self.storage=storage
self.length=0
self.segments=[]
self.add_new_segment()
def add_new_segment(self):
new_segment=self.storage.get_next_segment()
if new_segment==None:
raise Exception("Out of storage")
self.segments.append(new_segment)
def is_full(self):
return self.length!=0 and self.length%self.storage.segment_size==0
def calculate_storage_index_of(self,index):
segment_number=index//self.storage.segment_size
element_position=index%self.storage.segment_size
return self.segments[segment_number].number * self.storage.segment_size + element_position
def add(self,value):
if self.is_full():
self.add_new_segment()
last_segement=self.segments[-1]
element_position=0
if self.length!=0:
element_position=(self.length%self.storage.segment_size)
index=(last_segement.number*self.storage.segment_size)+element_position
self.__store(index,value)
self.length+=1
def __store(self,index,value):
self.storage.store(index,value)
def update(self,index,value):
self.__store(
self.calculate_storage_index_of(index),
value
)
def get(self,index):
return self.storage.get(self.calculate_storage_index_of(index))
def destroy(self):
for seg in self.segments:
self.storage.destroy_segment(seg)
st=storage()
array1=array(st)
array1.add(3)
Hi I did not have enough time to implement a full solution (and no time to complete it) but here is the direction I was taking (live demo : https://onlinegdb.com/sp7spV_Ui)
#include <cassert>
#include <array>
#include <stdexcept>
#include <vector>
#include <iostream>
namespace meta_array
{
template<typename type_t, std::size_t N> class meta_array_t;
// template internal class not for public use.
namespace details
{
// a block contains the meta information on a subarray within the meta_array
template<typename type_t, std::size_t N>
class meta_array_block_t
{
public:
// the iterator within a block is of the same type as that of the containing array
using iterator_t = typename std::array<type_t, N>::iterator;
/// <summary>
///
/// </summary>
/// <param name="parent">parent, this link is needed if blocks need to move within the parent array</param>
/// <param name="begin">begin iterator of the block</param>
/// <param name="end">end iterator of the block (one past last)</param>
/// <param name="size">cached size (to not have to calculate it from iterator differences)</param>
meta_array_block_t(meta_array_t<type_t, N>& parent, const iterator_t& begin, const iterator_t& end, std::size_t size) :
m_parent{ parent },
m_begin{ begin },
m_end{ end },
m_size{ size }
{
}
// the begin and end methods allow a block to be used in a range based for loop
iterator_t begin() const noexcept
{
return m_begin;
}
iterator_t end() const noexcept
{
return m_end;
}
// operation to shrink the size of the last free block in the meta-array
void move_begin(std::size_t n) noexcept
{
assert(n <= m_size);
m_size -= n;
m_begin += n;
}
// operation to move a block n items back in the meta array
void move_to_back(std::size_t n) noexcept
{
m_begin += n;
m_end += n;
}
std::size_t size() const noexcept
{
return m_size;
}
// assign a new array to the sub array
// if the new array is bigger then the array that is already there
// then move the blocks after it toward the end of the meta-array
template<std::size_t M>
meta_array_block_t& operator=(const type_t(&values)[M])
{
// move all other sub-arrays back if the new sub-array is bigger
// if it is smaller then adjusting the end iterator of the block is fine
if (M > m_size)
{
m_parent.move_back(m_end, M - m_size);
}
m_size = M;
// copy will do the right thing (copy from back to front) if needed
std::copy(std::begin(values), std::end(values), m_begin);
m_end = m_begin + m_size;
return *this;
}
private:
meta_array_t<type_t, N>& m_parent;
std::size_t m_index;
iterator_t m_begin;
iterator_t m_end;
std::size_t m_size;
};
} // details
//---------------------------------------------------------------------------------------------------------------------
//
template<typename type_t, std::size_t N>
class meta_array_t final
{
public:
meta_array_t() :
m_free_size{ N },
m_size{ 0ul },
m_last_free_block{ *this, m_buffer.begin(), m_buffer.end(), N }
{
}
~meta_array_t() = default;
// meta_array is non copyable & non moveable
meta_array_t(const meta_array_t&) = delete;
meta_array_t operator=(const meta_array_t&) = delete;
meta_array_t(meta_array_t&&) = delete;
meta_array_t operator=(meta_array_t&&) = delete;
// return the number of subarrays
std::size_t array_count() const noexcept
{
return m_size;
}
// return number of items that can still be allocated
std::size_t free_size() const noexcept
{
return m_free_size;
}
template<std::size_t M>
std::size_t push_back(const type_t(&values)[M])
{
auto block = allocate(M);
std::copy(std::begin(values), std::end(values), block.begin());
return m_blocks.size();
}
auto begin()
{
return m_blocks.begin();
}
auto end()
{
return m_blocks.end();
}
auto& operator[](const std::size_t index)
{
assert(index < m_size);
return m_blocks[index];
}
private:
friend class details::meta_array_block_t<type_t, N>;
void move_back(std::array<type_t,N>::iterator begin, std::size_t offset)
{
std::copy(begin, m_buffer.end() - offset - 1, begin + offset);
// update block administation
for (auto& block : m_blocks)
{
if (block.begin() >= begin )
{
block.move_to_back(offset);
}
}
}
auto allocate(std::size_t size)
{
if ((size == 0ul) || (size > m_free_size)) throw std::bad_alloc();
if (m_last_free_block.size() < size)
{
compact();
}
m_blocks.push_back({ *this, m_last_free_block.begin(), m_last_free_block.begin() + size, size });
m_last_free_block.move_begin(size);
m_free_size -= size;
m_size++;
return m_blocks.back();
}
void compact()
{
assert(false); // not implemented yet
// todo when a gap is found between 2 sub-arrays (compare begin/end iterators) then move
// the next array to the front
// the array after that will move to the front by the sum of the gaps ... etc...
}
std::array<type_t, N> m_buffer;
std::vector<details::meta_array_block_t<type_t,N>> m_blocks;
details::meta_array_block_t<type_t,N> m_last_free_block;
std::size_t m_size;
std::size_t m_free_size;
};
} // meta_array
//---------------------------------------------------------------------------------------------------------------------
#define ASSERT_TRUE(x) assert(x);
#define ASSERT_FALSE(x) assert(!x);
#define ASSERT_EQ(x,y) assert(x==y);
static constexpr std::size_t test_buffer_size = 16;
template<typename type_t, std::size_t N>
void show_arrays(meta_array::meta_array_t<type_t, N>& meta_array)
{
std::cout << "\n--- meta_array ---\n";
for (const auto& sub_array : meta_array)
{
std::cout << "sub array = ";
auto comma = false;
for (const auto& value : sub_array)
{
if (comma) std::cout << ", ";
std::cout << value;
comma = true;
}
std::cout << "\n";
}
}
void test_construction()
{
meta_array::meta_array_t<int, test_buffer_size> meta_array;
ASSERT_EQ(meta_array.array_count(),0ul);
ASSERT_EQ(meta_array.free_size(),test_buffer_size);
}
void test_push_back_success()
{
meta_array::meta_array_t<int, test_buffer_size> meta_array;
meta_array.push_back({ 1,2,3 });
meta_array.push_back({ 14,15 });
meta_array.push_back({ 26,27,28,29 });
ASSERT_EQ(meta_array.array_count(),3ul); // cont
ASSERT_EQ(meta_array.free_size(),(test_buffer_size-9ul));
}
void test_range_based_for()
{
meta_array::meta_array_t<int, test_buffer_size> meta_array;
meta_array.push_back({ 1,2,3 });
meta_array.push_back({ 14,15 });
meta_array.push_back({ 26,27,28,29 });
show_arrays(meta_array);
}
void test_assignment()
{
meta_array::meta_array_t<int, test_buffer_size> meta_array;
meta_array.push_back({ 1,2,3 });
meta_array.push_back({ 4,5,6 });
meta_array.push_back({ 7,8,9 });
meta_array[0] = { 11,12 }; // replace with a smaller array then what there was
meta_array[1] = { 21,22,23,24 }; // replace with a bigger array then there was
show_arrays(meta_array);
}
//---------------------------------------------------------------------------------------------------------------------
int main()
{
test_construction();
test_push_back_success();
test_range_based_for();
test_assignment();
return 0;
}

Sorting multidimensional arrays by individual elements in c++

I'm just going to come out and ask. I have 500 arrays[127]. I need to sort them all by the value of the very last element in descending order. How would you do this in the most efficient way possible?
EXAMPLE:
float arr1[] = {3,1,4,5};
float arr2[] = {1,2,5,8};
float arr3[] = {102,4,132,2};
OUTPUT:
{arr2,
arr1,
arr3}
Your could create a lightweight wrapper that just stores the begin and end iterators of an array.
#include <iterator>
template<class T>
struct span {
template<size_t N>
span(T (&arr)[N]) : m_begin(std::begin(arr)), m_end(std::end(arr)) {}
T* begin() const { return m_begin; }
T* end() const { return m_end; }
T& front() const { return *m_begin; }
T& back() const { return *std::prev(m_end); }
private:
T* m_begin;
T* m_end;
};
You could then put these lightweight wrappers in a std::vector<span<int>> and sort that. Note that this will not affect the original arrays in any way and since sorting the vector only moves the spans around and not the actual array data, it should be reasonably fast.
#include <algorithm>
#include <iostream>
#include <vector>
int main() {
int arr1[] = {3,1,4,5};
int arr2[] = {1,2,5,8};
int arr3[] = {102,4,132,2};
std::vector<span<int>> arrs{
arr1, arr2, arr3
};
// sort on the last element, decending:
std::sort(arrs.begin(), arrs.end(),[](const auto& lhs, const auto& rhs) {
return rhs.back() < lhs.back();
});
for(auto& arr : arrs) {
for(auto v : arr) std::cout << v << ' ';
std::cout << '\n';
}
}
Output:
1 2 5 8
3 1 4 5
102 4 132 2
Since a raw array is a pointer without size information carried out, your job isn't automatic.
Either you convert all your arrays to std::vector<float> so you can have the last element and then have a std::vector<std::vector<float>> with a custom sort operator to test, or you create a structure that contains the raw array along with size information.
std::vector<float> arr1 = {3,1,4,5}
std::vector<float> arr2 = {1,2,5,8};
std::vector<float> arr3 = {102,4,132,2};
std::vector<std::vector<float>> arrays = {arr1,arr2,arr3};
std::sort(arrays.begin(),arrays.end(),[](const std::vector<float>& r1,const std::vector<float>& r2) -> bool
{
if (r1[r1.size() - 1] < r2[r2.size() - 1]) return true;
return false;
});
struct ArrAndSize { float* arr; size_t sz; };
ArrAndSize arr1 = {{3,1,4,5},4};
ArrAndSize arr2 = {{1,2,5,8},4};
ArrAndSize arr3 = {{102,4,132,2},4};
std::vector<ArrAndSize> arrays = {arr1,arr2,arr3};
std::sort(arrays.begin(),arrays.end(),[](const ArrAndSize& r1,const ArrAndSize& r2) -> bool
{
if (r1.arr[r1.sz - 1] < r2.arr[r2.sz - 1]) return true;
return false;
});
In both cases, check if the array has size 0.

is there a way to declare a ranged based array in c++ arduino?

is there a way to declare a range based array in c++ arduino for example instad of writing
const int array[] = {2,3,4,5,6};
why isn't it possible to declare an array like this?
const int array[] = {2:6};
I assume that you are not using the Arduino STL, so you could create a simple array wrapping class template, similar to std::array, that takes two template parameters: The type, T, and the size, N.
Example:
template<class T, size_t N>
struct array {
// misc typedef's:
using value_type = T;
using const_pointer = const value_type*;
using pointer = value_type*;
using const_iterator = const value_type*;
using iterator = value_type*;
size_t size() const { return N; } // the fixed size of the array
// subscripting:
const T& operator[](size_t idx) const { return data[idx]; }
T& operator[](size_t idx) { return data[idx]; }
// implicit conversions when passed to functions:
operator const_pointer () const { return data; }
operator pointer () { return data; }
// iterator support:
const_iterator cbegin() const { return data; }
const_iterator cend() const { return data + N; }
const_iterator begin() const { return cbegin(); }
const_iterator end() const { return cend(); }
iterator begin() { return data; }
iterator end() { return data + N; }
T data[N]; // the actual array
};
You could then create a small helper function to create arrays and fill them with a range of values like in your question:
template<class T, T min, T max>
array<T, max - min + 1> make_array_min_max() {
array<T, max - min + 1> rv;
for(T i = min; i <= max; ++i) rv[i - min] = i;
return rv;
}
The creation would just be slightly different, but you could then use it pretty much like you use a normal array.
void func(const int* a, size_t s) { // C-style interface
for(size_t i = 0; i < s; ++i)
std::cout << a[i] << ' ';
std::cout << '\n';
}
int main() {
const auto arr = make_array_min_max<int, 2, 6>(); // create the range you want
func(arr, arr.size()); // implicit conversion to `const int*` for `arr`
for(auto v : arr) // range-based for loop
std::cout << v << ' ';
std::cout << '\n';
for(size_t i = 0; i < arr.size(); ++i) // classing loop
std::cout << arr[i] << ' ';
std::cout << '\n';
}
Output:
2 3 4 5 6
2 3 4 5 6
2 3 4 5 6

Push_Front Pop_Back for C++ Vector

I'm trying to keep a vector of commands so that it keeps 10 most recent. I have a push_back and a pop_back, but how do I delete the oldest without shifting everything in a for loop? Is erase the only way to do this?
Use std::deque which is a vector-like container that's good at removal and insertion at both ends.
If you're amenable to using boost, I'd recommend looking at circular_buffer, which deals with this exact problem extremely efficiently (it avoids moving elements around unnecessarily, and instead just manipulates a couple of pointers):
// Create a circular buffer with a capacity for 3 integers.
boost::circular_buffer<int> cb(3);
// Insert threee elements into the buffer.
cb.push_back(1);
cb.push_back(2);
cb.push_back(3);
cb.push_back(4);
cb.push_back(5);
The last two ops simply overwrite the elements of the first two.
Write a wrapper around a vector to give yourself a circular buffer. Something like this:
include <vector>
/**
Circular vector wrapper
When the vector is full, old data is overwritten
*/
class cCircularVector
{
public:
// An iterator that points to the physical begining of the vector
typedef std::vector< short >::iterator iterator;
iterator begin() { return myVector.begin(); }
iterator end() { return myVector.end(); }
// The size ( capacity ) of the vector
int size() { return (int) myVector.size(); }
void clear() { myVector.clear(); next = 0; }
void resize( int s ) { myVector.resize( s ); }
// Constructor, specifying the capacity
cCircularVector( int capacity )
: next( 0 )
{
myVector.resize( capacity );
}
// Add new data, over-writing oldest if full
void push_back( short v )
{
myVector[ next] = v;
advance();
}
int getNext()
{
return next;
}
private:
std::vector< short > myVector;
int next;
void advance()
{
next++;
if( next == (int)myVector.size() )
next = 0;
}
};
What about something like this:
http://ideone.com/SLSNpc
Note: It's just a base, you still need to work a bit on it. The idea is that it's easy to use because it has it's own iterator, which will give you the output you want. As you can see the last value inserted is the one shown first, which I'm guessing is what you want.
#include <iostream>
#include <vector>
template<class T, size_t MaxSize>
class TopN
{
public:
void push_back(T v)
{
if (m_vector.size() < MaxSize)
m_vector.push_back(v);
else
m_vector[m_pos] = v;
if (++m_pos == MaxSize)
m_pos = 0;
}
class DummyIterator
{
public:
TopN &r; // a direct reference to our boss.
int p, m; // m: how many elements we can pull from vector, p: position of the cursor.
DummyIterator(TopN& t) : r(t), p(t.m_pos), m(t.m_vector.size()){}
operator bool() const { return (m > 0); }
T& operator *()
{
static T e = 0; // this could be removed
if (m <= 0) // if someone tries to extract data from an empty vector
return e; // instead of throwing an error, we return a dummy value
m--;
if (--p < 0)
p = MaxSize - 1;
return r.m_vector[p];
}
};
decltype(auto) begin() { return m_vector.begin(); }
decltype(auto) end() { return m_vector.end(); }
DummyIterator get_dummy_iterator()
{
return DummyIterator(*this);
}
private:
std::vector<T> m_vector;
int m_pos = 0;
};
template<typename T, size_t S>
void show(TopN<T,S>& t)
{
for (auto it = t.get_dummy_iterator(); it; )
std::cout << *it << '\t';
std::cout << std::endl;
};
int main(int argc, char* argv[])
{
TopN<int,10> top10;
for (int i = 1; i <= 10; i++)
top10.push_back(5 * i);
show(top10);
top10.push_back(60);
show(top10);
top10.push_back(65);
show(top10);
return 0;
}

remove arbitrary list of items from std::vector<std::vector<T> >

I have a vector of vectors, representing an array. I would like to remove rows efficiently, ie with minimal complexity and allocations
I have thought about building a new vector of vectors, copying only non-deleted rows, using move semantics, like this:
//std::vector<std::vector<T> > values is the array to remove rows from
//std::vector<bool> toBeDeleted contains "marked for deletion" flags for each row
//Count the new number of remaining rows
unsigned int newNumRows = 0;
for(unsigned int i=0;i<numRows();i++)
{
if(!toBeDeleted[i])
{
newNumRows++;
}
}
//Create a new array already sized in rows
std::vector<std::vector<T> > newValues(newNumRows);
//Move rows
for(unsigned int i=0;i<numRows();i++)
{
if(!toBeDeleted[i])
{
newValues[i] = std::move(values[i]);
}
}
//Set the new array and clear the old one efficiently
values = std::move(newValues);
Is this the most effective way?
Edit : I just figured that I could avoid allocating a new array by moving rows down iteratively, this could be slightly more efficient and code is much more simple:
unsigned int newIndex = 0;
for(unsigned int oldIndex=0;oldIndex<values.size();oldIndex++)
{
if(!toBeDeleted[oldIndex])
{
if(oldIndex!=newIndex)
{
values[newIndex] = std::move(values[oldIndex]);
}
newIndex++;
}
}
values.resize(newIndex);
Thanks!
This can be solved using a variation on the usual erase-remove idiom, with a lambda inside the std::remove_if that looks up the index of the current row inside an iterator range of to be removed indices:
#include <algorithm> // find, remove_if
#include <iostream>
#include <vector>
template<class T>
using M = std::vector<std::vector<T>>; // matrix
template<class T>
std::ostream& operator<<(std::ostream& os, M<T> const& m)
{
for (auto const& row : m) {
for (auto const& elem : row)
os << elem << " ";
os << "\n";
}
return os;
}
template<class T, class IdxIt>
void erase_rows(M<T>& m, IdxIt first, IdxIt last)
{
m.erase(
std::remove_if(
begin(m), end(m), [&](auto& row) {
auto const row_idx = &row - &m[0];
return std::find(first, last, row_idx) != last;
}),
end(m)
);
}
int main()
{
auto m = M<int> { { 0, 1, 2, 3 }, { 3, 4, 5, 6 }, { 6, 7, 8, 9 }, { 1, 0, 1, 0 } };
std::cout << m << "\n";
auto drop = { 1, 3 };
erase_rows(m, begin(drop), end(drop));
std::cout << m << "\n";
}
Live Example.
Note: because from C++11 onwards, std::vector has move semantics, shuffling rows around in your std::vector<std::vector<T>> is done using simple pointer manipulations, regardless of your type T (it would be quite different if you want column-deletion, though!).