Pass by reference if possible, by value otherwise - c++

I would like to create an instance of my class Matrix using a transformation on another matrix in a template function.
Matrix<T> m(A.tri_lo());
The transformation, here tri_lo() returns a new value, so here my code throws an error :
error C2662: 'Matrix<long double> Matrix<long double>::tri_lo(bool)' : cannot convert a 'this' pointer from 'const Matrix<long double>' to 'Matrix<long double> &'
I tried overloading the constructor for pass-by-value but I couldn't get it to work. Here are my constructors :
Matrix() : data{ {T{}} } {}; // Implemented
Matrix(std::vector<std::vector<T>> _data) : data{ _data } {}; // Implemented
Matrix(unsigned int const lines, unsigned int const cols) { // Implemented
for (unsigned int i = 0; i < lines; i++) { this->data.push_back(std::vector<T>(cols, T())); }
};
template<class T2> Matrix(Matrix<T2> const& other) : data{ other.data } {}; // Implemented
template<class T2> Matrix(Matrix<T2> const other) : data{ other.data } {} // Implemented
Where am I going wrong ?
EDIT : here is the context.
template<class T>
template<class T2>
auto Matrix<T>::operator-(Matrix<T2> const& other) {
assert(this->lines() == other.lines());
assert(this->cols() == other.cols());
decltype(std::declval<T>() - std::declval<T2>()) T3;
Matrix<T3> res(this->lines(), this->cols());
for (unsigned int const i = 0; i < this->lines(); i++) {
for (unsigned int const j = 0; j < this->cols(); i++) {
res[i][j] -= other[i][j];
}
}
return res;
}
Here is the full pastebin. Feel free to include a small code review if needed !

Main issues
There are a lot of issues with your code that Visual Studio didn't catch, but that still break the code.
For example, on lines 86 and 87 of your pastebin file:
decltype (std::declval<T>()*std::declval<T2>()) T3;
Matrix<T3> result = Matrix<T3>::gen_full(this->lines(), other.cols());
You declare a variable called T3, and then try to use it as a template parameter for Matrix. What it should be is:
// Declare T3 as a type
using T3 = decltype (std::declval<T>()*std::declval<T2>());
// Now we can use T3
Matrix<T3> result = Matrix<T3>::gen_full(this->lines(), other.cols());
Or here, in gen_full:
template<class T>
Matrix<T> Matrix<T>::gen_full(unsigned int lines, unsigned int cols, T value){
for(unsigned int i = 0; i < lines; i++) {
std::vector<T> line;
for(unsigned int j = 0; j < cols; j++) {
line.push_back(value);
}
this->data.push_back(line); // Error here
}
};
You're using this, but gen_full is a static function so this isn't available.
We can rewrite it as:
template<class T>
Matrix<T> Matrix<T>::gen_full(unsigned int lines, unsigned int cols, T value){
Matrix<T> m;
for(unsigned int i = 0; i < lines; i++) {
std::vector<T> line;
for(unsigned int j = 0; j < cols; j++) {
line.push_back(value);
}
m.data.push_back(line); // Error here
}
return m;
};
You have the same issue on lines 346 and 348 that you had on 86 and 87:
decltype(std::declval<T>() - std::declval<T2>()) T3;
Matrix<T3> res(this->lines(), this->cols());
We can fix it the same way we did there (with using T3 = decltype(...))
On line 350, you declare i as const, and then you increment it. We can just remove the const and it works.
Other issues
Once we got through the main issues, there are still a few other issues that we can only catch by trying to instantiate the class.
For example, we can use a dummy function to get the compiler to check this for us:
void foo() {
// Forces the compiler to instantiate Matrix<double>
Matrix<double> A;
Matrix<double> B(A.tri_lo());
}
When we try to do this, we get a few cryptic errors, such as here on line 260:
Matrix<T> res(this->lines(), this->cols());
Gcc gives me the error
<source>: In instantiation of 'Matrix<T> Matrix<T>::tri_lo(bool) const [with T = double]':
<source>:365:31: required from here
<source>:262:15: error: passing 'const Matrix<double>' as 'this' argument discards qualifiers [-fpermissive]
262 | Matrix<T> res(this->lines(), this->cols());
| ^~~
What this means is that you're trying to use functions that aren't const (such as lines() and cols()) in a const context (since tri_lo is const)
We can fix this by marking lines() and cols() as const:
// On line 32 and 33
unsigned int cols() const; // Implemented
unsigned int lines() const; // Implemented
And here as well:
// Lines 71 to 75
template<class T>
unsigned int Matrix<T>::cols() const { return this->data.size(); };
template<class T>
unsigned int Matrix<T>::lines() const { return this->data[0].size(); };
What was causing the original problem?
As far as I can tell, the original problem occurred because lines() and cols() weren't marked const.
Conclusion
There were a lot of errors that Visual Studio didn't catch. It's a good idea to use a separate compiler, like gcc or clang, which will catch errors sooner and faster. You can use them online at https://godbolt.org, or you can install them locally.
Here is the original version of your code, along with the errors shown by gcc: https://godbolt.org/z/5eiRNw
And here's the updated version of your code, with the errors fixed (including the one described in your original post): https://godbolt.org/z/vFlyvk
You still need to add an implementation of Matrix<T>::gen_uninitialized, and on line 226, clang warns you that std::vector<T> diag(); is interpreted as the forward-declaration of a function named diag (remove the parenthesis), but everything else looks good!

