I wrote a template in ArrayUnion2.cpp (shown below) that correctly deduces the size of a square matrix. I got this from looking for online solutions, but not sure I understand the inner workings of the template. The purpose of the template function is to compare two square matrices of the same size to verify that they are nearly equal.
template<typename T, int N>
void
CompareRMatrixResults(T (&R)[N], creal_T M[4][4])
For the first argument, I pass in either a 2x2 or a 4x4 square matrix. For the 2nd argument, I pass in a 4x4 matrix whose elements are of type creal_T
. (Due to the way the data is set up, if N = 2, then M is treated as a 2x2 matrix in the body of the function.)
Why is the &
needed in (&R
)? Are there other signatures that also work? Please explain how to interpret (&R)[N]
and why I don't need [N][N]
since the 2d array is square. How does N get deduced with this template?
ArrayUnion2.cpp
#include <iostream>
#include "Array_3rd_Party.hpp"
struct ResultsXcorrMatrix // I created this struct to handle 2x2 and 4x4 matrix.
{
int rc{};
union
{
creal_T RMatrixData[16]{};
creal_T RMatrixData2[2][2];
creal_T RMatrixData4[4][4];
};
};
// THIS WORKS because R is either called with RMatrixData2 or RMatrixData4
template<typename T, int N>
void
CompareRMatrixResults(T (&R)[N], creal_T M[4][4])
{
std::cout << "CompareRMatrixResults: Display R: N = " << N << std::endl;
for (int rr = 0; rr < N; ++rr)
{
for (int cc = 0; cc < N; ++cc)
{
// std::cout << R[cc][r].re << " + " << R[cc][r].im << "i\t";
std::cout << R[cc][rr].re << " + " << R[cc][rr].im << "i\t";
}
std::cout << "\n";
}
std::cout << "\n";
std::cout << "CompareRMatrixResults: Display M: N = " << N << std::endl;
for (int rr = 0; rr < N; ++rr)
{
for (int cc = 0; cc < N; ++cc)
{
std::cout << M[cc][rr].re << " + " << M[cc][rr].im << "i\t";
}
std::cout << "\n";
}
std::cout << "\n";
// Comparison stuff goes here
}
int main()
{
ResultsXcorrMatrix res{};
// ******** print out 2x2 Array **********
GenerateMatrix(res.RMatrixData, 2); // 2x2
INPUT_OUTPUT_DATA_TYPE* ioDataPtr = &inputOutputData[0];
INPUT_OUTPUT_DATA_TYPE::Outputs ioDataOutputs = ioDataPtr->outputs;
std::cout << " *** CompareRMatrixResults(res.RMatrixData2, ioDataOutputs.R_Corr_Matrix); ***\n" << std::endl;
CompareRMatrixResults(res.RMatrixData2, ioDataOutputs.R_Corr_Matrix);
// ******** print out 4x4 Array **********
GenerateMatrix(res.RMatrixData, 4); // 4x4
ioDataPtr = &inputOutputData[1];
ioDataOutputs = ioDataPtr->outputs;
std::cout << " *** CompareRMatrixResults(res.RMatrixData4, ioDataOutputs.R_Corr_Matrix); ***\n" << std::endl;
CompareRMatrixResults(res.RMatrixData4, ioDataOutputs.R_Corr_Matrix);
} // END main
Array_3rd_Party.hpp (Note: The 3rd party source is column-major, and indexes were adjusted for C++ being row-major)
#ifndef ARRAY_3RD_PARTY_HPP
#define ARRAY_3RD_PARTY_HPP
// This header file (and its data) comes from a 3rd party.
// NOTE: **** 3rd party is column-major ****
typedef struct
{
double re;
double im;
} creal_T;
typedef struct Input_output_data_type
{
struct Outputs
{
int rc;
creal_T R_Corr_Matrix[4][4]; // This also represents a 2x2 matrix
} outputs;
} INPUT_OUTPUT_DATA_TYPE;
INPUT_OUTPUT_DATA_TYPE inputOutputData[2] =
{
{
{ // 2x2
0,
{
{ {111.0, 111.1}, {222.0, 222.2} }, // Col 1 -other two values in this row are {0+ 0i}
{ {333.0, 333.3}, {444.0, 444.4} } // Col 2
}
}
},
{
{ // 4x4
0,
{
{ {11.0, 11.1}, {12.0, 12.2}, {13.0, 13.3}, {14.0, 14.4} }, // Col 1
{ {21.0, 21.1}, {22.0, 22.2}, {23.0, 23.3}, {24.0, 24.4} }, // Col 2
{ {31.0, 31.1}, {32.0, 32.2}, {33.0, 33.3}, {34.0, 34.4} }, // Col 3
{ {41.0, 41.1}, {42.0, 42.2}, {43.0, 43.3}, {44.0, 44.4} }, // Col 4
} // other two values in this row are {0+ 0i}
// { }
}
}
};
// Output R_matrix_data: either a 2x2 or 4x4 matrix with populated data
void
GenerateMatrix( creal_T R_matrix_data[], int N)
{
switch(N)
{
case 2:
{
R_matrix_data[0].re = 111.0; // col 1
R_matrix_data[0].im = 111.1;
R_matrix_data[1].re = 222.0; // col 1
R_matrix_data[1].im = 222.2;
R_matrix_data[2].re = 333.0; // col 2
R_matrix_data[2].im = 333.3;
R_matrix_data[3].re = 444.0; // col 2
R_matrix_data[3].im = 444.4;
}
break;
case 4:
{
R_matrix_data[0].re = 11.0; // col 1
R_matrix_data[0].im = 11.1;
R_matrix_data[1].re = 12.0; // col 1
R_matrix_data[1].im = 12.2;
R_matrix_data[2].re = 13.0; // col 1
R_matrix_data[2].im = 13.3;
R_matrix_data[3].re = 14.0; // col 1
R_matrix_data[3].im = 14.4;
R_matrix_data[0+4].re = 21.0; // col 2
R_matrix_data[0+4].im = 21.1;
R_matrix_data[1+4].re = 22.0; // col 2
R_matrix_data[1+4].im = 22.2;
R_matrix_data[2+4].re = 23.0; // col 2
R_matrix_data[2+4].im = 23.3;
R_matrix_data[3+4].re = 24.0; // col 2
R_matrix_data[3+4].im = 24.4;
R_matrix_data[0+8].re = 31.0; // col 3
R_matrix_data[0+8].im = 31.1;
R_matrix_data[1+8].re = 32.0; // col 3
R_matrix_data[1+8].im = 32.2;
R_matrix_data[2+8].re = 33.0; // col 3
R_matrix_data[2+8].im = 33.3;
R_matrix_data[3+8].re = 34.0; // col 3
R_matrix_data[3+8].im = 34.4;
R_matrix_data[0+12].re = 41.0; // col 4
R_matrix_data[0+12].im = 41.1;
R_matrix_data[1+12].re = 42.0; // col 4
R_matrix_data[1+12].im = 42.2;
R_matrix_data[2+12].re = 43.0; // col 4
R_matrix_data[2+12].im = 43.3;
R_matrix_data[3+12].re = 44.0; // col 4
R_matrix_data[3+12].im = 44.4;
}
break;
}
}
#endif
$ g++ --std=c++17 -Wall -g ArrayUnion2.cpp && ./a.exe
Results:
*** CompareRMatrixResults(res.RMatrixData2, ioDataOutputs.R_Corr_Matrix); ***
CompareRMatrixResults: Display R: N = 2
111 + 111.1i 333 + 333.3i
222 + 222.2i 444 + 444.4i
CompareRMatrixResults: Display M: N = 2
111 + 111.1i 333 + 333.3i
222 + 222.2i 444 + 444.4i
*** CompareRMatrixResults(res.RMatrixData4, ioDataOutputs.R_Corr_Matrix); ***
CompareRMatrixResults: Display R: N = 4
11 + 11.1i 21 + 21.1i 31 + 31.1i 41 + 41.1i
12 + 12.2i 22 + 22.2i 32 + 32.2i 42 + 42.2i
13 + 13.3i 23 + 23.3i 33 + 33.3i 43 + 43.3i
14 + 14.4i 24 + 24.4i 34 + 34.4i 44 + 44.4i
CompareRMatrixResults: Display M: N = 4
11 + 11.1i 21 + 21.1i 31 + 31.1i 41 + 41.1i
12 + 12.2i 22 + 22.2i 32 + 32.2i 42 + 42.2i
13 + 13.3i 23 + 23.3i 33 + 33.3i 43 + 43.3i
14 + 14.4i 24 + 24.4i 34 + 34.4i 44 + 44.4i
I got the idea for this template by searching and found numerous links including:
https://en.cppreference.com/w/cpp/language/class_template_argument_deduction
Converting multidimensional arrays to pointers in c++
https://www.codeproject.com/articles/295853/obtaining-the-size-of-a-cplusplus-array-using-te-2
But although I got a recipe for doing 2d array deduction, I don't have a good sense of how it works.
Why is the
&
needed in(&R)
That's because a declaration of a function parameter that looks like an array declaration isn't really one. This is called "parameter type adjustment". The Standard says
After determining the type of each parameter, any parameter of type “array of T” or of function type
T
is adjusted to be “pointer toT
”
So if you declared the parameter as T R[N]
, it would be immediately adjusted to T* R
. Nore that N
disappears from the declaration and so becones non-dedusible.
Are there other signatures that also work?
Yes, if you pass in a square matrix, then T (*R)[N]
would also work. Note that T
here and T
in T (&R)[N]
will deduce to different things: for example, if you pass a double[4][4]
, then T
will become double
and double[4]
, respectively. This is because double[4][4]
will decay to double (*)[4]
in the first case, but not in the second.
why I don't need
[N][N]
You can actually use T (R&)[N][N]
. In this case T
will deduce to double
.