mardi 5 septembre 2017

Passing operation templated-function by reference in recursive template

In my PhD, I very often use matrices of various dimensions and data types. For first time, I use recursive templates in C++11 for manipulating my non-contagious arrays, so it is the last time I see calloc and free. With the help of stackoverflow link1 and link2 I achieved my initial goal of de&allocating them. Greedy me, I now want my class to perform the basic cellwise operations e.i. addition, division. It works fine if I use enum to determine which operation is performed, but I fail to use reference to an other templated function that performs the operation. The enum way:

The main:

//Define the size of each dimension in a vector
int height = 3, width = 2;
std::vector<int> dimensions;
dimensions.push_back(height);
dimensions.push_back(width);

// Allocate a table of doubles 3x2
smatrix<double **> dbltbl(dimensions);
// Assign values in the cells
for (int r = 0; r < height; ++r)
    for (int c = 0; c < width; ++c) {
        dbltbl.uacc()[r][c] = (double)(r * width + c) * 2.6;
    }

// Create a table of ints from the table of doubles (rounded copy)
smatrix<int **> inttbl = dbltbl;

// Add cell-wise the table of ints to the table of doubles
dbltbl.cellwise_add(inttbl);

The enum:

enum clc_op { addition, subtration, multiplication, division };

The operation function:

template <typename S, typename T>
T add(S one, T two) {
    return (T)one + two;
}

The recursive template solution:

template <typename S, typename T>
void cellwise_ops(S &src, T &dest, std::vector<int> dims, enum clc_op operation) {
    switch (operation) {
    case clc_op::addition:
        dest = add(src, dest);
        break;
    //…
    //...
    }
}

template <typename S, typename T>
void cellwise_ops(S *src, T *dest, std::vector<int> dims, enum clc_op operation) { 
    if (dims.size() == 0)
        return false;
    int toimp = dims.front();
    dims.erase(dims.begin());
    for (int i = 0; i < toimp; ++i) {
        cellwise_ops(src[i], dest[i], dims, operation)
    }
}

The class method (e.g. P = double** and U=int**):

template <typename P>
template <typename U>
void smatrix<P>::cellwise_add(smatrix<U> const &addend) {
    U temp = addend.uacc();
    cellwise_ops(temp, _full, _dim, clc_op::addition);
}

The output:

==========================================
Table of doubles:
0   2.6 
5.2 7.8 
10.4    13  
==========================================
Table of ints from the table of doubles:
0   2   
5   7   
10  13  
==========================================
Table of doubles+ints:
0   4.6 
10.2    14.8    
20.4    26  

This solution doesn’t look elegant, making me believe it is the wrong approach. So, I try to pass the operation as reference to function, and I fail hard time:

The addition (operation) function remains the same. The recursive solution:

template <typename S, typename T>
void cellwise_ops(S &src, T &dest, std::vector<int> dims, T (*operation)(S, T)) {
    dest = operation(src, dest);
}

template <typename S, typename T>
void cellwise_ops(S *src, T *dest, std::vector<int> dims, T (*operation)(S, T)) {
    if (dims.size() == 0)
        return false;
    int toimp = dims.front();
    dims.erase(dims.begin());
    for (int i = 0; i < toimp; ++i) {
        cellwise_ops(src[i], dest[i], dims, operation);
    }
}

The class method:

template <typename P>
template <typename U>
void smatrix<P>::cellwise_add(smatrix<U> const &addend) {
    U temp = addend.uacc();
    cellwise_ops(temp, _full, _dim, add<U, P>);
}

The error:

./sm_example/smatrix.hpp:305:17:   required from ‘void smatrix<P>::cellwise_add(const smatrix<U>&) [with U = int**; P = double**]’
./sm_example/sm_example.cpp:157:35:   required from here
./sm_example/smatrix.hpp:159:19: error: invalid operands of types ‘double**’ and ‘double**’ to binary ‘operator+’
     return (T)one + two;

I understand that addition between pointers is not allowed, although I know it will not happen the compiler doesn't. I have no clue how I could work around it. How do I pass the operation’s template-function (add) by reference in the recursive template-function? Do I use templates in a wrong way?

I do not want to use std::vector for the time being, but comments are welcome. Worst case scenario, my dataset reaches 100MBytes in 5 dimensions.

If you made it so far, thank you! Chris

Aucun commentaire:

Enregistrer un commentaire