I've been this trying for several hours now. I cannot find a way to pass a fixed-size array to an operator. I found some stuff here on stackoverflow and tried it that way, as you can see in my code, but it won't work at all. The task is, that the code shouldn't be compiled if the array is not of size 3, that means, that if the array is of size 2 or size 4, that I should get a compile error. Can someone tell me how to implement this? Thanks in advance! :)
class Vec3 {
private:
int x, y, z;
public:
Vec3 (int x, int y, int z) : x(x), y(y), z(z) {}
int getX () const
{
return x;
}
int getY () const
{
return y;
}
int getZ () const
{
return z;
}
};
Vec3 operator+(Vec3 &vec, int (*arr)[3]) {
int x,y,z;
x = vec.getX() + (*arr)[0];
y = vec.getY() + (*arr)[1];
z = vec.getZ() + (*arr)[2];
Vec3 result(x,y,z);
return result;
}
int main () {
Vec3 v1 (1,2,3);
int v3 [] = {2,4,6};
cout << "v1 + v3 = " << v1 + v3 << endl;
return 0;
}
You got the syntax slightly wrong. Instead of
Vec3 operator+(Vec3 &vec, int (*arr)[3])
it must be
Vec3 operator+(Vec3 &vec, int (&arr)[3])
to pass the array by reference. And you can drop the value-of-operator (*) before the array-access, so you end up with
Vec3 operator+(Vec3 &vec, int (&arr)[3]) {
int x,y,z;
x = vec.getX() + arr[0];
y = vec.getY() + arr[1];
z = vec.getZ() + arr[2];
Vec3 result(x,y,z);
return result;
}
Use template to do it:
template<size_t N>
Vec3 operator+(Vec3 &vec, int (&arr)[N]) {
static_assert(N==3,"wrong size of array");
// the rest of the code , small fix: arr[0] etc
static assert will be triggered when N is not equal to 3.
Demo
Related
I am working on an implementation of a Moog VCF filter that creates a fourth order filter by placing four first order filters in series. These first order filters all share the same values for their coefficients. One way of approaching this was to use aggregation and create a filter class whose member variables were reference types:
class FirstOrderFilter {
public:
FirstOrderFilter(float& a1, float& b0, float& b1, float& g) : a1(a1), b0(b0), b1(b1), g(g) {}
float process(float in);
private:
// Coefficients
float& a1;
float& b0;
float& b1;
float& g;
// x[n-1]
float previousInput;
// y[n-1]
float previousOutput;
};
The idea was that I could create an array or vector of filters at the start of the program and any change to the coefficients would cause the member variables for each class to be updated without the need to call a setter function.
This does work but I ran into a few issues. The only two options for initialising the containers that work are:
std::vector<FirstOrderFilter> filters (4, FirstOrderFilter{gA1, gB0, gB1, gCutoff});
std::array<FirstOrderFilter, 4> = {
FirstOrderFilter{gA1, gB0, gB1, gCutoff},
FirstOrderFilter{gA1, gB0, gB1, gCutoff},
FirstOrderFilter{gA1, gB0, gB1, gCutoff},
FirstOrderFilter{gA1, gB0, gB1, gCutoff}
};
If I do the following:
std::vector<FirstOrderFilter> filters;
for (int i = 0; i < 4; i++)
filters.push_back({gA1, gB0, gB1, gCutoff});
The program crashes because the output becomes incredibly large (although there is no runtime error produced). If I use emplace_back the program still runs but the filter does not display the correct behaviour.
I looked into using std::reference_wrapper but I am not sure of the correct way to use it. For example, the following does not improve matters:
// Coefficients
std::reference_wrapper<float> a1;
std::reference_wrapper<float> b0;
std::reference_wrapper<float> b1;
std::reference_wrapper<float> g;
I understand this is caused by the fact that references cannot be copied or assigned, however, I thought emplace_back might help get round this. If I was to use an array instead of a vector, is there another way of initialising the array instead of the cumbersome method above?
Can anyone explain what is happening here?
Here is a minimal reproducible example:
#include <iostream>
#include <vector>
float gA1 = 0.0f, gB0 = 0.0f, gB1 = 0.0f;
float gCutoff = 0.0f;
void calculate_coefficients()
{
gA1 = 1.0f; gB0 = 1.0f / 1.3f; gB1 = 0.3f / 1.3f;
gCutoff = 1.0f;
}
class Filter {
public:
Filter(float& a1, float& b0, float& b1, float& g) : a1(a1), b0(b0), b1(b1), g(g) {}
float process(float in);
private:
// Coefficients
float& a1;
float& b0;
float& b1;
float& g;
// x[n-1]
float previousInput = 0.0f;
// y[n-1]
float previousOutput = 0.0f;
};
float Filter::process(float in)
{
float out = ((b0*in + b1*previousInput - a1*previousOutput) * g) + a1*previousOutput;
previousInput = in;
previousOutput = out;
return out;
}
std::vector<Filter> filters;
// This produces the expected result
// std::vector<Filter> filters (4, Filter(gA1, gB0, gB1, gCutoff));
int main() {
// Comment out this loop if using fill constructor
for (unsigned int i = 0; i < 4; i++)
filters.push_back(Filter(gA1, gB0, gB1, gCutoff));
calculate_coefficients();
float out = 1.0f;
for (unsigned int i = 0; i < filters.size(); i++)
out = filters[i].process(out);
std::cout << std::to_string(out) << std::endl;
}
The output produced by running the above code is a negative long double. Something like -66466164548257686390571008.000000, for example.
Update: This error was caused by declaring the reference types on the same line and not initialising previousInput and previousOutput correctly.
Please take a look at the code below:
#include <iostream>
using namespace std;
int main(){
char matrix[2][2][2];
return 0;
}
int getMatrixData(char matrix[][2][2], int x, int y, int z) {
return matrix[x][y][z];
}
When matrix 3d array passed in as a parameter into a function, why is it ok not to specify the first [] size? How this missing dimension can be explained?
Your code is syntactically incorrect. Assuming you meant int getMatrixData(char matrix[][2][2], int x, int y, int z).
When you pass array arguments to function, array decays to pointer to first element (type char [2][2] in this case).
Now some syntax of array and pointer are similar so you don't find much difference.
When multidimensional array is passed, for example 3d in your case, it can be seen as array of 2-d arrays. So you need to give the type of each element char [2][2] in your case and you can skip the dimension of final array as it will decay to pointer anyway. char [2][2] is the information compiler needs to compute the offset of each element.
offset of matrix[x][y][z] = base address of matrix +
x * sizeof(char [2][2]) +
y * sizeof(char [2]) +
z
If you don't pass the dimensions of initial element, compiler can't resolve sizeof in above equation. Passing skipped dimension is optional.
In c++ I would use multidimensional arrays in a different way. There are many topics on the internet about it.
This topic explains how you could do it using a char***. E.g.:
char getMatrixData(char*** matrix, int x, int y, int z)
{
return matrix[x][y][z];
}
int main()
{
char ***matrix = new char**[2];
for (auto i = 0; i < 2; i++)
{
matrix[i] = new char*[2];
for (auto j = 0; j < 2; j++)
{
matrix[i][j] = new char[2];
}
}
getMatrixData(matrix, 1, 1, 1);
// N.B.! you should normally free the memory using delete []!!
// But in this case the program ends, so the memory is freed anyhow.
return 0;
}
But you could also use the std::vector type
#include <vector>
using std::vector;
using CharVector1D = vector<char>;
using CharVector2D = vector<CharVector1D>;
using CharVector3D = vector<CharVector2D>;
char getMatrixData(CharVector3D const& matrix, int x, int y, int z)
{
return matrix[x][y][z];
}
int main()
{
CharVector3D matrix(2, CharVector2D(2, CharVector1D(2)));
getMatrixData(matrix, 1, 1, 1);
return 0;
}
However, c++ is supposed to be an object oriented programming language. So it is probably better to define an matrix object.
#include <vector>
using std::vector;
template <class T>
class Matrix3D
{
private:
size_t _sizeX;
size_t _sizeY;
size_t _sizeZ;
vector<T> _data;
public:
Matrix3D(size_t const x_size, size_t const y_size, size_t const z_size)
: _sizeX(x_size)
, _sizeY(y_size)
, _sizeZ(z_size)
, _data(vector<T> (x_size*y_size*z_size))
{}
T GetData(size_t const x, size_t const y, size_t const z) const
{
return _data.at(x + (_sizeX * (y + (_sizeY * z))));
}
};
int main()
{
Matrix3D<char> matrix(2, 2, 2);
matrix.GetData(1, 1, 1);
return 0;
}
How can I convert the following class to a fixed-length float array?
class Vertex
{
public:
Vertex( float x = 0,
float y = 0,
float z = 0)
: x(x), y(y), z(z) {}
float x, y, z;
};
For example, I would like to use it like so:
Vertex v(0, 1, 0);
float arr[3] = v; // How to convert here?
Thanks!
Edit:
I should have added some background information before posting this question.
The reason why I'm using C-style arrays is because I want to combine the high level vertex objects into a vertex array for rendering with OpenGL, which as far as I know requires a collection of raw arrays (float[3]) or structs.
For that purpose I think user2079303's answer is the best option. However, if a more elegant solution exists that would be even better. :)
#include <iostream>
#include <array>
using namespace std;
class Vertex
{
public:
Vertex( float x = 0,
float y = 0,
float z = 0)
: x(x), y(y), z(z) {}
operator array<float, 3>() const {
return {x,y,z};
}
/* See #user2079303's answer for a compile-time check of the array dimension */
void fillArray(float arr[3]) {
arr[0] = x;
arr[1] = y;
arr[2] = z;
}
float x, y, z;
};
int main() {
Vertex v(1,1.4,2);
array<float, 3> arr = v;
float arr2[3];
v.fillArray(arr2);
for (int i = 0; i < 3; i++) {
cout << arr[i] << " " << arr2[i] << endl;
}
return 0;
}
Live Demo
std::array is as efficient as using a C-style array, no performance is lost. You can also use std::vector instead.
You can't just return and copy an array, even in C. That's why if you absolutely want to use a C array, you have to have a function like fillArray.
A class cannot be convertible to a (raw) array, because the cast operator would have to return an array, which is not allowed in c++. Furthermore, arrays cannot be copy-initialized anyway.
What you can do is define an array, and pass it to a function that populates the array according to the contents of an object:
void Vertex::fill_arr(float (&arr)[3]) {
arr[0] = x;
arr[1] = y;
arr[2] = z;
}
// usage
Vertex v(1, 2, 3);
float arr[3];
v.fill_arr(arr);
Another option is to use std::array which can be returned and copy-initialized normally.
You have many options, and it which you choose depends a lot on context. Here are four different ways to "convert" your vertex:
class Vertex
{
public:
Vertex(float x = 0,
float y = 0,
float z = 0)
: x(x), y(y), z(z) {}
operator array<float, 3> () const {
return {x, y, z};
}
array<float, 3> array_copy() const {
return {x, y, z};
}
unique_ptr<float[]> c_array_copy() const {
return unique_ptr<float[]>(new float[3]{ x, y, z });
}
void copy_into(float in[3]) const {
in[0] = x;
in[1] = y;
in[2] = z;
}
float x, y, z;
};
First, you can just cast the class using the () operator:
cout << "Direct access" << endl;
auto as_array = (array<float, 3>)vertex;
cout << as_array[0] << as_array[1] << as_array[2] << endl;
Second, you can let copy semantics to some work for you with array_copy():
cout << "Get a copy" << endl;
auto as_copy = vertex.array_copy();
cout << as_copy[0] << as_copy[1] << as_copy[2] << endl;
Third, you can get a more c-style array copy with a unique pointer to a dynamically allocated array:
cout << "Get a c-style copy" << endl;
auto as_c_copy = vertex.c_array_copy();
cout << as_c_copy[0] << as_c_copy[1] << as_c_copy[2] << endl;
Finally, you can copy into an out-parameter:
cout << "Copy onto an out-parameter" << endl;
float copied[3];
vertex.copy_into(copied);
cout << copied[0] << copied[1] << copied[2] << endl;
As I say, which you choose really depends on context and performance requirements.
Note that in no case can you just return a c-style array.
You can define a conversion operator to construct your array. Also I would suggest using a std::array instead of a raw array.
#include <array>
class Vertex
{
public:
Vertex(float x = 0.0f, float y = 0.0f, float z = 0.0f)
: x(x), y(y), z(z)
{}
float x;
float y;
float z;
operator const std::array<float, 3>() const
{
return {x, y, z};
}
};
int main()
{
Vertex v(0.0f, 1.0f, 0.0f);
std::array<float, 3> arr = v;
}
I have the following
std::vector<Cube> well = vector<Cube>();
createCube(well, x, y, z, id);
Later I try to insert a Cube into the vector like this,
void Viewer::createCube(std::vector<Cube> vec, int x, int y, int z, int id) {
float rgb[] = {0.0f, 0.0f, 1.0f};
vec.push_back(Cube(QMatrix4x4(), -1));
int loc = vec.size() - 1;
std::cout << "loc:" << loc << std::endl;
vec.at(vec.size() - 1).matrix.translate(x,y,z);
}
I get output loc = 0.
Why is it not the new Cube to my Vector?
You are passing the vector by value to the createCube function; that means that your vector is copied and then the element is added to the new vector and not to the original one. You can fix it by changing your method signature to:
void Viewer::createCube(std::vector<Cube>& vec, int x, int y, int z, int id)
I have a program that looks like the following:
double[4][4] startMatrix;
double[4][4] inverseMatrix;
initialize(startMatrix) //this puts the information I want in startMatrix
I now want to calculate the inverse of startMatrix and put it into inverseMatrix. I have a library function for this purpose whose prototype is the following:
void MatrixInversion(double** A, int order, double** B)
that takes the inverse of A and puts it in B. The problem is that I need to know how to convert the double[4][4] into a double** to give to the function. I've tried just doing it the "obvious way":
MatrixInversion((double**)startMatrix, 4, (double**)inverseMatrix))
but that doesn't seem to work. Is that actually the right way to do it?
No, there's no right way to do specifically that. A double[4][4] array is not convertible to a double ** pointer. These are two alternative, incompatible ways to implement a 2D array. Something needs to be changed: either the function's interface, or the structure of the array passed as an argument.
The simplest way to do the latter, i.e. to make your existing double[4][4] array compatible with the function, is to create temporary "index" arrays of type double *[4] pointing to the beginnings of each row in each matrix
double *startRows[4] = { startMatrix[0], startMatrix[1], startMatrix[2] , startMatrix[3] };
double *inverseRows[4] = { /* same thing here */ };
and pass these "index" arrays instead
MatrixInversion(startRows, 4, inverseRows);
Once the function finished working, you can forget about the startRows and inverseRows arrays, since the result will be placed into your original inverseMatrix array correctly.
For given reason that two-dimensional array (one contiguous block of memory) and an array of pointers (not contiguous) are very different things, you can't pass a two-dimensional array to a function working with pointer-to-pointer.
One thing you could do: templates. Make the size of the second dimension a template parameter.
#include <iostream>
template <unsigned N>
void print(double a[][N], unsigned order)
{
for (unsigned y = 0; y < order; ++y) {
for (unsigned x = 0; x < N; ++x) {
std::cout << a[y][x] << ' ';
}
std::cout << '\n';
}
}
int main()
{
double arr[3][3] = {{1, 2.3, 4}, {2.5, 5, -1.0}, {0, 1.1, 0}};
print(arr, 3);
}
Another, a bit clumsier way might be to make the function accept a pointer to a single-dimensional array, and both width and height given as arguments, and calculate the indexes into a two-dimensional representation yourself.
#include <iostream>
void print(double *a, unsigned height, unsigned width)
{
for (unsigned y = 0; y < height; ++y) {
for (unsigned x = 0; x < width; ++x) {
std::cout << a[y * width + x] << ' ';
}
std::cout << '\n';
}
}
int main()
{
double arr[3][3] = {{1, 2.3, 4}, {2.5, 5, -1.0}, {0, 1.1, 0}};
print(&arr[0][0], 3, 3);
}
Naturally, a matrix is something that deserves a class of its own (but the above might still be relevant, if you need to write helper functions).
Since you are using C++, the proper way to do something like this would be with a custom class and some templates. The following example is rather rough, but it gets the basic point across.
#include <iostream>
using namespace std;
template <int matrix_size>
class SquareMatrix
{
public:
int size(void) { return matrix_size; }
double array[matrix_size][matrix_size];
void copyInverse(const SquareMatrix<matrix_size> & src);
void print(void);
};
template <int matrix_size>
void SquareMatrix<matrix_size>::copyInverse(const SquareMatrix<matrix_size> & src)
{
int inv_x;
int inv_y;
for (int x = 0; x < matrix_size; x++)
{
inv_x = matrix_size - 1 - x;
for (int y = 0; y < matrix_size; y++)
{
inv_y = matrix_size - 1 - y;
array[x][y] = src.array[inv_x][inv_y];
}
}
}
template <int matrix_size>
void SquareMatrix<matrix_size>::print(void)
{
for (int y = 0; y < 4; y++)
{
for (int x = 0; x < 4; x++)
{
cout << array[x][y] << " ";
}
cout << endl;
}
}
template <int matrix_size>
void Initialize(SquareMatrix<matrix_size> & matrix);
int main(int argc, char * argList[])
{
SquareMatrix<4> startMatrix;
SquareMatrix<4> inverseMatrix;
Initialize(startMatrix);
inverseMatrix.copyInverse(startMatrix);
cout << "Start:" << endl;
startMatrix.print();
cout << "Inverse:" << endl;
inverseMatrix.print();
return 0;
}
template <int matrix_size>
void Initialize(SquareMatrix<matrix_size> & matrix)
{
for (int x = 0; x < matrix_size; x++)
{
for (int y = 0; y < matrix_size; y++)
{
matrix.array[x][y] = (x+1)*10+(y+1);
}
}
}
Two dimensional array is not a pointer to pointer or something similar. The correct type for you startMatrix is double (*)[4]. For your function, the signature should be like:
MatrixInversion( double (*A)[4], int order, double (*B)[4] );
There is a solution using the pointer to point by bobobobo
William Sherif (bobobobo) used the C version and I just want to show C++ version of bobobobo's answer.
int numRows = 16 ;
int numCols = 5 ;
int **a ;
a = new int*[ numRows* sizeof(int*) ];
for( int row = 0 ; row < numRows ; row++ )
{
a[row] = new int[ numCols*sizeof(int) ];
}
The rest of code is the same with bobobobo's.
You can definitely do something like the code below, if you want.
template <typename T, int n>
class MatrixP
{
public:
MatrixP operator()(T array[][n])
{
for (auto i = 0; i < n; ++i) {
v_[i] = &array[i][0];
}
return *this;
}
operator T**()
{
return v_;
}
private:
T* v_[n] = {};
};
void foo(int** pp, int m, int n)
{
for (auto i = 0; i < m; ++i) {
for (auto j = 0; j < n; ++j) {
std::cout << pp[i][j] << std::endl;
}
}
}
int main(int argc, char** argv)
{
int array[2][2] = { { 1, 2 }, { 3, 4 } };
auto pa = MatrixP<int, 2>()(array);
foo(pa, 2, 2);
}
The problem is that a two-dimensional array is not the same as an array of pointers. A two-dimensional array stores the elements one row after another — so, when you pass such an array around, only a pointer to the start is given. The receiving function can work out how to find any element of the array, but only if it knows the length of each row.
So, your receiving function should be declared as void MatrixInversion(double A[4][], int order, double B[4][]).
by nice coding if c++:
struct matrix {
double m[4][4];
};
matrix startMatrix;
matrix inverseMatrix;
so the interface would be
void MatrixInversion(matrix &A, int order, matrix &B);
and use it
MatrixInversion(startMatrix, 4, inverseMatrix);
The benefit
the interface is very simple and clear.
once need to modify "m" of matrix internally, you don't need to update the interface.
Or this way
struct matrix {
void Inversion(matrix &inv, int order) {...}
protected:
double m[4][4];
};
matrix startMatrix;
matrix inverseMatrix;
...
An ugly way in c
void MatrixInversion(void *A, int order, void *B);
MatrixInversion((void*)startMatrix, 4, (void*)inverseMatrix);
EDIT: reference code for MatrixInversion which will not crash:
void MatrixInversion(void *A, int order, void *B)
{
double _a[4][4];
double _b[4][4];
memcpy(_a, A, sizeof _a);
memcpy(_b, B, sizeof _b);
// processing data here
// copy back after done
memcpy(B, _b, sizeof _b);
}