J Sherratt
J Sherratt

Reputation: 41

Function that can take arrays with different dimensions

Is there a method to create a single function that can take any dimension of vector without overloading?

Currently I have,

someFunction(vector<int> a)
someFunction(vector<vector<int> > a)
someFunction(vector<vector<vector<int> > > a)

However, would it be possible to have a function:

singleFunction(<n-dimension vector>)
{
    // Get dimension of array/vector
}

Upvotes: 4

Views: 166

Answers (5)

Thomas Sablik
Thomas Sablik

Reputation: 16448

You can use a recursive template function

#include <iostream>
#include <vector>

void func(int el) {
  std::cout << el << std::endl;
}

template<typename T>
void func(std::vector<T> v) {
  for (const T& el : v) {
    func(el);
  }
}

int main() {
  std::vector<std::vector<int>> v {{1, 2}, {2, 3}};
  func(v);
  return 0;
}

It's calling it itself for each element until it reaches elements of type int.

To get the dimension you can use the same pattern:

#include <iostream>
#include <vector>

template<typename T>
int someFunction(std::vector<T> v, int dim = 1);

template<>
int someFunction(std::vector<int> v, int dim) {
  return dim;
}

template<typename T>
int someFunction(std::vector<T> v, int dim) {
  return someFunction(T(), dim + 1);
}

template<typename T>
void singleFunction(std::vector<T> v) {
  int dim(someFunction(v));
  std::cout << dim << std::endl;
  // Do something
}

int main() {
  std::vector<std::vector<std::vector<int>>> v {{{1, 0}, {2, 4}}, {{2, 2}, {3, 0}}};
  singleFunction(v);
  singleFunction(std::vector<std::vector<int>>());
  singleFunction(std::vector<int>());
  return 0;
}

Here it creates a new object of value type and calls itself until its value type is int. Every time it increments the dimension.

Upvotes: 4

Tyker
Tyker

Reputation: 3047

you can get the dimension with this code

#include <vector>
#include <iostream>

template<unsigned N, typename T>
struct meta {
    static unsigned func() {//terminale recursion case
        return N;
    }
};

template<unsigned N, typename T>
struct meta<N, std::vector<T> > {//mid recursion case
    static unsigned func() {
        return meta<N + 1, T>::func();
    }
};

template<typename T>
unsigned func(T) { //adapter to deduce the type
    return meta<0, T>::func();
}

int main() {
  std::cout << func(std::vector<std::vector<std::vector<int> > >()) << std::endl;
  std::cout << func(std::vector<int>()) << std::endl;
  std::cout << func(int()) << std::endl;
  std::cout << func(std::vector<std::vector<std::vector<std::vector<std::vector<std::vector<int> > > > > >()) << std::endl;
  return 0;
}

will output

3
1
0
6

Upvotes: 1

Davide Spataro
Davide Spataro

Reputation: 7482

With C++17 you can write a pretty simple solution:

template<typename T >
constexpr int func(){
  if constexpr (is_vector<typename T::value_type>::value )
   return 1+func<typename T::value_type>();
  return 1;
}

int main() {
    cout<< func<vector<vector<vector<vector<vector<int>>>>>>() <<endl;
    return 0;
}

which return 5 as expected.

You need to define is_vector as follows:

template<class T>
struct is_vector{
     static bool const value = false;   
};
template<class T>
struct is_vector<std::vector<T> > {
  static bool const value = true;
};

Upvotes: 2

Killzone Kid
Killzone Kid

Reputation: 6240

Perhaps you could try this approach, I think this is exactly what you are asking (adopted from std::rank):

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

template<typename T>
struct vector_rank : public std::integral_constant<std::size_t, 0> {};

template<typename T>
struct vector_rank<std::vector<T>> : public std::integral_constant<std::size_t, vector_rank<T>::value + 1> {};

template<typename T>
size_t GetVectorRank(T)
{
    return vector_rank<T>::value;
}

int main()
{
    std::vector<std::vector<std::vector<std::vector<std::vector<int>>>>> v1;
    std::cout << GetVectorRank(v1) << std::endl;
    std::vector<std::vector<std::vector<int>>> v2;
    std::cout << GetVectorRank(v2) << std::endl;
    return 0;
}

The second template be selected recursively while the type is std::vector<T>, the first template will be selected for everything else as well as at the end of recursion. The above example will return:

5
3

Demo: https://ideone.com/CLucGA

Upvotes: 3

Goswin von Brederlow
Goswin von Brederlow

Reputation: 12342

A simple template should solve this. From memory:

template <T> singleFunction(vector<T> &t) {
    return t.size();
}

Upvotes: 1

Related Questions