Search code examples
c++arrayspointersmatrixmultidimensional-array

Defined a Set function to copy a variable-sized 2D square matrix but getting garbage


Specification:
Given: NxN Rmatrix.
Given: N, where N is 2 or 4.

  1. Define a Set function that has access to Rmatrix and N.
  2. The Set function copies the information into a class data member(s) for later use when calling the API.
  3. Define a unit test print function using the stored class data members, Rmatrix, and N.
  4. Call API using the stored class data members, Rmatrix, and N.

// ***** THE 3RD PARTY API **** (cannot be changed)

void compute(const creal32_T R_data[], const int R_size[2],
             int* returnCode, int* stuff);  
// **returnCode** and **stuff** are outputs.

Below is my attempt, but I get mostly 0's. Here is the code that I was able to run after many build errors.
NOTICE that the API refers to creal32_T R_data[] whereas the test data refers to creal32_T R_data[4][4]; Two similar but different types were causing me to have build errors.

Array_3rd_Party_Inputs.hpp:
This part is just the test data from the third party for our unit test
I don't think I should include this section in my class wrapper around the 3rd party API (but if it is the only way to accomplish the goals, then I'll try to get a waiver).

#ifndef ARRAY_3RD_PARTY_INPUTS_HPP
#define ARRAY_3RD_PARTY_INPUTS_HPP
// This header file (and its data) comes from a 3rd party.
// NOTE: **** 3rd party is column-major ****

typedef int int32_T;

typedef struct
{
    float re;
    float im;
} creal32_T;

// The following struct provides input/output test data for unit tests.
// Shown are the inputs. Not shown are the outputs.
typedef struct Input_output_data_type
{
    struct Inputs
    {
      int rc;
      creal32_T R_data[4][4];
      int32_T R_size[2];
    } inputs;
} 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
      },
      {
        2, // R_size[0]
        2  // R_size[1]
      },
    }
  },
  {
    {  // 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
      },
      {
        4, // R_size[0]
        4  // R_size[1]
      },
    }
  }
};

#endif // ARRAY_3RD_PARTY_INPUTS_HPP

Set2DArray.cpp:

#include "Array_3rd_Party_Inputs.hpp"

#include <iostream>

template<int N, typename T>
static void
PrintRMatrixInput(T& R)
{
  std::cout << "PrintRMatrixInput: N = " << N << ";  T is type: " << typeid(T).name() << std::endl;
  for (int rr = 0; rr < N; ++rr)
  {
    for (int cc = 0; cc < N; ++cc)
    {
      std::cout << R[cc][rr].re << " + " << (R[cc][rr].im) << "i\t";
    }
    std::cout << "\n";
  }
  std::cout << "\n\n";
}

struct ResultsXcorrMatrix // I created this struc to handle 2x2 and 4x4 matrix.
{
    union
        {
            creal32_T RMatrixData[16]{}; 
            creal32_T RMatrixData2[2][2];
            creal32_T RMatrixData4[4][4];
        };
};

class Matrix_Display_Class
{
public:
  Matrix_Display_Class() {}  // ctor
  
void SetRmatrix(creal32_T* rMatrix, int32_T rMatrixSize[2])
{
  inR_.rMatrix_.RMatrixData = rMatrix;
  inR_.rMatrixSize_ = rMatrixSize;
}

 struct InputXcorrMatrix
  {
    union
    {
      creal32_T* RMatrixData{};   // maybe: "creal32_T RMatrixData[16]{}"
      creal32_T RMatrixData2[2][2];
      creal32_T RMatrixData4[4][4];
    } rMatrix_{};
    int32_T* rMatrixSize_{};
  } inR_{};

};

int main()
{
    // ResultsXcorrMatrix res{};

    Matrix_Display_Class myDisp{};

    // ******** print out 2x2 Array **********
    
    INPUT_OUTPUT_DATA_TYPE* ioDataPtr = &inputOutputData[0];
    INPUT_OUTPUT_DATA_TYPE::Inputs ioDataInputs = ioDataPtr->inputs;

    std::cout << "\nEXPECTED THIS:" << std::endl;
    PrintRMatrixInput<2>(ioDataInputs.R_data);

    myDisp.SetRmatrix(ioDataInputs.R_data[0], ioDataInputs.R_size);

    std::cout << "BUT GOT THIS:" << std::endl;
    PrintRMatrixInput<2>(myDisp.inR_.rMatrix_.RMatrixData2);

    // ******** print out 4x4 Array **********

    ioDataPtr = &inputOutputData[1];
    ioDataInputs = ioDataPtr->inputs;

    std::cout << "EXPECTED THIS:" << std::endl;
    PrintRMatrixInput<4>(ioDataInputs.R_data);

    std::cout << "BUT GOT THIS:" << std::endl;
    PrintRMatrixInput<4>(myDisp.inR_.rMatrix_.RMatrixData4);

}  // END main

I tried examining memory addresses in VSCode, but got a confused. In the original program having this code, I stepped into SetRmatrix()and saw that inR_.rMatrix_.RMatrixData had the first struct members (i.e., .re and .im) fields set correctly. I thought that since RMatrixData2 should have the same address, then it would also have the correct values.
I tried different calling sequences such as using creal32_T rMatrix[4][4], but all my approaches led to compiler errors.
I tried a memcpy, but I did not like the ensuing segfault.

OUTPUT:

EXPECTED THIS:
PrintRMatrixInput: N = 2;  T is type: A4_A4_9creal32_T
111 + 111.1i    333 + 333.3i
222 + 222.2i    444 + 444.4i


BUT GOT THIS:
PrintRMatrixInput: N = 2;  T is type: A2_A2_9creal32_T
2.58476e-26 + 2.84464e-43i      0 + 0i
0 + 0i  0 + 0i


EXPECTED THIS:
PrintRMatrixInput: N = 4;  T is type: A4_A4_9creal32_T
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


BUT GOT THIS:
PrintRMatrixInput: N = 4;  T is type: A4_A4_9creal32_T
2.58476e-26 + 2.84464e-43i      0 + 0i  0 + 0i  0 + 0i
0 + 0i  0 + 0i  0 + 0i  0 + 0i
0 + 0i  0 + 0i  0 + 0i  0 + 0i
0 + 0i  0 + 0i  0 + 0i  0 + 0i

Solution

  • Your direct issue is that in your setter you make RMatrixData an active member of an union:

    inR_.rMatrix_.RMatrixData = rMatrix;
    

    and the you attempt to do a type punning here (access to inactive member), which is undefined behavior.

    PrintRMatrixInput<2>(myDisp.inR_.rMatrix_.RMatrixData2);
    

    Also your ioDataInputs.R_data[0] points to the first row of data

    {{111.0, 111.1}, {222.0, 222.2}, {0,0}, {0,0}},
    

    so it would be wrong anyway.

    Then, your underlying issue is that you are trying to use some weird C-style coding with C++. Get rid of all of your raw arrays T arr[x][y] and raw pointers, you can use std::array<T, N> instead (array of array is also an option). If you need complex numbers, just use std::complex. If you need a variant (I don't think you do), use std::variant, in general I don't see any reason to have unions in your code.