2

I am writing some function templates using armadillo linear algebra library, but it encounters some errors. I am still learning C++ and its aspects, so will be very much thankful for any possible solutions. Most of my functions are like the following,

template<typename T1>
void some_function(const Mat<T1> & p)
{
    unsigned int n = p.n_rows;
    // do some stuffs...
    // ....
}

My main function contains:

Mat<double> A = ones<Mat<double>>(4,4);
int a(2);
some_function(A);  // runs perfectly
some_function(a*A); // compilation error as follows

test_function.hpp:35:8: note: template argument deduction/substitution failed:
test.cpp:22:17: note: ‘arma::enable_if2<true, const arma::eOp<arma::Mat<double>, arma::eop_scalar_times> >::result {aka const arma::eOp<arma::Mat<double>, arma::eop_scalar_times>}’ is not derived from ‘const arma::Mat<eT>’
some_function(a*A);

If I change the function as follows:

template<typename T1>
void some_function(const T1 & p)
{
    unsigned int n = p.n_rows;
    // do some stuffs...
    // ....
}

Then it gives the compilation error as follows:

test_function.hpp: In instantiation of ‘bool some_function(const T1&) [with T1 = arma::eOp<arma::Mat<double>, arma::eop_scalar_times>]’:
test.cpp:22:17:   required from here
test_function.hpp:37:26: error: ‘const class arma::eOp<arma::Mat<double>, arma::eop_scalar_times>’ has no member named ‘n_rows’
 unsigned int n = p.n_rows;

But the non-template functions work perfectly, like

void some_function(const Mat<double> & p)
{
    unsigned int n = p.n_rows();
    // do some stuffs...
    // ....
}

Any solutions??

Titas Chanda
  • 563
  • 1
  • 4
  • 14
  • It seems that `operator *(double, Mat)` return a Lazy evaluation and not directly a `Mat`... – Jarod42 Oct 02 '15 at 00:14
  • The error is so clear. Template can be `int`. Does `int` has a member `n_rows()`? – Enamul Hassan Oct 02 '15 at 00:26
  • @manetsus No. `int` do not have `n_rows`, but `Mat` has. In the second case, since I am only passing `Mat` class (or `int*Mat<>` ...), I think, only that version of the function will be instantiated. @Jarod42 Yes, that may be the problem. – Titas Chanda Oct 02 '15 at 00:32
  • That means you want to tell that your first (among five in the question) code block is getting error, right? – Enamul Hassan Oct 02 '15 at 00:35
  • From doc, there is constructor from `Mat(arma::eOp<..>)` but can't find a way to force arma::eOp to *"decay"* to `Mat<..>`. and row_count getter has an other name: `get_n_rows()`... – Jarod42 Oct 02 '15 at 00:37
  • @manetsus Yes. Both first and third block returns error... – Titas Chanda Oct 02 '15 at 00:46
  • @Jarod42 Thanks, but other member functions of Mat class also not working. May be I should only use non-member functions, or overload user defined functions by hand. – Titas Chanda Oct 02 '15 at 00:49
  • @TitasChanda: I post an answer which may work. But I expect the library provided a proper way to handle that. – Jarod42 Oct 02 '15 at 00:52
  • 1
    @Jarod42 - indeed the library does provide a proper way to handle that: the [.eval()](http://arma.sourceforge.net/docs.html#eval_member) member function. For example: `my_function( (a*A).eval() )` – mtall Oct 02 '15 at 15:41
  • @TitasChanda - `n_rows()` doesn't exist. Instead, there is a read-only variable called [n_rows](http://arma.sourceforge.net/docs.html#attributes) (ie. without the `()` brackets). Use it like this: `unsigned int n = p.n_rows` – mtall Oct 02 '15 at 15:43

3 Answers3

4

Use the .eval() member function to forcefully convert an Armadillo expression into a matrix.

You can then use the result of .eval() as the input to a function. For example:

mat A(10,20,fill::randu);
mat B(10,20,fill::randu);

some_function( (A+B).eval() );

Also note that the Mat class doesn't have a member function called n_rows(). Instead it has a read-only variable called n_rows. For example:

unsigned int n = X.n_rows;
mtall
  • 3,574
  • 15
  • 23
1

I think that with those helpers:

template <typename T>
const Mat<T>& as_Mat(const Mat<T>& m) {return m;}

template<typename T1, typename eop_type>
Mat<typename T1::elem_type> as_Mat(const eOp<T1, eop_type>& X)
{
    return {X};
}

You may write:

template<typename T>
void some_function(const T & p)
{
    const auto& mat = as_mat(p);
    unsigned int n = mat.n_rows();
    // do some stuffs...
    // ....
}
Jarod42
  • 203,559
  • 14
  • 181
  • 302
1

After Jarod42's post, I put some time on this matter and found out another possible solution to this problem, very similar to Jarod42's one. I am replying it as an answer for anyone who might stuck into this same problem like myself. I just changed the second as_Mat function that Jarod42 has already written.

template <typename T>
const Mat<T>& as_Mat(const Mat<T>& m) {return m;}


template<typename T>
Mat<typename T::elem_type> as_Mat(const T& X)
{
    return {X};
}

And all user defined functions will contain one line as before.

template<typename T>
void some_function(const T & X)
{
    const auto& Y = as_Mat(X);
    // do some stuffs with Y...
    // ....
} 

This way any delayed evaluation will be converted into matrix, eg. A+B, A*B, i*A (i = int, complex<>,double...) etc. etc. etc.

Though I am not sure about the performance of the code and also, as mtall mentioned about .eval() function, this fix can be very much helpful to build templated libraries using Armadillo, where library users do not need to put .eval() every other time. Again, thank you all for your kind help and attention.

Titas Chanda
  • 563
  • 1
  • 4
  • 14