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.
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'm trying to implement Functor and various other category-theoretic concepts using C++ concepts, but am getting compile errors:
http://coliru.stacked-crooked.com/a/e8b6eb387229bddf
Here's my full code (I know that requiring fmap<int, int> does not verify fmap for any two types, and I plan to change it to fmap<int, std::string> or something to achieve a slightly stronger test -- or instead, possibly alter the Functor concept so that it takes in addition to F, two types T and U and verifies the existence of fmap<T, U>, but that's all after I figure out how to fix the error that I'm getting):
#include <functional>
#include <iostream>
#include <vector>
// empty Functor_Impl struct - specialize for each functor
template<template<class> class F> struct Functor_Impl {};
// std::vector Functor implementation
template<>
struct Functor_Impl<std::vector> {
template<class T, class U>
static std::vector<U> fmap(std::vector<T> x, std::function<U(T)> f) {
std::vector<U> out;
out.reserve(x.size());
for (int i = 0; i < x.size(); i++) {
out.push_back(f(x[i]));
}
return out;
}
};
// Functor concept requires Functor_Impl<F> to have fmap
template<template<class> class F>
concept bool Functor = requires(F<int> x) {
{Functor_Impl<F>::template fmap<int, int>(x)} -> F<int>;
};
// Test function using constraint.
template<template<class> class F, class T>
requires Functor<F>
F<T> mult_by_2(F<T> a) {
return Functor_Impl<F>::template fmap<T, T>(a, [](T x) {
return x * 2;
});
}
int main() {
std::vector<int> x = {1, 2, 3};
std::vector<int> x2 = mult_by_2(x);
for (int i = 0; i < x2.size(); i++) {
std::cout << x2[i] << std::endl;
}
}
And the compile error:
lol#foldingmachinebox:~/p/website-editor$ g++ foo.cpp -std=c++17 -fconcepts -o foo
foo.cpp: In function ‘int main()’:
foo.cpp:39:38: error: cannot call function ‘F<T> mult_by_2(F<T>) [with F = std::vector; T = int]’
std::vector<int> x2 = mult_by_2(x);
^
foo.cpp:31:6: note: constraints not satisfied
F<T> mult_by_2(F<T> a) {
^~~~~~~~~
foo.cpp:24:14: note: within ‘template<template<class> class F> concept const bool Functor<F> [with F = std::vector]’
concept bool Functor = requires(F<int> x) {
^~~~~~~
foo.cpp:24:14: note: with ‘std::vector<int> x’
foo.cpp:24:14: note: the required expression ‘Functor_Impl<F>::fmap<int, int>(x)’ would be ill-formed
I'm guessing that my syntax for the concept itself is wrong - that it's treating a variable as a function, or vice versa, since I'm not very familiar with the concept syntax, and in addition some of the example code on cppreference.com does not compile under GCC's implementation (e.g. concept EqualityComparable does not compile, it must be changed to concept bool EqualityComparable).
If I remove requires Functor<F> from the mult_by_2 function declaration, then the code compiles and runs.
The problem is exactly what the error message says: Functor_Impl<F>::template fmap<int, int>(x) is not a valid expression. Functor_Impl<std::vector>::fmap has two parameters, not one.
I have a template class B that derives from a template class A.
Class A has 3 template parameters (the follow is A.h):
#ifndef A_HEADER
#define A_HEADER
#include "Eigen/Dense"
template<typename T = double, int xsize = Eigen::Dynamic, int ysize = Eigen::Dynamic>
class A {
public:
// Constructor, etc.
...
which we derive from in class B:
#ifndef B_HEADER
#define B_HEADER
#include "A.h"
#include "Eigen/Dense"
template<typename T= double, int ysize = Eigen::Dynamic>
class B : public A<T, 4, ysize> {
private:
Eigen::Matrix<T, ysize, 4> Hj = Eigen::Matrix<T, ysize, 4>::Identity(2, 4);
public:
// Constructor, etc.
...
Then in the main function, I instantiate B like this:
B<float> b(...
which I expect to pick up the template defaults. When I try to compile this in Cygwin G++, I don't get any compile errors. However, with Ubuntu G++, I am getting the errors
/mnt/c/Dropbox/drive/kf/src/b.h:11:68: error: declaration of ‘Eigen::Matrix<T, ysize, 4> B<T, ysize>::ysize’
Eigen::Matrix<T, ysize, 4> Hj = Eigen::Matrix<T, ysize, 4>::Identity(2, 4);
^
/mnt/c/Dropbox/drive/kf/src/b.h:7:38: error: shadows template parm ‘int ysize’
template<typename T = double, int ysize = -1>
^
/mnt/c/Dropbox/drive/kf/src/b.h:11:75: error: expected unqualified-id before numeric constant
Eigen::Matrix<T, ysize, 4> Hj = Eigen::Matrix<T, ysize, 4>::Identity(2, 4);
^
/mnt/c/Dropbox/drive/kf/src/b.h:11:58: error: wrong number of template arguments (1, should be at least 3)
Eigen::Matrix<T, ysize, 4> Hj = Eigen::Matrix<T, ysize, 4>::Identity(2, 4);
^
What is the issue with this pattern?
Note: It doesn't hurt me one bit to move the initialization to the constructor. I just don't understand why the Cygwin G++ compiler is fine with this, but the Ubuntu version isn't.
EDIT: Compiler info obtained from g++ --version
Windows Cygwin G++ : g++ (GCC) 6.4.0
Ubuntu G++ : g++ (Ubuntu 5.4.0-6ubuntu1~16.04.6) 5.4.0 20160609
The problem came from here - I wanted to create an approach solving a little bit more general problem. Consider an example:
#include <utility>
template<class T, std::size_t>
using deduced = T;
template<std::size_t N, class = std::make_index_sequence<N>>
struct Foo;
template<std::size_t N, std::size_t... Is>
struct Foo<N, std::index_sequence<Is...>>{
template <class... Args>
void Bar(deduced<Args, Is>...)
{ }
};
int main() {
Foo<3> myfoo;
myfoo.Bar(true, 2.0f, 3); // OK
//myfoo.Bar(1, 2, 3, 4); // error
}
clang has no problem with compiling the code, gcc on the other hand shows following errors:
prog.cc: In function 'int main()':
prog.cc:18:27: error: no matching function for call to 'Foo<3ul>::Bar(bool, float, int)'
myfoo.Bar(true, 2.0f, 3); // valid
^
prog.cc:12:10: note: candidate: template<class ... Args> void Foo<N, std::integer_sequence<long unsigned int, Is ...> >::Bar(deduced<Args, Is>...) [with Args = {Args ...}; long unsigned int N = 3ul; long unsigned int ...Is = {0ul, 1ul, 2ul}]
void Bar(deduced<Args, Is>...)
^~~
prog.cc:12:10: note: template argument deduction/substitution failed:
prog.cc:18: confused by earlier errors, bailing out
[live demo]
What confuses me gcc does not have a problem with deduction when using the same alias but outer parameter pack isn't involved, e.g.:
void Bar(deduced<Args, 0>...)
So the question is - is it legal to combine parameter packs from outer and inner class with alias of this form to make compiler deduce one of the template parameters or is it gcc bug?
Edit (based on bogdan's comment):
The code causes trouble also to MSVC (2017 RC), but works in this form with EDG compiler, icc (in version 16 and 17) also seem to deal well with a code. It is also worth noting that similar code with class instead of alias (also considered as deduced context in some places example by bogdan) causes even more trouble to compilers - only clang seems to deal well with this version(?)