I'm writing my own Matrix type version in C programming language. I'm also trying to use information hiding, so I have my code divided into two different files: a header file containing type definitions and function prototypes and a file containing code.
Since I need to work with square matrices, I defined my matrix struct as follow:
struct matrix {
int dim;
enum type matrix_type;
union {
int ** int_matrix;
long ** long_matrix;
float ** float_matrix;
double ** double_matrix;
} data;
};
where enum type is defined as follow:
enum type {M_INT = 1, M_LONG = 2, M_FLOAT = 3, M_DOUBLE = 4};
Matrix type is defined in header file as follow:
typedef struct matrix * Matrix;
Build and destroy functions work correctly, a filling function works too, I've found a way to calculate matrix determinant, too. To cope with the choice to have Matrix->data split into different types, I'm using the switch statement intensively, as following code shows:
static int terminate(char * message)
{
fprintf(stderr, "%s\n", message);
exit(EXIT_FAILURE);
}
static int set_dim (Matrix m, int dim)
{
m->dim = dim;
return 0;
}
static int set_type (Matrix m, int t)
{
if (t < 1 || t > 4) {
terminate("Matrix type not valid.");
}
m->matrix_type = t;
return 0;
}
int create_matrix (Matrix * m, int m_dim, int m_type)
{
if ( ( (*m) = malloc( sizeof(struct matrix) ) ) == NULL )
terminate("Not possible to create a new matrix type.");
set_dim(*m, m_dim);
set_type(*m, m_type);
switch (m_type) {
case M_INT:
if ( !( (*m)->data.int_matrix = (int **) malloc(m_dim * sizeof(*((*m)->data.int_matrix)))) )
terminate("Not enough space.");
for(int i = 0; i < m_dim; i++)
if (!( (*m)->data.int_matrix[i] = (int *) malloc(m_dim * sizeof(*((*m)->data.int_matrix[i])))) )
terminate("Not enough space.");
init_int_matrix((*m)->dim, (*m)->data.int_matrix);
break;
case M_LONG:
if ( !( (*m)->data.long_matrix = (long **) malloc(m_dim * sizeof(*((*m)->data.long_matrix)))) )
terminate("Not enough space.");
for(int i = 0; i < m_dim; i++)
if (!( (*m)->data.long_matrix[i] = (long *) malloc(m_dim * sizeof(*((*m)->data.long_matrix[i])))) )
terminate("Not enough space.");
init_long_matrix((*m)->dim, (*m)->data.long_matrix);
break;
case M_FLOAT:
if ( !( (*m)->data.float_matrix = (float **) malloc(m_dim * sizeof(*((*m)->data.float_matrix)))) )
terminate("Not enough space.");
for(int i = 0; i < m_dim; i++)
if (!( (*m)->data.float_matrix[i] = (float *) malloc(m_dim * sizeof(*((*m)->data.float_matrix[i])))) )
terminate("Not enough space.");
init_float_matrix((*m)->dim, (*m)->data.float_matrix);
break;
case M_DOUBLE:
if ( !( (*m)->data.double_matrix = (double **) malloc(m_dim * sizeof(*((*m)->data.double_matrix)))) )
terminate("Not enough space.");
for(int i = 0; i < m_dim; i++)
if (!( (*m)->data.double_matrix[i] = (double *) malloc(m_dim * sizeof(*((*m)->data.double_matrix[i])))) )
terminate("Not enough space.");
init_double_matrix((*m)->dim, (*m)->data.double_matrix);
break;
}
return 0;
}
Now I want to write a function to add two matrices. However, it seems that I'm stuck with my own Matrix definition. The easy choice is to code a function which adds two matrices only when they share the same matrix_type, returning false or -1 otherwise; but what about writing a function to add two matrices despite their inner data definition (int, long, float, double)? Is there any elegant and practical way to do that, instead of writing a function with nested if statements to check any possible type coupling?
Besides, I consider and think about my choice to define a struct matrix as I did. Is there any real advantage of splitting matrix data by int, long, float and double? What if I define only a double data type and then I cast it back to int or long when I need it? In this second case I might get rid of pointers and unions.
Edit: I'm currently using gcc 9.3.0 compiler, gnu11.