2

i am trying to convert c multidimensional array to multidimensional c++ vector, i mean, to convert something like this int arr[2][3] = {{1,2,3}, {4,5,6}}; into the correspondent vector.
The array isn't necessarily 2D shaped, it could also be something like this:

int arr[2][2][3] = {
   {
      {1,2,3},
      {4,5,6},
   },
   {
      {7,8,9},
      {10,11,12},
   }
};

initially i thought that something like this would have worked, but it didn't really turned out to be the case because it seems like if std::vector doesn't allows conversions from C arrays.

std::vector<std::any> V(arr);

Then i thought at something like function recursion, this is my attempt, that (i don't know why!) throws error: no matching function for call to 'length' .

#include <iostream>
#include <type_traits>
#include <vector>
#include <any>

// Get the lenght of a classic C array.
template <class T, unsigned S>
inline unsigned length(const T (&v)[S]) {
  return S;
};

// Check wether the input is a classic C array or not.
template <class T>
bool is(const T& t) {
  return std::is_array_v<T>;
};

// Turn the classic C input array to vector.
template <class T>
std::vector<std::any> toVector(const T& t) {
  std::vector<std::any> V;
  for (int k = 0; k < length(t); k++) {
    if (is(t[k])) {
      V.push_back(toVector(t[k]));
    } else {
      V.push_back(t[k]);
    }
  }
  return V;
}

int main() {

  int16 a[] = {1,2,3};

  auto b = toVector(a);

}

What did i wrong in the second attempt? Alternatively, is there a simpler way to manage to do this?
Also, i think it would be better to convert all the numbers in the vector to a unique given data type, is this possible?
I am using c++11 and g++ as compiler –

Note that i do not know how many dimensions my array has.

3
  • Maybe because you have misspelled length instead of lenght only? Commented Feb 7, 2021 at 17:44
  • @Pat.ANDRIA It is consistently misspelled. If that was the issue the error would be an "identifier not found" type, not a "no matching function" (where the compiler recognizes the name but can't match the types). Commented Feb 7, 2021 at 17:46
  • @Pat.ANDRIA noticed that now, but i misspelled it in the whole file, then i shouldn't be a problem. I am gonna fix it anyway. Commented Feb 7, 2021 at 17:48

2 Answers 2

2

The equivalent of a C multidimensional array is a flat C++ vector plus a mumtidimensional array view.

The naive equivalent is a vector of vectors (maybe of vectors), which actually corresponds to a C "jagged" array. The memory layout and performance characteristics will be very different.

There are many multi dimensional array-view implementstions on the web.

template<class A>
struct raw_ptr { using type=A*; };
template<class A, std::size_t N>
struct raw_ptr<A[N]>:raw_ptr<A>{};

template<class A>
using raw_ptr_t = typename raw_ptr<A>::type;

template<class T>
struct array_view;

template<class T, std::size_t D0, std::size_t D1>
struct array_view<T[D0][D1]> {
  T(*data)[D1]=0;
  constexpr array_view( T(&arr)[D0][D1] ):data(arr) {}
  explicit array_view( raw_ptr_t<T> buff ):data(reinterpret_cast<T(*)[D1]>(buff)) {}
  constexpr array_view<T[D1]> operator[](std::size_t i)const{
    return data[i];
  }
};
template<class T, std::size_t D0>
struct array_view<T[D0]> {
  constexpr array_view( T(&arr)[D0] ):data(arr) {}
  explicit constexpr array_view( T* buff ):data(buff) {}

  T* data=0;
  constexpr T& operator[](std::size_t i)const{
    return data[i];
  }
};

to convert a int[4][5][6] you'd do:

int array[4][5][6]={/*whatever*/};
std::vector<int> buff(&array[0][0][0], &array[3][4][5]);
array_view<int[4][5][6]> view{buff.data()};

now, view[a][b][c] is the same as array[a][b][c].

Live example.

template<class Array>
struct wrap_array_in_vector {
  using raw_ptr = raw_ptr_t<Array>;
  using value_type = std::remove_pointer_t<raw_ptr>;
  std::vector<value_type> data;
  array_view<Array> view;
  wrap_array_in_vector(wrap_array_in_vector const& other):
    data(other.data),
    view(data.data())
  {}
  wrap_array_in_vector(wrap_array_in_vector && other):
    data(std::move(other.data)),
    view(data.data())
  {
    other.view.data = nullptr; // no longer valid
  }
  decltype(auto) operator[](std::size_t i)const {
    return view[i];
  }
  wrap_array_in_vector( Array const& arr ):
    data( reinterpret_cast<value_type const*>(&arr[0]), reinterpret_cast<value_type const*>(&arr[1]) ),
    view(data.data())
  {}
};
template<class Array>
wrap_array_in_vector(Array&)->wrap_array_in_vector<Array>;
template<class Array>
wrap_array_in_vector(Array const&)->wrap_array_in_vector<Array>;

this lets you do

wrap_array_in_vector wrapped = array;

and wrapped deduces all of its type information it needs.

Sign up to request clarification or add additional context in comments.

3 Comments

Hi, thanks for answering my question. I write with c++ just from a few weeks, your code is really dense! Can you explain a bit better how this works?
@Giuppox Well, not it actually works. :) The array view stores a pointer to a buffer of memory, and does the array indexing math that C++ does by default when you do a[1][2][3]. wrap_array_in_vector uses deduction guides to allow you to take a raw C array, and it will shove it into both a std::vector with the data and build an array_view for it. Then it exposes []. The wrap_array_in_vector behaves a lot like a value type; I didn't implement operator= however. I suspect =default might work?
As mentioned, there are many array views that support multiple dimensions on the internet. Find one. I simply included one here for completeness, not because I think mine is going to be better than one you find at random on the internet.
1

This should be close to optimal for conversion to a vector. The part of the description that was misleading was that the sizes are not regular, as they are list-initialized and in this case with int, will be zeros.

#include <type_traits>
#include <vector>
#include <iostream>

template<typename T, std::size_t N>
auto to_vectors( T const (&c_array)[N] ) {
    using element_base_type = std::decay_t<T>;
    if constexpr( std::is_array_v<T> ) {
        using child_t = std::remove_cv_t<std::remove_reference_t<decltype( to_vectors( c_array[0] ) )>>;
        auto result = std::vector<child_t>( );
        result.reserve( N );
        for( auto const & element: c_array ) {
            result.push_back( to_vectors( element ) );
        }
        return result;
    } else {
        return std::vector<T>( c_array, c_array + N );
    }
 }


 int arr[2][2][3] = {
   {
      {1,2,3},
      {4,5,6},
   },
   {
      {7,8,9},
   }
};

auto v0 = to_vectors( arr );


template<typename Vec>
void display( Vec const & vec ) {
    if constexpr( std::is_same_v<int, typename Vec::value_type> ) {
        std::cout << "elements: {";
        for( int e: vec ) {
            std::cout << e << ',';
        }
        std::cout << "}\n";
    } else {
        std::cout << "element count: " << vec.size( ) << '\n';
        for( auto const & child: vec ) {
            display( child );
        }
    }
}


int main( ) {
    display( v0 );
}

this will output

element count: 2
element count: 2
elements: {1,2,3,}
elements: {4,5,6,}
element count: 2
elements: {7,8,9,}
elements: {0,0,0,}

4 Comments

hi, thanks for you answer and time. Where is to_vectors declared? sorry but i cannot understand the point of you answer.
The question was how to convert a c multidimensional array to a multidimensional c++ vector. This does that, the result of to_vectors, which is at the top of the code snippet under the includes, in the example case is a vector<vector<vector<int>>>.
ouch, my fault sorry. That's a really good answer actually!
is it possible to convert all the numbers inside the output to a unique data type (ex double or long int)? I'd need when the numbers inside the array have different dtypes.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.