I downloaded Eigen (3) library and started using it. I wrote a template function and declared a local variable of 'template type' inside the function. I am getting the following compilation error.
$ g++ EigenTest.cpp
EigenTest.cpp: In instantiation of ‘void myFunc(Eigen::MatrixBase<Derived>&) [with Type1 = Eigen::Matrix<double, -1, -1>]’:
EigenTest.cpp:24:10: required from here
EigenTest.cpp:16:26: error: conversion from ‘Eigen::DenseCoeffsBase<Eigen::Matrix<double, -1, -1>, 1>::Scalar {aka double}’ to non-scalar type ‘Eigen::Matrix<double, -1, -1>’ requested
Type1 tmp = matrix(0, 0);
"EigenTest.cpp" is given below.
#include "Eigen/Dense"
#include <iostream>
template<typename Type1>
void myFunc(Eigen::MatrixBase<Type1>& matrix)
{
int i=matrix.rows();
Type1 tmp = matrix(0, 0); // getting compiler error here
std::cout<<"tmp is ->"<<tmp<<std::endl;
}
int main()
{
Eigen::MatrixXd m(2,2);
m.setConstant(100);
myFunc(m);
return 0;
}
I also tried using 'typename Type1 tmp = matrix(0, 0);'
This also didn't work!
How to fix this?
In normal C++ template programming (without Eigen), I can define a local variable inside a template function as 'Type1 tmp;"
In Eigen::MatrixBase<Type1>, Type1 is not a scalar type but the type of the actual expression. In your example it will be MatrixXd but if myFunc is called on, e.g., m.block(...), then Type1 will be a Block<...>. To obtain the scalar type, you can use Type1::Scalar:
template<typename Type1>
void myFunc(Eigen::MatrixBase<Type1>& matrix)
{
typename Type1::Scalar Scalar;
Scalar tmp = matrix(0, 0);
}
And if you need a matrix type that is similar to Type1, use Type1::PlainObject, e.g.:
typename Type1::PlainObject mat = 2 * matrix * matrix.transpose();
It looks like MatrixBase uses the "CRTP" (see here), the template argument is actually the type deriving from it. Thus in your use of the method myFunc(), Type1 is actually representing Eigen::MatrixXd, and I think that you think Type1 is a double. So, this line:
Type1 tmp = matrix(0, 0);
In the documnetation for this library (see here) the typedef for MatrixXd is a matrix of doubles, so I guess the return from matrix(0, 0) is a double, and as tmp is of Type1 which is Eigen::MatrixXd, the one will not go into the other.
Scanning the docummentation I think it MIGHT be better for your function to take a Matrix as an argument, that way the scalar type should be available. Something like this:
template<class T, int rows, int cols, int opts, int maxR, int maxC >
void myFunc( Eigen::Matrix<T, rows, cols, opts, maxR, maxC>& matrix )
{
T tmp = matrix(0, 0);
}
(Looks dreadful though!!! ;-) )
In your code, Type1 is deduced to be double (because Eigen::MatrixXd is defined that way).
You are then trying to do
Type1 tmp = matrix(0, 0);
And I'm afraid my Eigen knowledge isn't enough, so I ran it through Clang 3.3, and got this error:
test.cpp:9:7: error: no viable conversion from 'Scalar' (aka 'double') to
'Eigen::Matrix<double, -1, -1, 0, -1, -1>'
Type1 tmp = matrix(0, 0); // getting compiler error here
^ ~~~~~~~~~~~~
test.cpp:17:1: note: in instantiation of function template specialization
'myFunc<Eigen::Matrix<double, -1, -1, 0, -1, -1> >' requested here
myFunc(m);
^
/usr/include/eigen3/Eigen/src/Core/Matrix.h:210:5: note: candidate constructor not viable:
no known conversion from 'Scalar' (aka 'double') to
'internal::constructor_without_unaligned_array_assert' for 1st argument
Matrix(internal::constructor_without_unaligned_array_assert)
^
/usr/include/eigen3/Eigen/src/Core/Matrix.h:284:25: note: candidate constructor not
viable: no known conversion from 'Scalar' (aka 'double') to 'const
Eigen::Matrix<double, -1, -1, 0, -1, -1> &' for 1st argument
EIGEN_STRONG_INLINE Matrix(const Matrix& other)
^
/usr/include/eigen3/Eigen/src/Core/Matrix.h:272:25: note: candidate template ignored:
could not match 'MatrixBase<type-parameter-0-0>' against 'double'
EIGEN_STRONG_INLINE Matrix(const MatrixBase<OtherDerived>& other)
^
/usr/include/eigen3/Eigen/src/Core/Matrix.h:292:25: note: candidate template ignored:
could not match 'ReturnByValue<type-parameter-0-0>' against 'double'
EIGEN_STRONG_INLINE Matrix(const ReturnByValue<OtherDerived>& other)
^
/usr/include/eigen3/Eigen/src/Core/Matrix.h:303:25: note: candidate template ignored:
could not match 'EigenBase<type-parameter-0-0>' against 'double'
EIGEN_STRONG_INLINE Matrix(const EigenBase<OtherDerived> &other)
^
1 error generated.
which is telling me you cannot call matrix like that, with two 0's as arguments. It's also weird syntax because the MatrixBase class does not have an operator() which you seem to be trying to calling.
Related
I'm looking to make a loss function that can take 2 tensors of any dimensions as parameters, but the dimensions of tensor 1 (t1) and tensor (t2) must match. Below are the templates that I tried to use to can pass the tensors into the function. I was thinking that T would be a type and N would model the number of indexes possible without explicitly writing a type for infinitely possible tensor dimensions.
loss.h
#include <iostream>
namespace Loss {
template<class T, std::size_t N>
void loss(Eigen::Tensor<T, N, 0>& predicted, Eigen::Tensor<T, N, 0>& actual) {
std::cout << "Loss::loss() not implemented" << std::endl;
};
};
main.cpp
#include "loss.h"
int main() {
Eigen::Tensor<double, 3> t1(2, 3, 4);
Eigen::Tensor<double, 3> t2(2, 3, 4);
t1.setZero();
t2.setZero();
Loss::loss(t1, t2);
return 0;
}
The type error that I get before compiling from my editor:
no instance of function template "Loss::loss" matches the argument list -- argument types are: (Eigen::Tensor<double, 3, 0, Eigen::DenseIndex>, Eigen::Tensor<double, 3, 0, Eigen::DenseIndex>
And this is the message I get once I compile (unsuccessfully):
note: candidate template ignored: substitution failure [with T = double]: deduced non-type template argument does not have the same type as the corresponding template parameter ('int' vs 'std::size_t' (aka 'unsigned long'))
void loss(Eigen::Tensor<T, N, 0>& predicted, Eigen::Tensor<T, N, 0>& actual) {
^
1 error generated.
The error message is pointing out the type of the non-type template parameter is size_t, but in the declaration of t1 and t2 the value of that parameter is 3, which has type int. This mismatch makes the template argument deduction fail.
You can fix this by changing the type of the non-type template parameter to int
template<class T, int N>
void loss( // ...
or just let it be deduced
template<class T, auto N>
void loss( // ...
number literals are signed integers, and you’ve specified the number type of your template as size_t Which is unsigned. So the types don’t match. Try Eigen::Tensor<double, 3u> … in your main program to use unsigned literals.
I am trying to make a custom Vector3 inheriting from Eigen's vector 3 like so.
using Vector3 = Eigen::Vector3d;
enum class CustomEnum
{
E1, E2 E3, E4,
};
class CustomVector3 : public Vector3
{
public:
// Taken from here: https://eigen.tuxfamily.org/dox/TopicCustomizing_InheritingMatrix.html
CustomVector3():Vector3(){}
template<typename OtherDerived>
CustomVector3(const Eigen::MatrixBase<OtherDerived>& other) : Vector3(other) {}
// Initializer Constructor
template<typename OtherDerived>
CustomVector3(const std::initializer_list<OtherDerived>& other) : Vector3(other) {}
template<typename OtherDerived>
CustomVector3& operator=(const Eigen::MatrixBase<OtherDerived>& other){
this->Vector3::operator=(other);
return *this;
}
CustomEnum e = CustomEnum::E1;
};
And the goal is to do the following:
TEST_CASE("Initalizer List Test")
{
CustomVector3 vec = {1.1, 2.2, 3.3};
CHECK(vec.x() == 1.1);
CHECK(vec.y() == 2.2);
CHECK(vec.z() == 3.3);
assert(vec.frame == CustomEnum::E1);
}
Without the initializer constructor, I am able to do the following, but not my goal mentioned above:
Vector3 data = {1.1, 2.2, 3.3};
CustomVector3 framedData = data;
With the initializer constructor, I get the following compile error message (I called it FramedVector3 in my code, but for clarity I called it CustomVector3 in this post):
/usr/include/eigen3/Eigen/src/Core/Matrix.h: In instantiation of ‘Eigen::Matrix<_Scalar, _Rows, _Cols, _Options, _MaxRows, _MaxCols>::Matrix(const T&) [with T = std::initializer_list<double>; _Scalar = double; int _Rows = 3; int _Cols = 1; int _Options = 0; int _MaxRows = 3; int _MaxCols = 1]’:
/home/seedship/uavAP/include/uavAP/Core/FramedVector3.h:23:81: required from ‘FramedVector3::FramedVector3(const std::initializer_list<_Tp>&) [with OtherDerived = double]’
/home/seedship/uavAP/include/uavAP/Core/SensorData.h:128:38: required from here
/usr/include/eigen3/Eigen/src/Core/Matrix.h:294:31: error: no matching function for call to ‘Eigen::Matrix<double, 3, 1>::_init1<std::initializer_list<double> >(const std::initializer_list<double>&)’
294 | Base::template _init1<T>(x);
| ~~~~~~~~~~~~~~~~~~~~~~~~^~~
In file included from /usr/include/eigen3/Eigen/Core:462,
from /usr/include/eigen3/Eigen/Dense:1,
from /usr/local/include/cpsCore/Utilities/LinearAlgebra.h:13,
from /home/seedship/uavAP/include/uavAP/Core/FramedVector3.h:9,
from /home/seedship/uavAP/include/uavAP/Core/SensorData.h:11,
from /home/seedship/uavAP/include/uavAP/Core/Orientation/ENU.h:8,
from /home/seedship/uavAP/src/Core/Orientation/ENU.cpp:5:
/usr/include/eigen3/Eigen/src/Core/PlainObjectBase.h:774:30: note: candidate: ‘void Eigen::PlainObjectBase<Derived>::_init1(Eigen::Index, typename Eigen::internal::enable_if<(((typename Eigen::internal::dense_xpr_base<Derived>::type::SizeAtCompileTime != 1) || (! Eigen::internal::is_convertible<T, typename Eigen::internal::traits<T>::Scalar>::value)) && ((! Eigen::internal::is_same<typename Eigen::internal::traits<T>::XprKind, Eigen::ArrayXpr>::value) || (typename Eigen::internal::dense_xpr_base<Derived>::type::SizeAtCompileTime == Eigen::Dynamic))), T>::type*) [with T = std::initializer_list<double>; Derived = Eigen::Matrix<double, 3, 1>; Eigen::Index = long int; typename Eigen::internal::enable_if<(((typename Eigen::internal::dense_xpr_base<Derived>::type::SizeAtCompileTime != 1) || (! Eigen::internal::is_convertible<T, typename Eigen::internal::traits<T>::Scalar>::value)) && ((! Eigen::internal::is_same<typename Eigen::internal::traits<T>::XprKind, Eigen::ArrayXpr>::value) || (typename Eigen::internal::dense_xpr_base<Derived>::type::SizeAtCompileTime == Eigen::Dynamic))), T>::type = std::initializer_list<double>]’
774 | EIGEN_STRONG_INLINE void _init1(Index size, typename internal::enable_if< (Base::SizeAtCompileTime!=1 || !internal::is_convertible<T, Scalar>::value)
| ^~~~~~
/usr/include/eigen3/Eigen/src/Core/PlainObjectBase.h:774:43: note: no known conversion for argument 1 from ‘const std::initializer_list<double>’ to ‘Eigen::Index’ {aka ‘long int’}
774 | EIGEN_STRONG_INLINE void _init1(Index size, typename internal::enable_if< (Base::SizeAtCompileTime!=1 || !internal::is_convertible<T, Scalar>::value)
| ~~~~~~^~~~
/usr/include/eigen3/Eigen/src/Core/PlainObjectBase.h:788:30: note: candidate: ‘template<class T> void Eigen::PlainObjectBase<Derived>::_init1(const Scalar&, typename Eigen::internal::enable_if<((typename Eigen::internal::dense_xpr_base<Derived>::type::SizeAtCompileTime == 1) && Eigen::internal::is_convertible<T, typename Eigen::internal::traits<T>::Scalar>::value), T>::type*) [with T = T; Derived = Eigen::Matrix<double, 3, 1>]’
788 | EIGEN_STRONG_INLINE void _init1(const Scalar& val0, typename internal::enable_if<Base::SizeAtCompileTime==1 && internal::is_convertible<T, Scalar>::value,T>::type* = 0)
| ^~~~~~
/usr/include/eigen3/Eigen/src/Core/PlainObjectBase.h:788:30: note: template argument deduction/substitution failed:
/usr/include/eigen3/Eigen/src/Core/PlainObjectBase.h: In substitution of ‘template<class T> void Eigen::PlainObjectBase<Eigen::Matrix<double, 3, 1> >::_init1<T>(const Scalar&, typename Eigen::internal::enable_if<((Eigen::MatrixBase<Eigen::Matrix<double, 3, 1> >::SizeAtCompileTime == 1) && Eigen::internal::is_convertible<T, double>::value), T>::type*) [with T = std::initializer_list<double>]’:
/usr/include/eigen3/Eigen/src/Core/Matrix.h:294:31: required from ‘Eigen::Matrix<_Scalar, _Rows, _Cols, _Options, _MaxRows, _MaxCols>::Matrix(const T&) [with T = std::initializer_list<double>; _Scalar = double; int _Rows = 3; int _Cols = 1; int _Options = 0; int _MaxRows = 3; int _MaxCols = 1]’
/home/seedship/uavAP/include/uavAP/Core/FramedVector3.h:23:81: required from ‘FramedVector3::FramedVector3(const std::initializer_list<_Tp>&) [with OtherDerived = double]’
/home/seedship/uavAP/include/uavAP/Core/SensorData.h:128:38: required from here
/usr/include/eigen3/Eigen/src/Core/PlainObjectBase.h:788:30: error: invalid use of incomplete type ‘struct Eigen::internal::enable_if<false, std::initializer_list<double> >’
[Truncated for brevity]
Can anyone explain what I am missing? I'm confused why the assignment operator works, but not the constructor. This happens on both Eigen 3.3.9-1 and eigen-git.
Update: maybe this question is very library specific, so I asked on the Eigen gitlab project (https://gitlab.com/libeigen/eigen/-/issues/2192)
You need to use an extra pair of parenthesis. Also there is no point in templating this constructor since the template argument type needs to match the scalar type of the Eigen vector and if you'd like the scalar type to be configurable you'd need to template the whole class.
In summary
CustomVector3(const std::initializer_list<double>& other) : Vector3({other}) {}
is the correct way to do this. The reason is that Eigen::Vector3d is a typedef for Eigen::Matrix<double, 3, 1>. The initializer list constructor for matrices needs to handle multiple rows and columns however and has thus the signature
explicit PlainObjectBase(const std::initializer_list<std::initializer_list<Scalar>>& list);
If you always want to use 3D vectors the (easier) alternative is to use Eigen's 3D constructor and aggregate initialization:
CustomVector3(double x, double y, double z) : Vector3(x, y, z) {}
I have a vector of Hill structs and want to find the one with the heighest height. Here's my code:
#include <vector>
#include <algorithm>
#include <assert.h>
struct Hill {
int height;
int changed;
};
int main() {
std::vector<Hill> hills(100);
hills[0].height = 100;
hills[1].height = 150;
auto byHeight = [&](const Hill& a, const Hill& b) {
return a.height < b.height;
};
Hill hill = std::max(hills.begin(), hills.end(), byHeight);
assert(hill.height == 150);
}
But it fails to compile:
mcve.cpp:15:10: error: no viable conversion from 'const
std::__1::__wrap_iter<Hill *>' to 'Hill'
Hill hill = std::max(hills.begin(), hills.end(), byHeight);
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
mcve.cpp:4:8: note: candidate constructor (the implicit copy constructor) not
viable: no known conversion from 'const std::__1::__wrap_iter<Hill *>' to
'const Hill &' for 1st argument
struct Hill {
^
mcve.cpp:4:8: note: candidate constructor (the implicit move constructor) not
viable: no known conversion from 'const std::__1::__wrap_iter<Hill *>' to
'Hill &&' for 1st argument
struct Hill {
^
In file included from mcve.cpp:1:
In file included from /Library/Developer/CommandLineTools/usr/include/c++/v1/vector:270:
In file included from /Library/Developer/CommandLineTools/usr/include/c++/v1/__bit_reference:15:
/Library/Developer/CommandLineTools/usr/include/c++/v1/algorithm:2627:12: error:
no matching function for call to object of type '(lambda at
mcve.cpp:12:21)'
return __comp(__a, __b) ? __b : __a;
^~~~~~
mcve.cpp:15:22: note: in instantiation of function template specialization
'std::__1::max<std::__1::__wrap_iter<Hill *>, (lambda at mcve.cpp:12:21)>'
requested here
Hill hill = std::max(hills.begin(), hills.end(), byHeight);
^
mcve.cpp:12:21: note: candidate function not viable: no known conversion from
'const std::__1::__wrap_iter<Hill *>' to 'const Hill' for 1st argument
auto byHeight = [&](const Hill& a, const Hill& b) {
^
2 errors generated.
How do I fix it?
Changing these two lines of code fixed the issue (thanks to #milleniumbug):
auto hill = std::max_element(hills.begin(), hills.end(), byHeight);
assert(hill->height == 150);
*std::max_element gets you the element itself. std::max_element returns an iterator, giving you an opportunity to modify the element.
Change std::max to *std::max_element and it'll work. With the *.
max_element() returns an iterator, which * dereferences to get the actual element.
I'm working on adding Sparse matrix support to an open source math library and would like to not have duplicated functions for both Dense and Sparse matrix types.
The below example shows an add function. A working example with two functions, then two attempts that failed. A godbolt link to the code examples are available below.
I've looked over the Eigen docs on writing functions that take Eigen types but their answers of using Eigen::EigenBase does not work because both MatrixBase and SparseMatrixBase have particular methods available that do not exist in EigenBase
https://eigen.tuxfamily.org/dox/TopicFunctionTakingEigenTypes.html
We use C++14, any help and your time is very appreciated!!
#include <Eigen/Core>
#include <Eigen/Sparse>
#include <iostream>
// Sparse matrix helper
using triplet_d = Eigen::Triplet<double>;
using sparse_mat_d = Eigen::SparseMatrix<double>;
std::vector<triplet_d> tripletList;
// Returns plain object
template <typename Derived>
using eigen_return_t = typename Derived::PlainObject;
// Below two are the generics that work
template <class Derived>
eigen_return_t<Derived> add(const Eigen::MatrixBase<Derived>& A) {
return A + A;
}
template <class Derived>
eigen_return_t<Derived> add(const Eigen::SparseMatrixBase<Derived>& A) {
return A + A;
}
int main()
{
// Fill up the sparse and dense matrices
tripletList.reserve(4);
tripletList.push_back(triplet_d(0, 0, 1));
tripletList.push_back(triplet_d(0, 1, 2));
tripletList.push_back(triplet_d(1, 0, 3));
tripletList.push_back(triplet_d(1, 1, 4));
sparse_mat_d mat(2, 2);
mat.setFromTriplets(tripletList.begin(), tripletList.end());
Eigen::Matrix<double, -1, -1> v(2, 2);
v << 1, 2, 3, 4;
// Works fine
sparse_mat_d output = add(mat * mat);
std::cout << output;
// Works fine
Eigen::Matrix<double, -1, -1> output2 = add(v * v);
std::cout << output2;
}
Instead of the two add functions I would just like to have one that takes in both sparse and dense matrices, but the attempts below have not worked out.
Template Template type
An obviously poor attempt on my part, but replacing the two add functions above with a template template type causes an ambiguous base class error.
template <template <class> class Container, class Derived>
Container<Derived> add(const Container<Derived>& A) {
return A + A;
}
Error:
<source>: In function 'int main()':
<source>:35:38: error: no matching function for call to 'add(const Eigen::Product<Eigen::SparseMatrix<double, 0, int>, Eigen::SparseMatrix<double, 0, int>, 2>)'
35 | sparse_mat_d output = add(mat * mat);
| ^
<source>:20:20: note: candidate: 'template<template<class> class Container, class Derived> Container<Derived> add(const Container<Derived>&)'
20 | Container<Derived> add(const Container<Derived>& A) {
| ^~~
<source>:20:20: note: template argument deduction/substitution failed:
<source>:35:38: note: 'const Container<Derived>' is an ambiguous base class of 'const Eigen::Product<Eigen::SparseMatrix<double, 0, int>, Eigen::SparseMatrix<double, 0, int>, 2>'
35 | sparse_mat_d output = add(mat * mat);
| ^
<source>:40:52: error: no matching function for call to 'add(const Eigen::Product<Eigen::Matrix<double, -1, -1>, Eigen::Matrix<double, -1, -1>, 0>)'
40 | Eigen::Matrix<double, -1, -1> output2 = add(v * v);
| ^
<source>:20:20: note: candidate: 'template<template<class> class Container, class Derived> Container<Derived> add(const Container<Derived>&)'
20 | Container<Derived> add(const Container<Derived>& A) {
| ^~~
<source>:20:20: note: template argument deduction/substitution failed:
<source>:40:52: note: 'const Container<Derived>' is an ambiguous base class of 'const Eigen::Product<Eigen::Matrix<double, -1, -1>, Eigen::Matrix<double, -1, -1>, 0>'
40 | Eigen::Matrix<double, -1, -1> output2 = add(v * v);
| ^
I believe It's the same diamond inheritance problem from here:
https://www.fluentcpp.com/2017/05/19/crtp-helper/
Using std::conditional_t
The below attempts to use conditional_t to deduce the correct input type
#include <Eigen/Core>
#include <Eigen/Sparse>
#include <iostream>
// Sparse matrix helper
using triplet_d = Eigen::Triplet<double>;
using sparse_mat_d = Eigen::SparseMatrix<double>;
std::vector<triplet_d> tripletList;
// Returns plain object
template <typename Derived>
using eigen_return_t = typename Derived::PlainObject;
// Check it Object inherits from DenseBase
template<typename Derived>
using is_dense_matrix_expression = std::is_base_of<Eigen::DenseBase<std::decay_t<Derived>>, std::decay_t<Derived>>;
// Check it Object inherits from EigenBase
template<typename Derived>
using is_eigen_expression = std::is_base_of<Eigen::EigenBase<std::decay_t<Derived>>, std::decay_t<Derived>>;
// Alias to deduce if input should be Dense or Sparse matrix
template <typename Derived>
using eigen_matrix = typename std::conditional_t<is_dense_matrix_expression<Derived>::value,
typename Eigen::MatrixBase<Derived>, typename Eigen::SparseMatrixBase<Derived>>;
template <typename Derived>
eigen_return_t<Derived> add(const eigen_matrix<Derived>& A) {
return A + A;
}
int main()
{
tripletList.reserve(4);
tripletList.push_back(triplet_d(0, 0, 1));
tripletList.push_back(triplet_d(0, 1, 2));
tripletList.push_back(triplet_d(1, 0, 3));
tripletList.push_back(triplet_d(1, 1, 4));
sparse_mat_d mat(2, 2);
mat.setFromTriplets(tripletList.begin(), tripletList.end());
sparse_mat_d output = add(mat * mat);
std::cout << output;
Eigen::Matrix<double, -1, -1> v(2, 2);
v << 1, 2, 3, 4;
Eigen::Matrix<double, -1, -1> output2 = add(v * v);
std::cout << output2;
}
This throws the error
<source>: In function 'int main()':
<source>:94:38: error: no matching function for call to 'add(const Eigen::Product<Eigen::SparseMatrix<double, 0, int>, Eigen::SparseMatrix<double, 0, int>, 2>)'
94 | sparse_mat_d output = add(mat * mat);
| ^
<source>:79:25: note: candidate: 'template<class Derived> eigen_return_t<Derived> add(eigen_matrix<Derived>&)'
79 | eigen_return_t<Derived> add(const eigen_matrix<Derived>& A) {
| ^~~
<source>:79:25: note: template argument deduction/substitution failed:
<source>:94:38: note: couldn't deduce template parameter 'Derived'
94 | sparse_mat_d output = add(mat * mat);
| ^
<source>:99:52: error: no matching function for call to 'add(const Eigen::Product<Eigen::Matrix<double, -1, -1>, Eigen::Matrix<double, -1, -1>, 0>)'
99 | Eigen::Matrix<double, -1, -1> output2 = add(v * v);
| ^
<source>:79:25: note: candidate: 'template<class Derived> eigen_return_t<Derived> add(eigen_matrix<Derived>&)'
79 | eigen_return_t<Derived> add(const eigen_matrix<Derived>& A) {
| ^~~
<source>:79:25: note: template argument deduction/substitution failed:
<source>:99:52: note: couldn't deduce template parameter 'Derived'
99 | Eigen::Matrix<double, -1, -1> output2 = add(v * v);
This seems to be because dependent parameters of dependent types can't be deduced like this link goes over.
https://deque.blog/2017/10/12/why-template-parameters-of-dependent-type-names-cannot-be-deduced-and-what-to-do-about-it/
Godbolt Example
The godbolt below has all of the instances above to play with
https://godbolt.org/z/yKEAsn
Is there some way to only have one function instead of two? We have a lot of functions that can support both sparse and dense matrices so it would be nice to avoid the code duplication.
Edit: Possible Answer
#Max Langhof suggested using
template <class Mat>
auto add(const Mat& A) {
return A + A;
}
The auto keyword is a bit dangerous with Eigen
https://eigen.tuxfamily.org/dox/TopicPitfalls.html
But
template <class Mat>
typename Mat::PlainObject add(const Mat& A) {
return A + A;
}
works, though tbh I'm not entirely sure why returning a plain object works in this scenario
Edit Edit
Several people have mentioned the use of the auto keyword. Sadly Eigen does not play well with auto as referenced in the second on C++11 and auto in the link below
https://eigen.tuxfamily.org/dox/TopicPitfalls.html
It's possible to use auto for some cases, though I'd like to see if there is a generic auto'ish way that is complaint for Eigen's template return types
For an example of a segfault with auto you can try replace add with
template <typename T1>
auto add(const T1& A)
{
return ((A+A).eval()).transpose();
}
If you want to pass EigenBase<Derived>, you can extract the underlying type using .derived() (essentially, this just casts to Derived const&):
template <class Derived>
eigen_return_t<Derived> add(const Eigen::EigenBase<Derived>& A_) {
Derived const& A = A_.derived();
return A + A;
}
More advanced, for this particular example, since you are using A twice, you can express that using the internal evaluator structure:
template <class Derived>
eigen_return_t<Derived> add2(const Eigen::EigenBase<Derived>& A_) {
// A is used twice:
typedef typename Eigen::internal::nested_eval<Derived,2>::type NestedA;
NestedA A (A_.derived());
return A + A;
}
This has the advantage that when passing a product as A_ it won't get evaluated twice when evaluating A+A, but if A_ is something like a Block<...> it will not get copied unnecessarily. However, using internal functionality is not really recommended (the API of that could change at any time).
The problem of your compiler is the following:
couldn't deduce template parameter 'Derived'
Passing the required type for Derived should probably work, like follows:
add<double>(v * v)
However I'm not sure because Eigen::Matrix is not the same type as Eigen::MatrixBase as it appears to me.
However, if you restrict the compiler less on the type, it will be able to figure out the type:
template <typename T>
auto add(const T& A) {
return A + A;
}
Edit:
Just saw in the comments that this solution has already been posted and that the Eigen documentation recommends to not use auto. I am not familiar with Eigen, but as it appears to me from skimming over the documentation, it could be that Eigen produces results which represent expressions - e.g. an object representing the matrix addition as an algorithm; not the matrix addition result itself. In this case, if you know that A + A results in type T (which it actually should for operator+ in my opinion) you could write it like follows:
template <typename T>
T add(const T& A) {
return A + A;
}
In the matrix example, this should force a matrix result to be returned; not the object representing the expression. However, since you have been originally using eigen_result_t, I'm not 100% sure.
I haven't understood all of your code and comments. Anyways, it seems that your problem reduces to finding a way to write a function which can accept serveral matrix types.
template <typename T>
auto add(const T& A)
{
return 2*A;
}
You can also add 2 matrices of different types:
template <typename T1, typename T2>
auto add(const T1& A, const T2& B) -> decltype(A+B) // decltype can be omitted since c++14
{
return A + B;
}
Then, add(A,A) gives the same result as add(A). But the add function with 2 arguments makes more sense I think. And it's more versatile as you can sum a sparse matrix with a dense matrix.
int main()
{
constexpr size_t size = 10;
Eigen::SparseMatrix<double> spm_heap(size,size);
Eigen::MatrixXd m_heap(size,size);
Eigen::Matrix<double,size,size> m_stack;
// fill the matrices
std::cout << add(spm_heap,m_heap);
std::cout << add(spm_heap,m_stack);
return 0;
}
EDIT
About the edit where you state that auto should not be used with Eigen. This is quite interesting!
template <typename T>
auto add(const T& A)
{
return ((A+A).eval()).transpose();
}
This produces a segfault. Why? auto does deduce the type well, but the deduced type is not decltype(A), but a reference of that type. Why? I first thought it was because of the parentheses around the return value (read here if interested), but it seems to be due to the return type of the transpose function.
Anyways, it is easy to overcome that problem. As you suggested, you could remove auto:
template <typename T>
T add(const T& A)
{
return ((A+A).eval()).transpose();
}
Or, you could use auto but specifying the desired return type:
template <typename T>
auto add(const T& A) -> typename std::remove_reference<decltype(A)>::type // or simply decltype(A.eval())
{
return ((A+A).eval()).transpose();
}
Now, for this particular add function, the first option (omitting auto) is the best solution. However, for the other add function which takes 2 arguments of different types, this is quite a good solution:
template <typename T1, typename T2>
auto add(const T1& A, const T2& B) -> decltype((A+B).eval())
{
return ((A+B).eval()).transpose();
}
I have a method
class FooInterface {
bool put(uint8_t* array, unsigned array_length);
}
The test needs to verify that array of {1, 2, 3, 4, 5}, which has 5 elements is being passed to the put In My TEST_F(), I have the following code.
uint8_t arr[5] = {1, 2, 3, 4, 5}; // Values for 'array' the out parameter
MockFoo foo;
FooInterface* fooI = &foo;
EXPECT_CALL(foo, put(_, 5))
.With(Args<0,1>(ElementsAreArray(arr, 5)));
This seems to work, but it is driving me crazy because, it seems like instead of Args<0,1>, I should have Args<0> since I am matching array for the first parameter and the array size is set to 5. Changing to:
EXPECT_CALL(BFO, put(_, 5))
.With(Args<0>(ElementsAreArray(arr, 5))); // Here is 'Args<0>'
Produces these errors:
/home/sporty/ws-ccs/googletest/googlemock/include/gmock/gmock-matchers.h:3114:34: error: no type named 'value_type' in 'std::tr1::tuple<const unsigned char *>'
typedef typename StlContainer::value_type Element;
~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~
/home/sporty/ws-ccs/googletest/googlemock/include/gmock/gmock-matchers.h:3532:28: note: in instantiation of template class 'testing::internal::ElementsAreMatcherImpl<const std::tr1::tuple<const unsigned char *> &>' requested here
return MakeMatcher(new ElementsAreMatcherImpl<Container>(
^
/home/sporty/ws-ccs/googletest/googlemock/include/gmock/gmock-matchers.h:555:12: note: in instantiation of function template specialization 'testing::internal::ElementsAreArrayMatcher<unsigned char>::operator Matcher<const std::tr1::tuple<const unsigned char *> &>' requested here
return polymorphic_matcher_or_value;
^
/home/sporty/ws-ccs/googletest/googlemock/include/gmock/gmock-matchers.h:531:12: note: in instantiation of member function 'testing::internal::MatcherCastImpl<const std::tr1::tuple<const unsigned char *> &, testing::internal::ElementsAreArrayMatcher<unsigned char> >::CastImpl' requested here
return CastImpl(
^
/home/sporty/ws-ccs/googletest/googlemock/include/gmock/gmock-matchers.h:628:45: note: in instantiation of member function 'testing::internal::MatcherCastImpl<const std::tr1::tuple<const unsigned char *> &, testing::internal::ElementsAreArrayMatcher<unsigned char> >::Cast' requested here
return internal::MatcherCastImpl<T, M>::Cast(polymorphic_matcher_or_value);
^
/home/sporty/ws-ccs/googletest/googlemock/include/gmock/gmock-matchers.h:666:34: note: in instantiation of function template specialization 'testing::SafeMatcherCastImpl<const std::tr1::tuple<const unsigned char *> &>::Cast<testing::internal::ElementsAreArrayMatcher<unsigned char> >' requested here
return SafeMatcherCastImpl<T>::Cast(polymorphic_matcher);
^
/home/sporty/ws-ccs/googletest/googlemock/include/gmock/gmock-generated-matchers.h:221:24: note: in instantiation of function template specialization 'testing::SafeMatcherCast<const std::tr1::tuple<const unsigned char *> &, testing::internal::ElementsAreArrayMatcher<unsigned char> >' requested here
: inner_matcher_(SafeMatcherCast<const SelectedArgs&>(inner_matcher)) {}
^
/home/sporty/ws-ccs/googletest/googlemock/include/gmock/gmock-generated-matchers.h:288:28: note: in instantiation of function template specialization 'testing::internal::ArgsMatcherImpl<const std::tr1::tuple<const unsigned char *, unsigned int> &, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1>::ArgsMatcherImpl<testing::internal::ElementsAreArrayMatcher<unsigned char> >'
return MakeMatcher(new ArgsMatcherImpl<ArgsTuple, k0, k1, k2, k3, k4, k5,
^
/home/sporty/ws-ccs/hw_1_5/miwt-os/coap/unittest/cbor_encoder_test.cpp:176:15: note: in instantiation of function template specialization 'testing::internal::ArgsMatcher<testing::internal::ElementsAreArrayMatcher<unsigned char>, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1>::operator Matcher<const std::tr1::tuple<const unsigned char *, unsigned int> &>' requested here
.With(Args<0>(ElementsAreArray(arr, 5)));
^
A pointer is not an array; it is simply an address to a spot in memory. The system has zero way of knowing how long the memory you reserved for the array is. The second argument is specifically for that purpose. You know when you create it how long it is so you have to pass that information along.
However, unless you are forbidden to do so, I would recommend taking the time to learn how to use templates to handle your arrays and array type structures. The std::array is very nice and has all kinds of bells and whistles you can use. Best of all it handles all of the hassle that goes with maintaining your array.