Related

Templated elipsis constructor C++

I want to create a templated math vector class. But it says code editor says ambiguous constructor.
I use only header file!!
.h
template<class T>
class MathVec {
private:
std::vector<T> mathVec;
size_t dimension;
public:
MathVec(size_t dim);
MathVec(size_t dim, ...);
MathVec(const MathVec& other);
MathVec();
void print();
};
template<class T>
MathVec<T>::MathVec(size_t dim) {
this->dimension = dim;
this->mathVec = std::vector<T>(dim,0);
}
template<class T>
MathVec<T>::MathVec(size_t dim, ...) {
this->mathVec = std::vector<T>(dim);
this->dimension = dim;
va_list list;
va_start(list, dim);
for (size_t i = 0; i < dim; i++){
this->mathVec[i] = va_arg(list, T);
}
va_end(list);
}
template<class T>
MathVec<T>::MathVec(const MathVec & other) {
this->dimension = other.dimension;
this->mathVec = other.mathVec;
}
template<class T>
MathVec<T>::MathVec() {
this->dimension = 0;
}
template<class T>
void MathVec<T>::print() {
for(int i = 0; i < this->dimension; ++i)
std::cout << this->mathVec[i] << " ";
std::cout << std::endl;
}
In the main.cpp file I use the following code it is working.
MathVec<int> vect(3,1,2,3);
vect.print();
But if I use the following code it is not working
MathVec<int> vect(1);
vect.print();
The error message is the following.
main.cpp:9:18: error: call to constructor of 'MathVec<int>' is ambiguous
mathvec.h:14:9: note: candidate constructor
mathvec.h:15:9: note: candidate constructor
mathvec.h:16:9: note: candidate constructor
I assume that wrong with the ellipsis, but I do not know what can be the problem, and how to solve this issue.
Modern C++ has better way to accomplish that. Create the following constructor:
MathVec( std::initializer_list<T> values )
Usage:
MathVec<int> vect( { 1, 2, 3 } );
My guess would be that the compiler does not know what constructor to call, as these two:
MathVec(size_t dim);
MathVec(size_t dim, ...);
can both be called by
MathVec<int> vect(1);
may I offer some improvements to your design?
There is no reason to pass dimension as a parameter to constructor. I think be better to make it template parameter.
Thus, when you try mix in some expression matrices with different dimensions, you catch error at compile time, not when program executes ;)
Working example:
#include <vector>
#include <iostream>
template <typename T, unsigned int dim>
class Matrix
{
const unsigned int m_dim = dim;
std::vector<T> m_vec;
public:
Matrix(): m_vec(std::vector<T>(m_dim, 0)) {}
virtual ~Matrix() {}
//suppose, we want m_vec be R/O for public
const std::vector<T>& Read() {return m_vec;}
};
int main(int, char **)
{
Matrix<double, 4> m;
for (auto& x : m.Read())
std::cout << x << std::endl;
}
In general I would avoid using C style variadic arguments, check variadic templates instead (C++11) or fold expressions (C++17)

