void** to float** or double**

Hi

I am new to C++. Here's my situation.

I have to create an array of array named m2DArray. It has 2 rows and 5 cols i;e a size of [2][5].

The array can be `float**` or `double**` which is known to me only at runtime.

Since I do not know the type at compile time, I initialize it in my header files as

void **m2DArray;

Then I create a template function:

1
2
3
4
5
6
7
8
9
10
11
12
template <typename SampleType>
void MyClass::initiliaze2DArray(SampleType** m2DArrayTyped)
{
    m2DArray = m2DArrayTyped; 	
    m2DArray = new SampleType* [2]; //2 rows
    int32 sizeOfOneCol =  5 * sizeof(SampleType); // 5 cols
    for (int32 row = 0; row < 2; row++)
    {
        m2DArray[row] = new  SampleType [sizeOfOneColumn];
	memset(m2DArray[row], 0, sizeOfOneCol);
    }
}


Then at runtime I decide between float** or double** based on some logic in the class and accordingly try to initalize the 2d array.

1
2
3
4
5
6
7
8
if (somelogictellsfloat){
   float **mArrayFloat;
   initiliaze2DArray(mArrayFloat);
} 
else {
   double **mArrayDouble;
   initiliaze2DArray(mArrayDouble);
}


However, when trying to initialize this 2d array, I am not able to convert the void** to either float** or double** in my template function.


I get the following error:


error: invalid conversion from ‘double**’ to ‘void**’ in line m2DArray = new SampleType* [2];


This error is related to line number 5 of my above template function.

My Question:

How do I cast **m2DArray from void ** to float ** or double ** ?

Any help would be appreciated.
Last edited on
Does it have to be an array of pointers to arrays? Can you not just do
1
2
3
4
5
6
template <typename T>
T *initialize_table(int width, int height){
    T *table = new T[width * height]
    std::fill(table, table + width * height, (T)0);
    return table;
}
? This is just so much easier to deal with than pointers-to-pointers.

Now, regarding your actual question, I'd suggest this instead of a void **:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class MyClass{
    union{
        float *float_table;
        double *double_table;
    } pointer;
    bool is_double;

    void initialize(){
        if (this->is_double)
            this->pointer.double_table = initialize_table<double>(2, 5);
        else
            this->pointer.float_table = initialize_table<float>(2, 5);
    }
};

But ideally I'd try to do something with polymorphic classes. When you find yourself using unions and branches, it's often the sign that it's time to use inheritance.
Last edited on
You've already started with a template, keep going.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include<fstream>
#include<iostream>
using namespace std;

template <typename SampleType>
class MyClass {
public:
  SampleType  **m2DArray;
  void initiliaze2DArray(SampleType **m);
  MyClass() {
    cout << "Making for type with size = " << sizeof(SampleType) << endl;
  }
};

template <typename SampleType>
void MyClass<SampleType>::initiliaze2DArray(SampleType** m2DArrayTyped)
{
    m2DArray = new SampleType* [2]; //2 rows
    // etc
}

int main()
{
  MyClass<double> d;
  MyClass<float> f;
  return 0;
}
---------------------------------------------
@helios
---------------------------------------------
thanks a lot for the insight. Defintely helpful for me. I will see if one can get into a 1d table. While it is easy to intialize it here that ways, it is not be very intuittive in my domain. So for further processing, I will have to keep doing the math. But that is OK with me.

However in your example you store the data in two different variable names.
pointer.double_table vs pointer.float_table.

I need to save them in a single class variable name m2DArray as I have common set of further prcessing to be done on the variable irrespective of whether it is float or double. is there a way to get them into a single class variable like m2DArray. Thanks a lot.

----------------------------------------------
@salem c
----------------------------------------------
thanks for the suggesstion. i tried using template in my class. But my class inherits from a library class. And if I add the template <typename SampleType> on top of inherited MyClass, my IDE
starts complaining this for all my functions defined in the .cpp file

this declaration has no storage class or type specifier


And I have no clue what that is supposed to mean.
Last edited on
Note that any table can be stored as a 1D array:
1
2
3
4
5
6
7
for (int row = 0; row < height; row++){
    for (int column = 0; column < width; width++){
        operate_on_cell(array[column + row * width], column, row);
        //Or for column-major order:
        operate_on_cell(array[row + column * height], column, row);
    }
}


