Primitive array vs. Array template in C++ - c++

I got this question from the cracking the coding interview book. I was able to write this method in python and java. But when I tried to write it in c++, the compiler starts yelling at me. I think the problem is that in the main function, I had a array instantiated by a template but the function is taking in a primitive array. How should I instantiate a primitive array?
// Given a sorted array of positive integers with an empty spot (zero) at the
// end, insert an element in sorted order.
bool sortSortedArray(size_t arrInt[], size_t x)
{
size_t indexArr{0};
size_t insertNum{x};
while (x != 0) {
if (x < arrInt[indexArr]) {
size_t swapVal = arrInt[indexArr];
arrInt[indexArr];
insertNum = swapVal;
++indexArr;
}
}
return true;
}
// Test the sortSortedArray function.
int main()
{
array<size_t, 5> testArr{1, 4, 5, 8, 0};
if (sortSortedArray(testArr, 3)) {
return 0;
}
}

Either make testArr a primitive array:
int testArr[] = {1, 4, 5, 8, 0};
or call data() to get the underlying array:
if (sortSortedArray(testArr.data(), 3)) {

#include <cstddef>
#include <array>
#include <iostream>
// this is a function template because each std::array<> parameter set creates a
// a type and we need a function for each type (we know std::size_t is the element
// type so this is only parameterized on the size)
template<size_t ArrSize>
void sortSortedArray(
std::array<std::size_t, ArrSize>& arr,
const std::size_t insertNum)
{
// last position is known to be "empty"
arr[arr.size() - 1] = insertNum;
// swap value in last position leftwards until value to the left is less
auto pos = arr.size() - 1;
if (pos == 0)
return;
while (arr[pos - 1] > arr[pos])
{
const auto tmp = arr[pos - 1];
arr[pos - 1] = arr[pos];
arr[pos] = tmp;
--pos;
if (pos == 0)
return;
}
}
template<typename T, size_t N>
void printArray(const std::array<T, N>& r)
{
for (const auto i : r)
{
std::cout << i << " ";
}
std::cout << '\n';
}
int main()
{
std::array<std::size_t, 5> testArr{{1, 4, 5, 8, 0}};
printArray(testArr);
sortSortedArray(testArr, 3);
printArray(testArr);
}

Related

C++ Return indexes of lowest values in array

I'm trying to return indexes of lowest values in an array, and i'm unable to find a good solution anywhere.
int indexofSmallestElement(double array[], int size)
{
int index = 0;
for(int i = 1; i < size; i++)
{
if(array[i] < array[index])
index = i;
}
return index;
}
This is the probably the simplest way of finding index of one value, but what if I have multiple lowest values in an array?
EDIT: Oh, I just realized from the comment that there is the possibility of duplicate values, so if the question is actually that, you could do this, it also returning a std::vector:
std::vector<int> indexesOfSmallestElements(double array[], int size)
{
std::vector<int> indexes = { 0 };
for (int i = 1; i < size; i++)
{
double current_smallest = array[indexes.front()];
if (array[i] < current_smallest) {
indexes.clear();
indexes.emplace_back(i);
}
else if (array[i] == current_smallest) {
indexes.emplace_back(i);
}
}
return indexes;
}
ORIGINAL:
"Lowest" is always only one, if you want "lower than a value", just return a std::vector.
std::vector<int> indexesOfSmallElements(double array[], int size, double value)
{
std::vector<int> indexes;
for (int i = 0; i < size; i++)
{
if (array[i] < value) {
indexes.emplace_back(i);
}
}
return indexes;
}
Array indices are a fairly inflexible concept, in C++ you can gain plenty of generality with iterators - there's a whole lot of C++ algorithms that take iterators directly, and essentially next to nothing in the standard library uses naked indices.
C++20 & onwards
We can create a minimums range view (lazy range combinator) that takes a range, and returns a range that only contains the minimums from the original range. The range isn't a new vector with filtered elements: it is a view onto the original range (e.g. onto the original vector of doubles).
#include <algorithm>
#include <ranges>
#include <vector>
template <typename Range>
auto minimums(Range && range) {
using namespace std::ranges;
auto first_min = min_element(range);
auto const is_min = [=](auto const &el){ return el == *first_min; };
return subrange(first_min, std::end(range)) | views::filter(is_min);
}
You definitely do not need to recover the indices of the array - iterators are a generic concept and apply quite widely. But just to show a solution equivalent to the one for C++17 & prior below, let's write some adapters that'll help with index recovery:
template <typename Range>
auto addressof(Range && range) {
using namespace std::ranges;
return views::transform(range, [](auto &el){ return &el; });
}
template <typename Range, typename It>
auto array_indices(Range && range, It && reference) {
using namespace std::ranges;
auto to_index = [ref_addr = &*reference](auto *el){ return ref_addr - el; };
return range | addressof | views::transform(to_index);
}
And now we can test it:
#include <cassert>
#include <iostream>
int main()
{
const double array[] = {8, 3, -1, -1, 9, -1, 5};
auto min_range = minimums(array);
{ // test
auto min = std::ranges::min_element(array);
// have we got the correct number of minimums?
auto min_size = std::distance(std::begin(min_range), std::end(min_range));
assert(min_size == std::count(std::begin(array), std::end(array), *min));
// are all of the minimums indeed minimums?
assert(std::ranges::all_of(min_range,
[=](auto &el){ return el == *min; }));
// are all of the minimums references to the array
assert(std::ranges::all_of(min_range,
[&](auto &el){ return &el >= std::begin(array) && &el < std::end(array); }));
}
for (auto &min : min_range)
std::cout << std::distance(std::begin(array), &min) << ' ';
std::cout << '\n';
}
Output:
2 3 5
C++17 through C++11
Thus, let's have a minimumLocations function that takes two forward iterators that define a range, and return a vector of iterators to all the minimums that were found:
#include <iterator>
#include <type_traits>
#include <vector>
template <typename I1, typename I2>
std::vector<I1> minimumLocations(I1 start, I2 end)
{
if (start == end) return {};
std::vector<I1> locations = {start};
std::decay<decltype(*start)>::type min = *start;
std::advance(start, 1);
for (; start != end; std::advance(start, 1)) {
auto const &value = *start;
if (value < min) {
// new minimum
locations.clear();
locations.push_back(start);
min = *start;
}
else if (value == min)
locations.push_back(start);
}
return locations;
}
For convenience's sake, we can also have an adapter function that takes a range instead of a pair of iterators. In C++, a "range" is anything that has a beginning and an end:
template <typename R>
auto minimumLocations(R &range) {
return minimumLocations(std::begin(range), std::end(range));
}
Both of the functions will work on any container that provides forward iterators - not only on arrays, but also vectors, lists, forward lists, etc. A specialized version could also be provided for pre-sorted containers.
And now a test and a demo:
#include <algorithm>
#include <cassert>
#include <iostream>
int main()
{
const double array[] = {8, 3, -1, -1, 9, -1, 5};
auto min_indices = minimumLocations(array);
{ // test
auto min = std::min_element(std::begin(array), std::end(array));
// have we got the correct numer of locations?
assert(min_indices.size() == std::count(std::begin(array), std::end(array), *min));
// are all of the locations indeed minimums?
assert(std::all_of(std::begin(min_indices), std::end(min_indices),
[=](auto it){ return *it == *min; }));
}
for (auto i : min_indices)
std::cout << std::distance(array, i) << ' ';
std::cout << '\n';
}
Output:
2 3 5

return array reference from function

I have an image with int[Components] components per pixel.
I want get() return an int[Components] reference when Components != 1 and an int& when Components == 1.
pos is a pointer to raw image data.
I already read this StackOverflow question before ask, but I have no success.
The following code compiles ok, but returns garbage.
#include <iostream>
template<typename ComponentType, uint8_t Size>
struct A
{
typedef typename std::conditional<Size == 1,
ComponentType&, ComponentType(&)[Size]>::type SampleType;
SampleType get()
{
if constexpr (Size == 1) return *pos;
else return reinterpret_cast<SampleType>(pos);
}
void advance() { pos += Size; }
ComponentType *pos;
};
int main()
{
int i[10] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
A<int, 2> a{i};
std::cout << (size_t) &a << "\r\n";
std::cout << (size_t) a.get() << "\r\n";
std::cout << (size_t) a.pos << "\r\n";
std::cout << (size_t) i << "\r\n";
for(int c : a.get())
std::cout << c << " ";
a.get()[1] = 1000;
return 0;
}
The code has the following output. Pointer values indicates that I totally screwed.
2293256
2293256
2293264
2293264
2293264 0
Can I ask what I was terribly wrong? How to fix this? Of-course get() can return a pointer if Components != 1 but I want the ability to use for( _ : _ )
A span is probably good for this.
template<typename ComponentType, uint8_t Size>
struct A
{
typedef typename std::conditional<Size == 1,
ComponentType&, span<ComponentType, Size>>::type SampleType;
SampleType get()
{
if constexpr (Size == 1) return *pos;
else return span<ComponentType, Size>(pos, Size);
}
void advance() { pos += Size; }
ComponentType *pos;
};
Solution strictly on returning an array reference.
Although span solution is very neat.
template<typename ComponentType, uint8_t Size>
struct A
{
typedef typename std::conditional<Size == 1,
ComponentType, ComponentType[Size]>::type SampleType;
SampleType& get()
{
if constexpr (Size == 1) return *pos;
else return *reinterpret_cast<SampleType*>(pos);
}
void advance() { pos += Size; }
ComponentType *pos;
};

Is it possible to use -1 to fetch the last element of a container/array?

What I want is illustrated as follows:
int array[4] { 1, 2, 3, 4 };
auto n1 = array[-1];
assert(4 == n1);
auto n2 = array[-2];
assert(3 == n2);
std::vector coll { 1, 2, 3, 4 };
auto n3 = coll[-1];
assert(4 == n3);
auto n4 = coll[-2];
assert(3 == n4);
I tried the following template function:
template<typename C, typename I>
constexpr decltype(auto) operator [](const C& coll, I idx)
{
if (idx >= 0)
{
return coll[idx];
}
else
{
return coll[std::size(coll) + idx];
}
}
But Clang complains:
error : overloaded 'operator[]' must be a non-static member function
constexpr decltype(auto) operator [](const C& coll, I idx)
Is it possible to correctly implement the function in modern C++?
You can't overload operator[] for a raw array.
But you can just define some named functions, e.g., off the cuff:
using Index = ptrdiff_t;
template< class Item, size_t n >
auto item( Index const i, Item (&a)[n] )
-> Item&
{ return (i < 0? a[Index( n ) + i] : a[i]); }
Then the test code for raw arrays, suitably adapted, would be like
int array[] { 1, 2, 3, 4 };
int n1 = item( -1, array );
assert( 4 == n1 );
int n2 = item( -2, array );
assert( 3 == n2 );
I leave a definition for a general collection like std::vector, as an exercise for the reader. :)
You can also make above solution more generic (at least for standard sequence containers) with the use of non-member std::begin() functions. Example:
template <typename C>
auto item(C const& container, int index) -> decltype(*std::cbegin(container))
{
return index >= 0 ?
*(std::cbegin(container) + index) :
*(std::crbegin(container) - index - 1);
}
And then use it like that:
std::vector<int> coll{ 1, 2, 3, 4 };
int arra[] { 10, 20, 30, 40 };
auto last = item(coll, -1);
auto secondLast = item(coll, -2);
last = item(arra, -1);
secondLast = item(arra, -2);
return 0;
Hope that helps :)

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!).