Initializing a 2D array member of a class in a constructor

I am getting a compile error setting a 2D array class member in the constuctor:
#include <queue>
using namespace std;
#define N 11
struct Elem {
Elem(uint32_t row, uint32_t col)
: row_(row), col_(col)
{ }
uint32_t row_, col_;
};
class Mycomp {
public:
Mycomp(int arr[][N])
{
arr_ = arr;
}
bool operator() (const Elem &lhs, const Elem &rhs)
{
return arr_[lhs.row_][lhs.col_] > arr_[rhs.row_][rhs.col_];
}
int arr_[][N];
};
int *mergeKArrays(int arr[][N], int k)
{
Mycomp mycomp(arr);
priority_queue<Elem, vector<Elem>, Mycomp> pq(mycomp);
for (uint32_t i = 0; i < k; ++i) {
pq.push(Elem(i, 0));
}
return (int *) arr;
}
int main() { }
I am getting the following error:
./mergek.cc: In constructor ‘Mycomp::Mycomp(int (*)[11])’:
./mergek.cc:23:22: error: incompatible types in assignment of ‘int (*)[11]’ to ‘int [0][11]’
arr_ = arr;
^
I have tried different variations, e.g. "&arr_[0] = arr;" did not work.
Thank you,
Ahmed.
Try to avoid using C style arrays and start using C++ containers like std::vectors, std::array, std::maps, etc.
In your code you tried to directly assign a array, which is not according to the rules and hence error would be lvalue must be modifiable value.
This problem can be rectified by visiting
error : expression must be a modifiable lvalue

Calling a functor from a Cuda Kernel [duplicate]

This question already has answers here:
default visibility of C++ class/struct members
(4 answers)
Closed 7 years ago.
I am trying to call a functor from a Cuda Kernel. The functor is given by the programmer and my library uses it to perform some functions and returns the processed array.
Since the functor is in Host Memory Space, I am Copying the object to Device and using the functor in my kernel call.
Error : It says the Functor operator() is inaccessible from the kernel.'
I cannot understand where I am Wrong.
Note : Full Error Message dumped At the end.
Here is the Full Code :
#include <cstdio>
using namespace std;
class my_functor
{
__device__
int operator() (int x)
{
return x*10;
}
};
template <class T,typename Func>
__global__
void for_each_kernel (T* d_v,int N,Func f)
{
int idx = blockIdx.x*blockDim.x + threadIdx.x;
int num_threads = gridDim.x * blockDim.x;
__shared__ T s_x[1024];
for(int i = idx; i < N; i += num_threads)
{
s_x[threadIdx.x] = d_v[i];
// Call The Functor Here
s_x[threadIdx.x] = f (s_x[threadIdx.x]);
d_v[i] = s_x[threadIdx.x];
}
}
template <class T>
class device_vector
{
T *d_v;
int numEle;
public :
device_vector (T *h_v,int N)
{
cudaMalloc ((T**)&d_v,N*sizeof(T));
cudaMemcpy(d_v, h_v, N * sizeof(T), cudaMemcpyHostToDevice);
numEle = N;
}
void set (T data,int index)
{
cudaMemcpy (&d_v[index],&data,sizeof(T),cudaMemcpyHostToDevice);
}
T get (int index)
{
T temp;
cudaMemcpy (&temp,&d_v[index],sizeof(T),cudaMemcpyDeviceToHost);
return temp;
}
// Only Provide Start And End Vertices Fot Which you Want To Do Some Operation
template <typename Func>
void for_each (int start,int end,Func f)
{
Func *new_func;
cudaMalloc (&new_func,sizeof(my_functor));
cudaMemcpy (new_func,&f,sizeof (my_functor),cudaMemcpyHostToDevice);
for_each_kernel<<<26,1024>>> (d_v,end-start+1,*new_func);
}
};
int a[1<<28];
int main ()
{
int N = 1<<28;
my_functor functor;
for (int i=0;i<N;i++)
a[i] = i;
device_vector<int> d (a,N);
d.for_each (0,N-1,functor);
printf ("Getting Element At Index %d : %d \n",100,d.get(100));
return 0;
}
Error Message Dump :
device_vector.cu(40): error: function "my_functor::operator()"
(18): here is inaccessible
detected during:
instantiation of "void for_each_kernel(T *, int, Func) [with T=int, Func=my_functor]"
(107): here
instantiation of "void device_vector<T>::for_each(int, int, Func) [with T=int, Func=my_functor]"
(125): here
1 error detected in the compilation of "/tmp/tmpxft_00005da2_00000000-9_device_vector.cpp1.ii".
You are getting the inaccessible error because my_functor is a class. Class members are, by default, private. If you change your definition of my_functorlike this:
class my_functor
{
public:
__device__
int operator() (int x)
{
return x*10;
}
};
or change it to a struct (note struct members are public by default):
struct my_functor
{
__device__
int operator() (int x)
{
return x*10;
}
};
you might find the code compiles. There are possibly other things wrong with the code, but either of these modifications should remove the source of that particular compilation error.