And if I add the template <typename SampleType> on top of inherited MyClass, my IDE
starts complaining this for all my functions defined in the .cpp file

this declaration has no storage class or type specifier


And I have no clue what that is supposed to mean.
You will need to post the code and point to the line that gives the error. In principle, doing something like
1
2
3
4
template <typename T>
class Derived : public Base{
    //...
};
is perfectly valid.
Last edited on
However in your example you store the data in two different variable names.
pointer.double_table vs pointer.float_table.
Although they are different names, they (probably) occupy the same address. That's what unions do.

I need to save them in a single class variable name m2DArray as I have common set of further prcessing to be done on the variable irrespective of whether it is float or double.
To do that you'll have to do something like this:
1
2
3
4
5
if (the data are doubles) {
   processAsDoubles(static_cast<double**>(m2DArray));
} else {
   processAsFloats(static_cast<float**>(m2DArray));
}


Helios's class beautifully encapsulates what you need for that:
1
2
3
4
5
6
7
MyClass c;
...
if (c.is_double) {
    processAsDoubles(c.pointer.double_table);
} else {
    processAsFloats(c.pointer.float_table);
}


And you might even be able to hide that logic within the class:
1
2
3
MyClass c;
...
c.process();   // process as doubles or floats 

@dhayden - thanks a lot for the clarification. Still learning C++ and its clear that I need to spend more time getting my basics in place.

@helios - Thanks a lot, i think I will go with your advice of treating the 2d array as 1d. After your suggestion i also read that it is much faster and efficient to use a 1d array as it uses more efficient caching.

Stay safe
Then at runtime I decide between float** or double** based on some logic in the class

To what avail?

But my class inherits from a library class. And if I add the template <typename SampleType> on top of inherited MyClass, my IDE starts complaining this for all my functions defined in the .cpp file

My IDE proved far more indulgent towards me :)

My2DArray.hpp:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#ifndef MY2DARRAY_HPP
#define MY2DARRAY_HPP


#include <vector>


template <typename T> struct My2DArray : public std::vector<T> {
    int cols {};
    int rows {};

    My2DArray() = default;
    My2DArray(int cols_arg, int rows_arg);

    T& at(int col, int row);
    const T& at(int col, int row) const;
};


#endif // !MY2DARRAY_HPP 


My2DArray.cpp:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include "My2DArray.hpp"


template<typename T> My2DArray<T>::My2DArray(int cols_arg, int rows_arg)
    : cols { cols_arg }
    , rows { rows_arg }
{
    std::vector<T>::resize(cols * rows);
}


template<typename T> T& My2DArray<T>::at(int col, int row)
{
    return std::vector<T>::at( row * cols + col);
}


template<typename T> const T& My2DArray<T>::at(int col, int row) const
{
    return std::vector<T>::at( row * cols + col );
}


template class My2DArray<float>;
template class My2DArray<double>;


main.cpp:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include "My2DArray.hpp"
#include <numeric>
#include <iomanip>
#include <iostream>


int main()
{
    My2DArray<double> arr_d( 3, 5 );
    My2DArray<float> arr_f( 3, 5 );

    std::iota(arr_d.begin(), arr_d.end(), 1);

    for (int i {}; i < arr_d.rows; ++i) {
        for (int j {}; j < arr_d.cols; ++j) {
            std::cout << "arr_d.at(" << j << ", " << i << "): "
                      << std::setw(2) << arr_d.at(j, i) << ";  ";
        }
        std::cout << '\n';
    }
    std::cout << '\n';
}


Output:
arr_d.at(0, 0):  1;  arr_d.at(1, 0):  2;  arr_d.at(2, 0):  3;
arr_d.at(0, 1):  4;  arr_d.at(1, 1):  5;  arr_d.at(2, 1):  6;
arr_d.at(0, 2):  7;  arr_d.at(1, 2):  8;  arr_d.at(2, 2):  9;
arr_d.at(0, 3): 10;  arr_d.at(1, 3): 11;  arr_d.at(2, 3): 12;
arr_d.at(0, 4): 13;  arr_d.at(1, 4): 14;  arr_d.at(2, 4): 15;

Topic archived. No new replies allowed.