Function that accepts both Eigen Dense and Sparse Matrices - c++

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();
}

Related

Why does my variadic template instantiation not work?

I am revisiting C++ after a long hiatus, and I would like to use templates to design the known "map" function -- the one which applies a function to every element of a collection.
Disregarding the fact my map doesn't return anything (a non-factor here), I have managed to implement what I wanted if the function passed to "map" does not need to accept additional arguments:
#include <iostream>
template <typename C, void fn(const typename C::value_type &)> void map(const C & c) {
for(auto i : c) {
fn(i);
}
}
struct some_container_type { /// Just some hastily put together iterable structure type
typedef int value_type;
value_type * a;
int n;
some_container_type(value_type * a, int n): a(a), n(n) { }
value_type * begin() const {
return a;
}
value_type * end() const {
return a + n;
}
};
void some_fn(const int & e) { /// A function used for testing the "map" function
std::cout << "`fn` called for " << e << std::endl;
}
int main() {
int a[] = { 5, 7, 12 };
const some_container_type sc(a, std::size(a));
map<some_container_type, some_fn>(sc);
}
However, I would like map to accept additional arguments to call fn with. I've tried to compile the modified variant of the program (container type definition was unchanged):
template <typename C, typename ... T, void fn(const typename C::value_type &, T ...)> void map(const C & c, T ... args) {
for(auto i : c) {
fn(i, args...);
}
}
void some_fn(const int & e, int a, float b, char c) {
std::cout << "`fn` called for " << e << std::endl;
}
int main() {
int a[] = { 5, 7, 12 };
const some_container_type sc(a, std::size(a));
map<some_container_type, int, float, char, some_fn>(sc, 1, 2.0f, '3');
}
But gcc -std=c++20 refuses to compile the modified program containing the above variant, aborting with:
<source>: In function 'int main()':
<source>:29:56: error: no matching function for call to 'map<some_container_type, int, float, char, some_fn>(const some_container_type&, int, int, int)'
29 | map<some_container_type, int, float, char, some_fn>(sc, 1, 2, 3);
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~
<source>:16:97: note: candidate: 'template<class C, class ... T, void (* fn)(const typename C::value_type&, T ...)> void map(const C&, T ...)'
16 | template <typename C, typename ... T, void fn(const typename C::value_type &, T ... args)> void map(const C & c, T ... args) {
| ^~~
<source>:16:97: note: template argument deduction/substitution failed:
<source>:29:56: error: type/value mismatch at argument 2 in template parameter list for 'template<class C, class ... T, void (* fn)(const typename C::value_type&, T ...)> void map(const C&, T ...)'
29 | map<some_container_type, int, float, char, some_fn>(sc, 1, 2, 3);
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~
<source>:29:56: note: expected a type, got 'some_fn'
Microsoft Visual C++ compiler (19.24.28314) gives a more descriptive error message:
error C3547: template parameter 'fn' cannot be used because it follows a template parameter pack and cannot be deduced from the function parameters of 'map'
Can someone explain if and how I can idiomatically accomplish for map to accept arbitrary arguments for forwarding these to fn?
I know I can pass fn to the map function as argument instead of specifying it as an argument to the template, but for reasons related to inlining and to better understand C++ templates, I'd like to retain fn a template rather than a function parameter.
I also don't want to use any libraries, including the standard library (what use of std I show in the examples above is only for clarifying the question). I know there are "functor" and "forward" somewhere in the libraries, but I suppose they too were written in C++, so I am curious if my problem can be solved without any libraries.
A simple way to fix this would be to deduce the non-type template parameter for the function, and reorder the template parameter list
template <typename C, auto fn, typename ... T>
void map(const C & c, T ... args) {
for(auto i : c) {
fn(i, args...);
}
}
and then call it like this
map<some_container_type, some_fn, int, float, char>(sc, 1, 2.0f, '3');
Here's a demo
You could also move fn to the beginning of the template parameter list.
template <auto fn, typename C, typename ... T>
void map(const C & c, T ... args) {
for(auto i : c) {
fn(i, args...);
}
}
Now since C and T can be deduced from the function arguments, this makes the call site much cleaner
map<some_fn>(sc, 1, 2.0f, '3');
Here's a demo

Adding a type to an existing template with out it being dropped c++17

My goal is to be able to include my own extra type at declaration and have it passed to my template function. How would I be able to declare my type so that the compiler would not drop my extra template parameter.
For example I have this code:
#include <iostream>
#include <vector>
// my_vector is an alias for std::vector<T> that also takes an extra type E
template<typename T, typename E>
using my_vector = std::vector<T>;
// my aliased type is being demoted to std::vector<T, std::allocator<T> >
template<typename T, typename E>
void write_to(std::ostream stream, const my_vector<T, E>& vec) {
// I need E type for somthing here for example this
stream << static_cast<E>(vec.size());
for (auto elm : vec) {
stream << elm;
}
}
int main() {
// very redundantly declaring that I want my_vector
my_vector<float, uint8_t> vec = my_vector<float, uint8_t>{ 1.0f, 2.0f, 3.0f };
write_to(std::cout, vec);
// this would compile if I called write_to<uint8_t> but I want this to be assumed by the compiler
}
g++ output suggests that it is not passing my_vector<T, E> to write_to but instead drops my_vector altogether and instead passes std::vector<T, std::allocator<T> >, is it possible to get the compiler to not drop the extra template parameter so that I dont have to explicitly include it in every call of write_to here?
Here is my output from g++ std=c++17
[arkav:~/devel/packetize] $g++ template_demote.cc --std=c++17
template_demote.cc: In function ‘int main()’:
template_demote.cc:21:25: error: no matching function for call to ‘write_to(std::ostream&, my_vector<float, unsigned char>&)’
21 | write_to(std::cout, vec); // this would compile if I called write_to<uint8_t> but I want this to be assumed by the compiler
| ^
template_demote.cc:10:6: note: candidate: ‘template<class T, class E> void write_to(std::ostream, my_vector<T, E>&)’
10 | void write_to(std::ostream stream, const my_vector<T, E>& vec) {
| ^~~~~~~~
template_demote.cc:10:6: note: template argument deduction/substitution failed:
template_demote.cc:21:25: note: couldn’t deduce template parameter ‘E’
21 | write_to(std::cout, vec); // this would compile if I called write_to<uint8_t> but I want this to be assumed by the compiler
| ^
Solution
Inherent std::vector and its constructor in my type definition
template<typename E, typename T>
class my_vector: public std::vector {
using std::vector<T>::vector;
};
You can try something like this:
#include <iostream>
#include <vector>
template<class T, class E>
struct util {
std::vector<T> my_vector;
void write_to(std::ostream& stream) {
stream << static_cast<E>(my_vector.size());
for (auto elm : my_vector) {
stream << elm;
}
}
};
int main() {
util<float, uint8_t> u;
u.my_vector = std::vector<float>{ 1.0f, 2.0f, 3.0f };
u.write_to(std::cout);
}
You can read in cppreference that, when aliasing:
Alias templates are never deduced by template argument deduction when deducing a template template parameter.

Generating virtual methods in template base class for optional override in inheriting class

What I want is a template class that, given tuple, and used as a base class, provides method with default behavior for each type of element in tuple. These methods should be virtual, so that they could be overridden in inheriting class.
Code below does exactly that:
#include <tuple>
#include <iostream>
struct A {};
struct B {};
struct C {};
template <typename Class, uint16_t tag>
struct def {
using message_type = Class;
static constexpr uint16_t class_tag = tag;
};
// (3) adding "constexpr" or "const" causes compilation failure
auto t = std::make_tuple(def<A, 0>(), def<B, 1>(), def<C, 2>());
template <typename T> // (1)
struct base_handler_t {
virtual void h(T const& t) { std::cout << "base_handler_t\n"; }
};
template <typename ...Ts> // (2) - adding "const" to "std::tuple<Ts...>" in line below makes code work again if "t" is constant
struct base_handler_t<std::tuple<Ts...>> : public base_handler_t<typename Ts::message_type>...{
using base_handler_t<typename Ts::message_type>::h...;
};
struct service_t : public base_handler_t<decltype(t)> {
using base_handler_t<decltype(t)>::h;
void h(B const & b) {
std::cout << "service_t\n";
}
};
int main() {
service_t n;
n.h(A());
n.h(B());
}
EDIT AFTER FINDING EXACT AND MINIMAL EXAMPLE THAT BREAKS CODE:
Code above works fine when entered as-is, but if line below comment (3) (about adding constexpr to definition of t) is changed to either:
const auto t = std::make_tuple(def<A, 0>(), def<B, 1>(), def<C, 2>());
or
constexpr auto t = std::make_tuple(def<A, 0>(), def<B, 1>(), def<C, 2>());
code fails to compile. Compiler claims that:
x.cc: In function ‘int main()’:
x.cc:35:16: error: no matching function for call to ‘service_t::h(A)’
35 | n.h(A());
| ^
x.cc:28:14: note: candidate: ‘void service_t::h(const B&)’
28 | void h(B const & b) {
| ^
x.cc:28:26: note: no known conversion for argument 1 from ‘A’ to ‘const B&’
28 | void h(B const & b) {
| ~~~~~~~~~~^
x.cc:18:22: note: candidate: ‘void base_handler_t<T>::h(const T&) [with T = const std::tuple<def<A, 0>, def<B, 1>, def<C, 2> >]’
18 | virtual void h(T const& t) { std::cout << "base_handler_t\n"; }
| ^
x.cc:18:33: note: no known conversion for argument 1 from ‘A’ to ‘const std::tuple<def<A, 0>, def<B, 1>, def<C, 2> >&’
18 | virtual void h(T const& t) { std::cout << "base_handler_t\n"; }
| ~~~~~~~~~^
I assumed that when it comes to templates, there is no real difference if type is extracted from constant or variable.
Code starts to work after changing in line below (2):
struct base_handler_t<std::tuple<Ts...>> : ...
to
struct base_handler_t<const std::tuple<Ts...>> : ...
Why is it so? Is it because std::tuple<Ts...> does not match const std::tuple<Ts...> exactly? What are exact rules that govern this case?
Thanks in advance.
ORIGINAL PLEAD FOR HELP:
In original code base methods for each type (A, B and C in example above) are defined in "service_t" class by hand. I attempted to solve this problem exactly as in example above. As long as all methods were present, code still worked fine. As soon as I commented out single method I received error that there is no matching method to call, followed by list of possible matches. There was match for method with argument std::tuple<def<.... and so on - it seems that while in my snippet above everything works fine (so one method is generated for each tuple element type), there's something preventing larger code base from matching template (2) and instead it uses template (1).
I'd like to hear any idea why this would fail. Thanks in advance.
With
auto t = std::make_tuple(def<A, 0>(), def<B, 1>(), def<C, 2>());
decltype(t) is std::tuple<def<A, 0>, def<B, 1>, def<C, 2>>
If t is const qualified: const auto t = /*..*/, then, decltype(t) is const std::tuple<def<A, 0>, def<B, 1>, def<C, 2>>.
So for base_handler_t<decltype(t)>, base_handler_t<const std::tuple<def<A, 0>, def<B, 1>, def<C, 2>>> match only primary template definition, not your specialization.
You might use instead base_handler_t<std::remove_const_t<decltype(t)>> or base_handler_t<std::decay_t<decltype(t)>> (remove reference and then cv qualifiers)

Expression templates toy example: user-defined cast not applied for complex types

I am trying to build a toy example for expression trees using modern C++17 based on this blogpost. The code works fine for primitive types like int or double. However, when I try it with something like an Eigen::Vector3d the build fails because the user-defined cast operator result_t() const is not applied correctly (I assume).
Can someone give me a hint what exactly is the reason for the failure?
#include <Eigen/Dense>
template <class T, class U, class Callable>
struct BinaryExpression {
const T* left {nullptr};
const U* right {nullptr};
Callable callable;
BinaryExpression(const T& t, const U& u, const Callable c)
: left(&t), right(&u), callable(c) {}
auto operator()() const
{
return callable(*left, *right);
}
using result_t = decltype(std::declval<Callable>()(std::declval<const T&>(), std::declval<const U&>()));
operator result_t() const
{
return this->operator()();
}
};
struct Plus {
template <class T, class U>
auto operator()(const T& left, const U& right) const
{
return left + right;
}
};
int main()
{
Eigen::Vector3d v1 (1, 2, 3);
Eigen::Vector3d v2 (1, 2, 3);
int num1 = 1;
double num2 = 2.4;
BinaryExpression expr(num1, num2, Plus{});
BinaryExpression expr2(num2, expr, Plus{}); // this works fine
BinaryExpression expr3(v1, v2, Plus{});
BinaryExpression expr4(expr3, v1, Plus{}); // why does this give an error?
}
Compiler error message
../main.cpp: In instantiation of ‘auto Plus::operator()(const T&, const U&) const [with T = BinaryExpression<Eigen::Matrix<double, 3, 1>, Eigen::Matrix<double, 3, 1>, Plus>; U = Eigen::Matrix<double, 3, 1>]’:
.../main.cpp:36:55: required from ‘struct BinaryExpression<BinaryExpression<Eigen::Matrix<double, 3, 1>, Eigen::Matrix<double, 3, 1>, Plus>, Eigen::Matrix<double, 3, 1>, Plus>’
.../main.cpp:63:45: required from here
.../main.cpp:47:21: error: no match for ‘operator+’ (operand types are ‘const BinaryExpression<Eigen::Matrix<double, 3, 1>, Eigen::Matrix<double, 3, 1>, Plus>’ and ‘const Eigen::Matrix<double, 3, 1>’)
return left + right;
~~~~~^~~~~~~
I understand that operator+ is not defined for adding a BinaryExpression with a Matrix type. However, I expect the code to apply the user-defined cast of BinaryExpression which evaluates it to a Matrix and therefore calls the operator+ for adding two Matrix types.
line 47 is that they dont know the type of variable "left" and "right".
If you add an int with a string, it will produces error.

Compilation Error Using template programming with Eigen C++ library

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.