is there a way to pass nested initializer lists in C++11 to construct a 2D matrix?

Imagine you have a simple matrix class
template <typename T = double>
class Matrix {
T* data;
size_t row, col;
public:
Matrix(size_t m, size_t n) : row(m), col(n), data(new T[m*n]) {}
//...
friend std::ostream& operator<<(std::ostream& os, const Matrix& m) {
for (int i=0; i<m.row; ++i) {
for (int j=0; j<m.col; ++j)
os<<" "<<m.data[i + j*m.row];
os<<endl;
}
return os;
}
};
Is there a way that I can initialize this matrix with an initializer list? I mean to obtain the sizes of the matrix and the elements from an initializer list. Something like the following code:
Matrix m = { {1., 3., 4.}, {2., 6, 2.}};
would print
1 3 4
2 6 2
Looking forward to your answers. Thank you all.
aa
EDIT
So I worked on your suggestions to craft a somewhat generic array that initializes elements using initializer lists. But this is the most generic I could obtain.
I would appreciate if any of you have any suggestions as to make it a more generic class.
Also, a couple of questions:
Is it fine that a derived class initializes the state of the base class? I'm not calling the base constructor because of this, but should I call it anyways?
I defined the destructor a the Generic_base class as protected, is this the right way to do it?
Is there any foreseeable way to carry out the code that belongs to the constructor of the initializer in a more generic way? I mean to have one general constructor that takes care of all cases?
I included just the necessary code to illustrate the use of initializer lists in construction. When going to higher dimensions it gets messy, but I did one just to check the code.
#include <iostream>
#include <cassert>
using std::cout;
using std::endl;
template <int d, typename T>
class Generic_base {
protected:
typedef T value_type;
Generic_base() : n_(), data_(nullptr){}
size_t n_[d] = {0};
value_type* data_;
};
template <int d, typename T>
class Generic_traits;
template <typename T>
class Generic_traits<1,T> : public Generic_base<1,T> {
protected:
typedef T value_type;
typedef Generic_base<1,T> base_type;
typedef std::initializer_list<T> initializer_type;
using base_type::n_;
using base_type::data_;
public:
Generic_traits(initializer_type l) {
assert(l.size() > 0);
n_[0] = l.size();
data_ = new T[n_[0]];
int i = 0;
for (const auto& v : l)
data_[i++] = v;
}
};
template <typename T>
class Generic_traits<2,T> : public Generic_base<2,T> {
protected:
typedef T value_type;
typedef Generic_base<2,T> base_type;
typedef std::initializer_list<T> list_type;
typedef std::initializer_list<list_type> initializer_type;
using base_type::n_;
using base_type::data_;
public:
Generic_traits(initializer_type l) {
assert(l.size() > 0);
n_[0] = l.size();
n_[1] = l.begin()->size();
data_ = new T[n_[0]*n_[1]];
int i = 0, j = 0;
for (const auto& r : l) {
assert(r.size() == n_[1]);
for (const auto& v : r) {
data_[i + j*n_[0]] = v;
++j;
}
j = 0;
++i;
}
}
};
template <typename T>
class Generic_traits<4,T> : public Generic_base<4,T> {
protected:
typedef T value_type;
typedef Generic_base<4,T> base_type;
typedef std::initializer_list<T> list_type;
typedef std::initializer_list<list_type> llist_type;
typedef std::initializer_list<llist_type> lllist_type;
typedef std::initializer_list<lllist_type> initializer_type;
using base_type::n_;
using base_type::data_;
public:
Generic_traits(initializer_type l) {
assert(l.size() > 0);
assert(l.begin()->size() > 0);
assert(l.begin()->begin()->size() > 0);
assert(l.begin()->begin()->begin()->size() > 0);
size_t m = n_[0] = l.size();
size_t n = n_[1] = l.begin()->size();
size_t o = n_[2] = l.begin()->begin()->size();
n_[3] = l.begin()->begin()->begin()->size();
data_ = new T[m*n*o*n_[3]];
int i=0, j=0, k=0, p=0;
for (const auto& u : l) {
assert(u.size() == n_[1]);
for (const auto& v : u) {
assert(v.size() == n_[2]);
for (const auto& x : v) {
assert(x.size() == n_[3]);
for (const auto& y : x) {
data_[i + m*j + m*n*k + m*n*o*p] = y;
++p;
}
p = 0;
++k;
}
k = 0;
++j;
}
j = 0;
++i;
}
}
};
template <int d, typename T>
class Generic : public Generic_traits<d,T> {
public:
typedef Generic_traits<d,T> traits_type;
typedef typename traits_type::base_type base_type;
using base_type::n_;
using base_type::data_;
typedef typename traits_type::initializer_type initializer_type;
// initializer list constructor
Generic(initializer_type l) : traits_type(l) {}
size_t size() const {
size_t n = 1;
for (size_t i=0; i<d; ++i)
n *= n_[i];
return n;
}
friend std::ostream& operator<<(std::ostream& os, const Generic& a) {
for (int i=0; i<a.size(); ++i)
os<<" "<<a.data_[i];
return os<<endl;
}
};
int main()
{
// constructors for initializer lists
Generic<1, double> y = { 1., 2., 3., 4.};
cout<<"y -> "<<y<<endl;
Generic<2, double> C = { {1., 2., 3.}, {4., 5., 6.} };
cout<<"C -> "<<C<<endl;
Generic<4, double> TT = { {{{1.}, {7.}, {13.}, {19}}, {{2}, {8}, {14}, {20}}, {{3}, {9}, {15}, {21}}}, {{{4.}, {10}, {16}, {22}}, {{5}, {11}, {17}, {23}}, {{6}, {12}, {18}, {24}}} };
cout<<"TT -> "<<TT<<endl;
return 0;
}
Which prints as expected:
y -> 1 2 3 4
C -> 1 4 2 5 3 6
TT -> 1 4 2 5 3 6 7 10 8 11 9 12 13 16 14 17 15 18 19 22 20 23 21 24
Why not?
Matrix(std::initializer_list<std::initializer_list<T>> lst) :
Matrix(lst.size(), lst.size() ? lst.begin()->size() : 0)
{
int i = 0, j = 0;
for (const auto& l : lst)
{
for (const auto& v : l)
{
data[i + j * row] = v;
++j;
}
j = 0;
++i;
}
}
And as stardust_ suggests - you should use vectors, not arrays here.
The main issue with using initializer lists to tackle this problem, is that their size is not easily accessible at compile time. It looks like this particular class is for dynamic matrices, but if you wanted to do this on the stack (usually for speed/locality reasons), here is a hint at what you need (C++17):
template<typename elem_t, std::size_t ... dim>
struct matrix
{
template<std::size_t ... n>
constexpr matrix(const elem_t (&...list)[n]) : data{}
{
auto pos = &data[0];
((pos = std::copy(list, list + n, pos)), ...);
}
elem_t data[(dim * ... * 1)];
};
template<typename ... elem_t, std::size_t ... n>
matrix(const elem_t (&...list)[n]) -> matrix<std::common_type_t<elem_t...>, sizeof...(n), (n * ... * 1) / sizeof...(n)>;
I had to tackle this same problem in my linear algebra library, so I understand how unintuitive this is at first. But if you instead pass a C-array into your constructor, you will have both type and size information of the values you've passed in. Also take note of the constuctor template argument deduction (CTAD) to abstract away the template arguments.
You can then create constexpr matrix objects like this (or, leave out constexpr to simply do this at runtime on the stack):
constexpr matrix mat{ {1, 2, 3}, {4, 5, 6}, {7, 8, 9}, {10, 11, 12} };
Which will initialize an object at compile time of type:
const matrix<int, 4, 3>
If C++20 is supported by your compiler, I would recommend adding a "requires" clause to the CTAD to ensure that all sub-arrays are the same size (mathematically-speaking, n1 == n2 == n3 == n4, etc).
Using std::vector::emplace_back() (longer)
Using std::vector, instead of plain old array, you can use std::vector::emplace_back() to fill the vector:
template <typename T = double>
class Matrix {
std::vector<T> data;
size_t row{}, col{}; // Non-static member initialization
public:
Matrix(size_t m, size_t n) : data(std::vector<T>(m * n)), row(m), col(n)
{ // ^ Keep the order in which the members are declared
}
Matrix(std::initializer_list<std::initializer_list<T>> lst)
: row(lst.size())
, col(lst.size() ? lst.begin()->size() : 0) // Minimal validation
{
// Eliminate reallocations as we already know the size of matrix
data.reserve(row * col);
for (auto const& r : lst) {
for (auto const &c : r) {
data.emplace_back(c);
}
}
}
};
int main() {
Matrix<double> d = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
}
Using std::vector::insert() (better and shorter)
As #Bob mentioned in a comment, you can use std::vector::insert() member function, instead of the inner emplace_back loop:
template <typename T = double>
class Matrix {
std::vector<T> data;
size_t row{}, col{}; // Non-static member initialization
public:
Matrix(size_t m, size_t n) : data(std::vector<T>(m * n)), row(m), col(n)
{ // ^ Keep the order in which the members are declared
}
Matrix(std::initializer_list<std::initializer_list<T>> lst)
: row{lst.size()}
, col{lst.size() ? lst.begin()->size() : 0} // Minimal validation
{
// Eliminate reallocations as we already know the size of the matrix
data.reserve(row * col);
for (auto const& r : lst) {
data.insert(data.end(), r.begin(), r.end());
}
}
};
int main() {
Matrix<double> d = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
}
So, we're saying: For each row (r) in the lst, insert the content of the row from the beginning (r.begin()) to the end (r.end()) into the end of the empty vector, data, (in an empty vector semantically we have: empty_vec.begin() == empty_vec.end()).
i might be a bit late but here is code for generally initializing tensors, regardless if they are matricies or vectors or whatever tensor.You could restrict it by throwing runtime errors when its not a matrix. Below is the source code to extract the data from the initilizer_list its a bit hacky. The whole trick is that the constructor are implicitly called with the correct type.
#include <initializer_list>
#include <iostream>
using namespace std;
class ShapeElem{
public:
ShapeElem* next;
int len;
ShapeElem(int _len,ShapeElem* _next): next(_next),len(_len){}
void print_shape(){
if (next != nullptr){
cout <<" "<< len;
next->print_shape();
}else{
cout << " " << len << "\n";
}
}
int array_len(){
if (next != nullptr){
return len*next->array_len();
}else{
return len;
}
}
};
template<class value_type>
class ArrayInit{
public:
void* data = nullptr;
size_t len;
bool is_final;
ArrayInit(std::initializer_list<value_type> init) : data((void*)init.begin()), len(init.size()),is_final(true){}
ArrayInit(std::initializer_list<ArrayInit<value_type>> init): data((void*)init.begin()), len(init.size()),is_final(false){}
ShapeElem* shape(){
if(is_final){
ShapeElem* out = new ShapeElem(len,nullptr);
}else{
ArrayInit<value_type>* first = (ArrayInit<value_type>*)data;
ShapeElem* out = new ShapeElem(len,first->shape());
}
}
void assign(value_type** pointer){
if(is_final){
for(size_t k = 0; k < len;k ++ ){
(*pointer)[k] = ( ((value_type*)data)[k]);
}
(*pointer) = (*pointer) + len;
}else{
ArrayInit<value_type>* data_array = (ArrayInit<value_type>*)data;
for(int k = 0;k < len;k++){
data_array[k].assign(pointer);
}
}
}
};
int main(){
auto x = ArrayInit<int>({{1,2,3},{92,1,3}});
auto shape = x.shape();
shape->print_shape();
int* data = new int[shape->array_len()];
int* running_pointer = data;
x.assign(&running_pointer);
for(int i = 0;i < shape->array_len();i++){
cout << " " << data[i];
}
cout << "\n";
}
outputs
2 3
1 2 3 92 1 3
The shape() function will return you the shape of the tensor at each dimension. The array is exactly saved as it is written down. It's really import to create something like shape since this will give you the ordering in which the elements are.
If you want a specific index out of the tensor lets say a[1][2][3]
the correct position is in 1*a.shape[1]a.shape[2] + 2a.shape[2] + 3
Some minor details and tricks can be found in: https://github.com/martinpflaum/multidimensional_array_cpp