1

I'm trying to write a function that allows me to initialise every element of a matrix with a given value. I'd like for this function to be as generic as possible, meaning that it would be able to treat matrices of any data type (float, char, etc). The function would obviously need as one of the argument the value that the user wants the elements of the matrix to be initialised with. Since this value could be of any kind of data type, I don't know how to go about this. Standard functions such as printf and scanf are able to accept arguments of any kind thanks to their use of format specifiers (%d, %f, etc). This got me wondering: how and is it even possible to use format specifiers in a programmer-defined function? Ideally, my function would look something like this:

void initMatrix(void*** matrixToInit, int nRows, int nCols, const char format, void(?) value)

So that upon calling it I would have to write something like:

char matrixOfAs[3][3];

initMatrix(&matrixOfAs, 3, 3, "%c", 'A');

Is something like this feasible? Are there other solutions to this problem?

Gian
  • 327
  • 2
  • 8
  • Related [An example of use of varargs in C](https://stackoverflow.com/questions/15784729/an-example-of-use-of-varargs-in-c) – mch May 14 '19 at 10:36
  • 1
    Why not define multiple init functions, one each for the required data type, and given the type of elements of the matrix, call the suitable one. – Sourav Ghosh May 14 '19 at 10:36
  • 4
    Google `_Generic` which is in C since C11. You may also want to do it with a macro, which is easier, although more dangerous. Or probably the easiest solution is to do a different function for every type you are interested: foo_int(), foo_flt(), foo_dbl(), ... – alx - recommends codidact May 14 '19 at 10:37
  • Just pass the function the size of the element type and a pointer to its initial value. Since this function does not depend on the semantics of the type, it can operate entirely by copying bytes. Some care with pointers is needed: If the data stored in memory is, say `int *`, casting a pointer to it (an `int **`) to `void **` is not technically correct, Using multiple levels of pointers to implement multidimensional arrays is bad practice anyway. Arrays of arrays should be preferred. – Eric Postpischil May 14 '19 at 10:50
  • `&matrixOfAs` is a `char (*)[3][3]` which is nothing like `void *** matrixToInit`. – Ian Abbott May 14 '19 at 11:04

2 Answers2

1

Assuming the matrix is a proper 2-D array of the element type (an array of array of element type), and not a 1-D array of pointer to element type, you could pass a void * pointer to the first element of the matrix to be initialized, the dimensions of the matrix, the element size, and a pointer to an initial element value as illustrated by the following:

#include <string.h>
#include <stdio.h>

void initMatrix(void *matrixToInit, unsigned int nRows, unsigned int nCols,
        size_t elemSize, const void *elemVal)
{
    size_t offset, end;

    end = nRows * nCols * elemSize;
    for (offset = 0; offset < end; offset += elemSize) {
        memcpy((char *)matrixToInit + offset, elemVal, elemSize);
    }
}

int main(void)
{
    char matrixOfAs[3][3];
    int i, j;

    initMatrix(&matrixOfAs[0][0], 3, 3, sizeof(char), (char []){ 'A' });
    for (i = 0; i < 3; i++) {
        for (j = 0; j < 3; j++) {
            printf(" %c", matrixOfAs[i][j]);
        }
        printf("\n");
    }
    return 0;
}

If you do not like the compound literal array value (char []){ 'A' } which I passed to the parameter elemVal (allowing the array value to decay to a pointer to its first element), you can replace it with a pointer to an initialized variable of type char in this case, e.g. define char a = 'A'; and replace (char []){ 'A' } with &a.

Ian Abbott
  • 15,083
  • 19
  • 33
0

To make this function generic for any data type you choose to use, just create the function that doesnt handle a spesific data type but uses function pointers instead.

It means instead of passing this function the symbol of which type the user choose and then create a case for int, case for char and case for float, pass functions that hanle each type as an argument.

For example, make the function initMatrix like:

void initMatrix(void*** matrix, int nRows, int nCols, void(*creatematrix)(void ***, int, int));

Now, Create another function that handle the creation of int **matrix, lets call it void CreateIntMatrix(void ***matrix, int m, int m); and pass a pointer to this function as an argument of initmatrix function. Now, when you call initMatrix to handle int data types, just call it like that:

void initMatrix(&matrixOfAs, 3, 3, CreateIntMatrix);

you should create as well function that handle char, double etc..

Now, when you creating the initMatrix function, create it like that:

void initMatrix(void*** matrix, int nRows, int nCols, void(*creatematrix)(void ***, int, int)){
    /*Make something*/
    creatematrix(matrix, nRows, nCols)//initialize any type of matrix
    /*Make something*/
}
Yoni Newman
  • 185
  • 13