Memory Leak in C++ at template

In visual studio _CrtDumpMemoryLeaks() has detected memory leaks! But I could not find how it is happening. Please tell me what is wrong with the following code.?
// Declaration
template <class T> class cMatrix {
private:
std::vector<std::vector<T> > mat;
unsigned rows;
unsigned cols;
public:
cMatrix(unsigned _rows, unsigned _cols, const T& _initial);
cMatrix(const cMatrix<T>& rhs);
virtual ~cMatrix();
}
//Constructor
template<class T>
cMatrix<T>::cMatrix(unsigned _rows, unsigned _cols, const T& _initial) {
mat.resize(_rows);
for (unsigned i=0; i<mat.size(); i++) {
mat[i].resize(_cols, _initial);
}
rows = _rows;
cols = _cols;
}
//VirtualDestructor
template<class T>
cMatrix<T>::~cMatrix() {}
mat1 and mat4 can be destroyed after CrtDumpMemoryLeaks() call, move declaration of variable to own block.
int main()
{
{
cMatrix<double> mat1(2, 3, 23.0), mat4(2, 3, 15.0);
}
CrtDumpMemoryLeaks();
return 0;
}
Problem is that _CrtDumpMemoryLeaks(); is called in the same scope as vector is, so object does not get destroyed when it is called.
This won't show any memory leaks:
int main()
{
{
cMatrix<double> mat1(2, 3, 23.0), mat4(2, 3, 15.0);
}
_CrtDumpMemoryLeaks();
}
By the way, more efficient way to define cMatrix would be
// Declaration
template <class T> class cMatrix {
private:
std::vector<std::vector<T> > mat;
unsigned rows;
unsigned cols;
public:
cMatrix(unsigned _rows, unsigned _cols, const T& _initial);
virtual ~cMatrix();
};
//Constructor
template<class T>
cMatrix<T>::cMatrix(unsigned _rows, unsigned _cols, const T& _initial)
: mat(_rows,std::vector<T>(_cols,_initial))
, rows(_rows)
, cols(_cols)
{
}
//VirtualDestructor
template<class T>
cMatrix<T>::~cMatrix() {}
You've gotten some advice giving one possible way to deal with the problem you've seen. At least IMO, there's a somewhat better way though. Instead of mixing the diagnostic code with the other code, then adding braces to introduce a scope that's irrelevant to the real code, I'd separate the leak-dumping code even more completely, by putting it into a separate class:
struct leak_dumper {
~leak_dumper() { _CrtDumpMemoryLeaks(); }
} dump_leaks_at_exit;
This creates a global instance of the object, so you can simply add these three lines of code to the same file that contains your main (or WinMain) and it'll do the leak-dumping after main exits (with no other modification of your existing code).
As far as how to define a 2D matrix goes: a vector of vectors generally makes sense only if different rows of the matrix might contain different numbers of columns. If you want a rectangular matrix, you're generally better off with a single vector, and converting 2D coordinates to one dimension in an overloaded operator.
template <class T>
class matrix2D {
std::vector<T> data;
int columns;
public:
T &operator()(int x, int y) {
return data[y * columns + x];
}
matrix2D(int x, int y) : data(x*y), columns(x) {}
};
This reduces the total memory you use and, perhaps more importantly, keeps the data contiguous instead of allocating the data for each row separately.

