I have a Matrix class template as follows:
template<typename T, std::size_t nrows, std::size_t ncols>
class Matrix
{
T data[nrows][ncols];
public:
T& operator ()(std::size_t i, std::size_t j)
{
return data[i][j];
}
};
What I want is to define a .setIdentity() function only for instantiations when nrows==ncols is true at compile time. And there will be no definition of .setIdentity() when nrows==ncols is false.
What I am trying is using enable_if idiom, but that will define the function for all cases. Isn't it?
You can do it with std::enable_if in the following mode
template <std::size_t r = nrows, std::size_t c = ncols>
typename std::enable_if<r == c>::type setIdentity ()
{ /* do something */ }
A full example
#include <type_traits>
template<typename T, std::size_t nrows, std::size_t ncols>
class Matrix
{
T data[nrows][ncols];
public:
T& operator ()(std::size_t i, std::size_t j)
{ return data[i][j]; }
template <std::size_t r = nrows, std::size_t c = ncols>
typename std::enable_if<r == c>::type setIdentity ()
{ /* do something */ }
};
int main()
{
Matrix<int, 3, 3> mi3;
Matrix<int, 3, 2> mnoi;
mi3.setIdentity();
// mnoi.setIdentity(); error
return 0;
}
--- EDIT ---
As pointed in a comment by Niall (regarding the TemplateRex's answer, but my solution suffer from the same defect) this solution can be circonvented expliciting the number of rows and columns in this way
mi3.setIdentity<4, 4>();
(but this isn't a real problem (IMHO) because mi3 is a square matrix and setIdentity() could work with real dimensions (nrows and ncols)) or even with
mnoi.setIdentity<4, 4>()
(and this is a big problem (IMHO) because mnoi isn't a square matrix).
Obviously there is the solution proposed by Niall (add a static_assert; something like
template <std::size_t r = nrows, std::size_t c = ncols>
typename std::enable_if<r == c>::type setIdentity ()
{
static_assert(r == nrows && c == ncols, "no square matrix");
/* do something else */
}
or something similar) but I propose to add the same check in std::enable_if.
I mean
template <std::size_t r = nrows, std::size_t c = ncols>
typename std::enable_if< (r == c)
&& (r == nrows)
&& (c == ncols)>::type setIdentity ()
{ /* do something */ }
The lazy and needlessly repetitive way
Just add a partial specialization:
template<typename T, std::size_t N>
class Matrix<T, N, N>
{
T data[N][N];
public:
T& operator ()(std::size_t i, std::size_t j)
{
return data[i][j];
}
void setidentity(/*whatever params*/) { std::cout << "yay!"; }
};
Live Example
For general N * M matrices, the general template will be instantiated, whereas only for N * N matrics, this specialization is a better match.
Disadvantage: code repetition of all regular code. Could use a base class, but it's actually easier to do some SFINAE magic (below)
A slightly harder but more economical way
You can also use SFINAE by adding hidden template parameters N and M that default to nrows and ncols to setidentity, and to enable_if on the condition N == M.
template<typename T, std::size_t nrows, std::size_t ncols>
class Matrix
{
T data[nrows][ncols];
public:
T& operator ()(std::size_t i, std::size_t j)
{
return data[i][j];
}
template <std::size_t N = nrows, std::size_t M = ncols, std::enable_if_t<(N == M)>* = nullptr>
void setidentity(/*whatever params*/) {
static_assert(N == nrows && M == ncols, "invalid");
std::cout << "yay!";
}
};
Or, since the question was tagged C++11, use typename std::enable_if<(N == M)>::type instead.
Live Example
Use a pseudo-CRTP to add modular support for something.
template<class T, std::size_t nrows, std::size_t ncols>
class Matrix;
template<class T, std::size_t size>
struct MatrixDiagonalSupport {
auto self() { return static_cast<Matrix<T, size, size>*>(this); }
auto self() const { return static_cast<Matrix<T, size, size> const*>(this); }
void setIdentity() {
for (std::size_t i = 0; i < size; ++i) {
for (std::size_t j = 0; j < i; ++j) {
(*self())(i,j) = {};
}
(*self())(i,i) = 1; // hope T supports this!
for (std::size_t j = i+1; j < size; ++j) {
(*self())(i,j) = {};
}
}
}
};
template<class T>
struct empty_t {};
template<bool b, class T>
using maybe= std::conditional_t<b, T, empty_t<T>>;
template<typename T, std::size_t nrows, std::size_t ncols>
class Matrix: public maybe<nrows==ncols,MatrixDiagonalSupport<T, nrows>>
{
// ...
Here we inherit from nothing if we aren't diagonal, and a class implementing set identity if we are diagonal.
Users of Matrix get .setIdentity() from its parent magically if it is right.
static_cast inside self() ends up being a zero-cost abstraction and giving the base class access to the child class.
This is pseudo-CRTP because we don't actually pass the derived class type to the parent, just enough information for the parent to reconstruct it.
This solution makes the method an actual method, and avoids any kind of SFINAE trickery.
Live example
In C++11 replace conditional_t<?> with typename conditional<?>::type:
template<bool b, class T>
using maybe=typename std::conditional<b, T, empty_t<T>>::type;
and everything should compile.
A basic, but simple solution not mentioned by any other answer: you can use std::conditional and inheritance.
It follows a minimal, working example:
#include<type_traits>
#include<cstddef>
struct HasSetIdentity {
void setIdentity() { }
};
struct HasNotSetIdentity {};
template<typename T, std::size_t nrows, std::size_t ncols>
class Matrix: public std::conditional<(nrows==ncols), HasSetIdentity, HasNotSetIdentity>::type
{
T data[nrows][ncols];
public:
T& operator ()(std::size_t i, std::size_t j)
{
return data[i][j];
}
};
int main() {
Matrix<int, 2,2> m1;
m1.setIdentity();
Matrix<int, 2,3> m2;
// Method not available
// m2.setIdentity();
}
You can still move data down the hierarchy if you need them to be shared by all the subobjects.
It mostly depends on the real problem.
skypjack and max66 have both presented simple answers to the problem. This is just an alternate way of doing it, using simple inheritance, although it means the use of a child class for square matrices:
template<typename T, std::size_t nrows, std::size_t ncols>
class Matrix
{
protected:
T data[nrows][ncols];
public:
T& operator ()(std::size_t i, std::size_t j)
{
return data[i][j];
}
};
template<typename T, std::size_t N>
class SqMatrix : public Matrix <T, N, N>
{
public:
setIdentity()
{
//Do whatever
}
}
Related
In my PhD, I very often use matrices of various dimensions and data types. For first time, I use recursive templates in C++11 for manipulating my non-contiguous arrays, so it is the last time I see calloc and free. With the help of stackoverflow link1 and link2 I achieved my initial goal of de&allocating them. Greedy me, I now want my class to perform the basic cellwise operations e.i. addition, division. It works fine if I use enum to determine which operation is performed, but I fail to use reference to an other templated function that performs the operation.
The main:
//Define the size of each dimension in a vector
int height = 3, width = 2;
std::vector<int> dimensions;
dimensions.push_back(height);
dimensions.push_back(width);
// Allocate a table of doubles 3x2
smatrix<double **> dbltbl(dimensions);
// Assign values in the cells
for (int r = 0; r < height; ++r)
for (int c = 0; c < width; ++c) {
dbltbl.uacc()[r][c] = (r * width + c) * 2.6;
}
// Create a table of ints from the table of doubles (rounded copy)
smatrix<int **> inttbl = dbltbl;
// Add cell-wise the table of ints to the table of doubles
dbltbl.cellwise_add(inttbl);
The enum way
The enum:
enum clc_op { addition, subtration, multiplication, division };
The operation function:
template <typename S, typename T>
T add(S one, T two) {
return one + two;
}
The recursive template solution:
template <typename S, typename T>
void cellwise_ops(S &src, T &dest, std::vector<int> dims, enum clc_op operation) {
switch (operation) {
case clc_op::addition:
dest = add(src, dest);
break;
//…
//...
}
}
template <typename S, typename T>
void cellwise_ops(S *src, T *dest, std::vector<int> dims, enum clc_op operation) {
if (dims.size() == 0)
return;
int toimp = dims.front();
dims.erase(dims.begin());
for (int i = 0; i < toimp; ++i) {
cellwise_ops(src[i], dest[i], dims, operation)
}
}
The class method (e.g. P = double** and U=int**):
template <typename P>
template <typename U>
void smatrix<P>::cellwise_add(smatrix<U> const &addend) {
U temp = addend.uacc();
cellwise_ops(temp, _full, _dim, clc_op::addition);
}
The output:
==========================================
Table of doubles:
0 2.6
5.2 7.8
10.4 13
==========================================
Table of ints from the table of doubles:
0 2
5 7
10 13
==========================================
Table of doubles+ints:
0 4.6
10.2 14.8
20.4 26
This solution doesn’t look elegant, making me believe it is the wrong approach. So, I try to pass the operation as reference to function, and I fail hard time.
The function by reference way:
The addition (operation) function remains the same. The recursive solution:
template <typename S, typename T>
void cellwise_ops(S &src, T &dest, std::vector<int> dims, T (*operation)(S, T)) {
dest = operation(src, dest);
}
template <typename S, typename T>
void cellwise_ops(S *src, T *dest, std::vector<int> dims, T (*operation)(S, T)) {
if (dims.size() == 0)
return;
int toimp = dims.front();
dims.erase(dims.begin());
for (int i = 0; i < toimp; ++i) {
cellwise_ops(src[i], dest[i], dims, operation);
}
}
The class method:
template <typename P>
template <typename U>
void smatrix<P>::cellwise_add(smatrix<U> const &addend) {
U temp = addend.uacc();
cellwise_ops(temp, _full, _dim, add<U, P>);
}
The error:
./sm_example/smatrix.hpp:305:17: required from ‘void smatrix<P>::cellwise_add(const smatrix<U>&) [with U = int**; P = double**]’
./sm_example/sm_example.cpp:157:35: required from here
./sm_example/smatrix.hpp:159:19: error: invalid operands of types ‘double**’ and ‘double**’ to binary ‘operator+’
return (T)one + two;
I understand that addition between pointers is not allowed, although I know it will not happen the compiler doesn't. I have no clue how I could work around it. How do I pass the operation’s template-function (add) by reference in the recursive template-function? Do I use templates in a wrong way?
I do not want to use std::vector for the time being, but comments are welcome. Worst case scenario, my dataset reaches 100MBytes in 5 dimensions.
Solution in C++11 thx to #Yakk below
The operation function:
struct add {
template <typename S, typename T>
S operator()(S &x, T &y) { return x + y; }
};
The rock bottom of recursive template solution:
template <typename S, typename T, class Op>
void cellwise_ops(S &src, T &dest, std::vector<int> dims, Op &&operation) {
dest = operation(dest, src);
return;
}
The class method:
template <typename P>
template <typename U>
void smatrix<P>::cellwise_add(smatrix<U> const &addend) {
cellwise_ops(addend.uacc(), _full, _dim, add());
}
If you made it so far, thank you!
Chris
Don't use function pointers.
template <typename S, typename T>
T add(S one, T two) {
return (T)one + two;
}
const auto do_add=[](auto&&...args)->decltype(auto) {
return add( decltype(args)(args)... );
};
or
#define RETURNS(...) \
noexcept(noexcept(__VA_ARGS__)) \
-> decltype(__VA_ARGS__) \
{ return __VA_ARGS__; }
#define DO_FUNC(...) \
[](auto&&...args) \
RETURNS( __VA_ARGS__( decltype(args)(args)... ) )
const auto do_add=DO_FUNC(add);
in both of these cases, we have a single object do_add that represents adding things. This can also be written manually as a struct with an operator() if you care.
do_add isn't a function pointer.
template <class S, class T, class Op>
void cellwise_ops(S &src, T &dest, std::vector<int> dims, Op&& operation)
{
dest = operation(src, dest);
}
template <class S, class T, class Op>
void cellwise_ops(S *src, T *dest, std::vector<int> dims, Op&& operation)
{
if (dims.size() == 0)
return false;
int toimp = dims.front();
dims.erase(dims.begin());
for (int i = 0; i < toimp; ++i) {
cellwise_ops(src[i], dest[i], dims, operation);
}
}
and we are done.
I want to design a m x n matrix class (as a template parameterized by m rows and n columns) and need to specialize it in order to equip it with operations that are mathematically possible based on three conditions:
m > n
m == n
no specialization for m < n, that is, basic or default implementation
The template signature is simply:
template <size_t m, size_t n, typename T = double> class MatrixBase
{
....
};
How do I do that? Can it be done with type traits? Or should I use std::conditional<> or std::enable_if<> ?. Conceptually, what I want to accomplish is to add methods to a class but without subclassing it and creating any inheritance hierarchy. The derivation tree I want to use for other things, namely the data storage within the matrix.
So I would like to have a matrix that if declared as for instance MatrixBase<4, 4, float> has (by virtue of specialization) a method called inverse (), while matrices declared with m <> n don't. Similarly, extra methods for matrices with m > n.
It can be done with std::enable_if:
template <size_t m, size_t n, typename T = double>
class MatrixBase
{
public:
template <typename T1 = T>
std::enable_if_t<m == n, MatrixBase<m, m, T1>> inverse() const
{
// Calculate inverse
return{};
}
};
int main(int argc, const char *argv[])
{
auto msquare = MatrixBase<4, 4>();
auto mrect = MatrixBase<4, 3>();
msquare.inverse(); // No error
mrect.inverse(); // Compilation error
return 0;
}
For partial specialization you can also use enable_if:
template <size_t m, size_t n, typename T = double, typename E = void>
class MatrixBase
{
public:
template <typename T1 = T>
std::enable_if_t<m == n, MatrixBase<m, m, T1>> inverse() const
{
// Calculate inverse
return{};
}
};
template <size_t m, size_t n, typename T>
class MatrixBase<m, n, T, std::enable_if_t<m == n, void>>
{
public:
static bool m_equals_n()
{
return true;
}
};
template <size_t m, size_t n, typename T>
class MatrixBase<m, n, T, std::enable_if_t<n < m, void>>
{
public:
static bool m_greater_than_n()
{
return true;
}
};
template <size_t m, size_t n, typename T>
class MatrixBase < m, n, T, std::enable_if_t<m < n, void>>
{
public:
static bool m_less_than_n()
{
return true;
}
};
int main(int argc, const char *argv[])
{
auto msquare = MatrixBase<4, 4>();
auto m_4_3 = MatrixBase<4, 3>();
auto m_3_4 = MatrixBase<3, 4>();
msquare.m_equals_n();
//msquare.m_greater_than_n(); // Compilation error
m_4_3.m_greater_than_n();
m_3_4.m_less_than_n();
return 0;
}
Let me define a matrix class as follows
template<typename T, size_t rowSize, size_t colSize>
class Matrix
{
//Class implementation
};
Now if i've 2 matrices defined as
Matrix<double,3,2> A;
Matrix<double,2,5> B;
A*B;
Can the multiplication be done, If I try it would say no arguments can take the rhs as double,2,5 for *. Is it possible to generalise the template to multiply 'double,x,y' and 'double,y,z' and return a new matrix 'double,x,y'
template<typename T, size_t rowSize, size_t colSize>
class Matrix
{
// Implementation
};
template<
typename T,
typename U,
size_t rowSize1,
size_t commonSize,
size_t colSize2
>
auto operator * (Matrix<T, rowSize1,commonSize> const & a,
Matrix<U, commonSize,colSize2> const & b)
-> Matrix<decltype(std::declval<T>()*std::declval<U>()), rowSize1, colSize2>
{
// Implementation
}
int main()
{
Matrix<double,3,2> A;
Matrix<double,2,5> B;
auto C = A*B;
}
Or as member:
template<typename T, size_t rowSize, size_t colSize>
class Matrix
{
public:
template<typename U, size_t colSize2>
auto operator * (Matrix<U, colSize,colSize2> const & b)
-> Matrix<decltype(std::declval<T>()*std::declval<U>()), rowSize, colSize2>
{
// Implementation
}
};
It is possiple to overwrite the * operator within a class.
But then it would be generally
T& T::operator*(T otherMatrix);
{
//do multiplication by hand and return a new Matrix
}
and not taking just double as only type.
I made an N-dimensional structure with vectors and templates:
//----------------N-dimensional vector--------------------------------
template<int dim,typename T> class n_dim_vector {
public:
typedef std::vector<typename n_dim_vector<dim - 1, T>::vector> vector;
};
template<typename T> class n_dim_vector <0, T> {
public:
typedef T vector;
};
It can be instatiaated with different dimnsion-counts and is prt of a class that represent a search space.
template<int dim, typename T> class n_dim_ssc {
private:
typename n_dim_vector<dim, T>::vector searchspace;
};
My problem: I cannot get operator[] right to access searchspace properly, specifically the return type.
I tried:
template<typename V> std::vector<V>& operator[](unsigned i) {
return searchspace[i];
}
T& operator[](unsigned i) {
return searchspace[i];
}
at first, thinking the compiler would derive typename V as whatever type searchspace contained at all but the last level. Thats what T& operator[](unsigned i) was for.
But alas, doen't work this way. And I cannot work out how it would
EDIT Don't fear, I do not access empty memory, the structure is initialized and filled, I just didn't include the code for clarity's sake.
Also, I don't intend to access it with a single integer, I wanted to use searchspace[i][j]..[k]
The way to let compiler deduces the return type is auto:
In C++14:
auto operator[](unsigned i) { return searchspace[i]; }
In C++11:
auto operator[](unsigned i) -> decltype(searchspace[i]) { return searchspace[i]; }
I'm answering to your comment
Feel free to recommend something better, I'd appreciate it.
The following code shows one way to handle the multidimensional vector at once, i.e. non-recursively. It could be improved in several ways which I didn't consider for now (for instance, I wouldn't want to use and pass that many arrays but rather use variadic parameter lists. This however requires much more and more diffcult code, so I'll let it be.)
#include <numeric>
template<size_t Dim, typename T>
struct MultiDimVector
{
std::array<size_t, Dim> Ndim;
std::array<size_t, Dim> stride;
std::vector<T> container;
MultiDimVector(std::array<size_t, Dim> const& _Ndim) : Ndim(_Ndim), container(size())
{
stride[0] = 1;
for (size_t i = 1; i<Dim; ++i)
{
stride[i] = stride[i - 1] * Ndim[i - 1];
}
}
size_t size() const
{
return std::accumulate(Ndim.begin(), Ndim.end(), 1, std::multiplies<size_t>());
}
size_t get_index(std::array<size_t, Dim> const& indices) const
{
//here one could also use some STL algorithm ...
size_t ret = 0;
for (size_t i = 0; i<Dim; ++i)
{
ret += stride[i] * indices[i];
}
return ret;
}
T const& operator()(std::array<size_t, Dim> const& indices) const
{
return container[get_index(indices)];
}
};
You can use it like
MultiDimVector<3, double> v({ 3, 2, 5 }); //initialize vector of dimension 3x2x5
auto a = v({0,1,0}); //get element 0,1,0
But as I wrote, the curly brackets suck, so I'd rewrite the whole thing using variadic templates.
The problem with your approach is that you're not initializing any memory inside the vector and just trying to return non-existent memory spots. Something on the line of the following (WARNING: uncleaned and unrefactored code ahead):
#include <iostream>
#include <vector>
template<int dim,typename T> class n_dim_vector {
public:
typedef std::vector<typename n_dim_vector<dim - 1, T>::vector> vector;
};
template<typename T> class n_dim_vector <0, T> {
public:
typedef T vector;
};
template<int dim, typename T> class n_dim_ssc {
public:
typename n_dim_vector<dim, T>::vector searchspace;
n_dim_ssc() {}
n_dim_ssc(typename n_dim_vector<dim, T>::vector space) : searchspace(space) {}
n_dim_ssc<dim-1, T> operator[](std::size_t i) {
if(searchspace.size() < ++i)
searchspace.resize(i);
return n_dim_ssc<dim-1, T>(searchspace[--i]);
}
typename n_dim_vector<dim, T>::vector get() {
return searchspace;
}
};
template<typename T> class n_dim_ssc<0,T> {
public:
typename n_dim_vector<0, T>::vector searchspace;
n_dim_ssc() {}
n_dim_ssc(typename n_dim_vector<0, T>::vector space) : searchspace(space) {}
typename n_dim_vector<0, T>::vector get() {
return searchspace;
}
};
int main(int argc, char** argv) {
n_dim_ssc<0, int> ea;
int a = ea.get();
n_dim_ssc<1, int> ea2;
auto dd2 = ea2[0].get();
n_dim_ssc<2, int> ea3;
auto dd3 = ea3[0][0].get();
}
Try it out
will work with an accessor method (you can modify this as you want).
Anyway I strongly have to agree with Kerrek: a contiguous memory space accessed in a multi-dimensional array fashion will both prove to be faster and definitely more maintainable/easier to use and read.
I'm trying to get familiar with C++ templates. I need to write a template of function that concatenates 2 arrays:
template<typename T, int Size>
class Array
{
public:
void push(int i, const T& t) { _elem[i] = t; }
private:
T _elem[Size];
};
For example I have 2 arrays:
Array<int,3> a1;
Array<int,4> a2;
I don't know how to write this function, that will return
Array<int,7>.
How should header of this function look like?
You should try it like this:
template<typename T, int A, int B>
Array<T, A+B> concatenate(Array<T, A> first, Array<T, B> second)
{
Array<T, A+B> result;
for (int idx = 0; idx < A; ++idx) {
result.push(idx, first[idx]);
}
for (int idx = 0; idx < B; ++idx) {
result.push(idx+A, second[idx]);
}
return result;
}
You could do it like this, as a free-function outside the class:
template <typename T, int SizeA, int SizeB>
Array<T, SizeA + SizeB> join(const Array<T, SizeA>& first,
const Array<T, SizeB>& second)
{
/* ... */
}
For what it's worth, you should probably use std::size_t from <cstddef> instead of int.