Is it possible to realize column-wise operation and row-wise operation by one template function?

87 views Asked by At

Here is my problem, I am dealing with a n-dimensional data. Let's say n=2 for simplicity. Also I have an algorithm for 1D data. In order to extend this algorithm for 2D problem, I can do

for each row
    apply algorithm

However, if I want to apply this algorithm for each column, I need to write a new function

for each column
    apply algorithm

For example, assume i have a function:

void func(vector<T> &vec);

Then to apply this function to a vector I can simply call this function:

vector<T> vec;
func(vec);

For a 3D data:

T multiArray[l][m][n];

From my knowledge, if I want to apply above function for all vectors in first dimension, I will do:

for(int j=0;j<m;j++){
    for(int k=0;k<n;k++){
        vector<T> singleArray;
        for(int i=0;i<l;i++){
            singleArray.push_back(multiArray[i][j][k]);
        }
        func(singleArray);
    }
}

However, for the same data, if I want to apply the above function for all vectors in the third dimension, I need to rewrite it as:

for(int i=0;i<l;i++){
    for(int j=0;j<m;j++){
        vector<T> singleArray;
        for(int k=0;k<n;k++){
            singleArray.push_back(multiArray[i][j][k]);
        }
        func(singleArray);
    }
}

Basically, everything is the same except iterators in each for loop. I hope there are some ways that I can realize those two computation with one function.

Thx

1

There are 1 answers

3
max66 On BEST ANSWER

I don't know a general solution but you can solve your specific problem (use the first, or the second or the third index or...) using a reference.

In the 3D case, first you can declare the loop variables (i, j and k)

std::size_t i, j, k;

Next you can "link" another variable (r) to i, j or k according to a template value I

std::size_t & r = (I == 0U ? i : (I == 1U ? j : k));

The following is a compilable example

#include <vector>
#include <iostream>

template <std::size_t I>
void func (std::vector<std::vector<std::vector<double> > > & d)
 {
   std::size_t i, j, k;

   std::size_t & r = (I == 0U ? i : (I == 1U ? j : k));

   for ( i = 0U ; i < d.size() ; ++i )
     for ( j = 0U ; j < d[i].size() ; ++j )
       for ( k = 0U ; k < d[i][j].size() ; ++k )
          d[i][j][k] += r+1;
 }

int main()
 {
   std::vector<std::vector<std::vector<double> > > data;

   // some data in data

   func<0>(data); // r is i
   func<1>(data); // r is j
   func<2>(data); // r is k
 }

--- EDIT ---

The OP ask

is there anyway that this function could work for arbitrary dimension?

No.

Not this function.

But I propose a completely different (and more complex) solution. I write it but don't ask me to check if it really works.

The idea isn't based on reference anymore but on template specialization.

This time the template index is 1-based: use template value 1 if you want intercept the first index (ex x), 2 if you want intercept the second index (ex y), etc.

So you call

foo<1U>(data1);    // r is the first index

for a 1D vector,

foo<1U>(data2);     // r is the first index
foo<2U>(data2);     // r is the second index

for a 2D vector, etc.

If you call

foo<I>(data)

where I is bigger than the dimension of data, you get a compilation error.

If you call

foo<0>(data)

you get a compilation error but only if you compile C++11 or newer (with C++98 r become zero; but you can add an assert() to get a runtime error).

The example

#include <vector>
#include <iostream>

template <std::size_t I>
struct bar
 {
   template <typename T>
   static void baz (std::vector<T> & v, std::size_t)
    {
      for ( std::size_t i = 0U ; i < v.size() ; ++i )
         bar<I-1U>::baz(v[i], i);
    }
 };

template <>
struct bar<0U>
 {
   template <typename T>
   static void baz (std::vector<T> & v, std::size_t r)
    {
      for ( std::size_t i = 0U ; i < v.size() ; ++i )
         baz(v[i], r);
    }

   static void baz (double & d, std::size_t r)
    { d += r + 1U; }
 };


template <std::size_t I, typename V>
void foo (V & v)
 {
#if __cplusplus >= 201103L   
   static_assert(I > 0U, "!"); // c++11 or newer
#endif

   bar<I>::baz(v, 0U);
 }

int main()
 {
   std::vector<double >                            data1;
   std::vector<std::vector<double> >               data2;
   std::vector<std::vector<std::vector<double> > > data3;

   // some data in data1, data2 and data3

   // foo<0U>(data1);  // compilation error in C++11 or newer
   foo<1U>(data1);     // r is the first index
   // foo<2U>(data1);  // compilation error

   // foo<0U>(data2);  // compilation error in C++11 or newer
   foo<1U>(data2);     // r is the first index
   foo<2U>(data2);     // r is the second index
   // foo<3U>(data2);  // compilation error

   // foo<0U>(data3);  // compilation error in C++11 or newer
   foo<1U>(data3);     // r is the first index
   foo<2U>(data3);     // r is the second index
   foo<3U>(data3);     // r is the third index
   // foo<4U>(data3);  // compilation error
 }