Compile error on boost::multi_array builder

It is said that we'd better use a multi_array builder if we want to use the multi_array more efficiently. However, I'm so new to both template and boost, I tried to copy some code from a book. It looks like this:
class multi_builder : boost::noncopyable
{
public:
typedef boost::multi_array<T,N> array_type;
typedef boost::shared_ptr<array_type > type;
private:
boost::any ext;
public:
multi_builder() : ext(boost::extents){}
~multi_builder(){}
template<std::size_t n>
void dim(std::size_t x)
{
BOOST_STATIC_ASSERT(n >= 0 && n < N);
ext = boost::any_cast<boost::detail::multi_array::extent_gen<n> >(ext) [x];
}
boost::type create(void)
{
return boost::type<array_type>(new array_type(boost::any_cast<boost::detail::multi_array::extent_gen<N> >(ext)));
}
};
However, when I tried to use it in the code like this:
multi_builder<int,2> builder;
builder.dim<0>(2);
builder.dim<1>(2);
BOOST_AUTO(mp,builder.create());
for(int i = 0,v = 0; i < 2; ++i)
for(int j = 0; j < 2; ++j)
(*mp)[i][j] = v++;
the compiler generates the following errors:
error:invalid use of template-name 'boost::type' without an argument list
error:'class multi_builder<int, 2u>' has no member named 'create'.
error:invalid type in declaration before '=' token
error:'class multi_builder<int, 2u>' has no member named 'create'
error:invalid type argument of 'unary *'
Could someone tell me how to fix the errors?
From the looks of it, the return type of create() lacks a template argument list. I haven't used this Boost component but based on how the value is returned it should probably look like this:
boost::type<array_type> create(void)
There are several problem with the code.
First you are probably missing template parameters for the class (as the other answer pointed out).
Second, create seems to return a smart pointer (shared_ptr) that you define as type in your class (bad name by the away).
This code now compiles and runs: https://godbolt.org/z/qs19eYfso
#include<boost/any.hpp>
#include<boost/multi_array.hpp>
#include<boost/shared_ptr.hpp>
template<class T, std::size_t N>
class multi_builder : boost::noncopyable
{
public:
typedef boost::multi_array<T,N> array_type;
typedef boost::shared_ptr<array_type > type;
private:
boost::any ext;
public:
multi_builder() : ext(boost::extents){}
~multi_builder(){}
template<std::size_t n>
void dim(std::size_t x)
{
BOOST_STATIC_ASSERT(n >= 0 && n < N);
ext = boost::any_cast<boost::detail::multi_array::extent_gen<n> >(ext) [x];
}
type create(void)
{
return type(new array_type(boost::any_cast<boost::detail::multi_array::extent_gen<N> >(ext)));
}
};
int main() {
multi_builder<int,2> builder;
builder.dim<0>(2);
builder.dim<1>(2);
auto mp = builder.create();
for(int i = 0,v = 0; i < 2; ++i)
for(int j = 0; j < 2; ++j)
(*mp)[i][j] = v++;
}