cgenerics

_Generic in C is making me go insane


Take a look at my code:

#include <stdio.h>
#include <stdlib.h>

#define add_int(a, b) ((a) + (b))

typedef struct matrix
{
    int** data;
    int n_rows;
    int n_columns;
} matrix;

matrix* CreateMatrix(int n_rows, int n_columns)
{
    int** rows = malloc((sizeof(int*)) * n_rows);
    if (!rows)
    {
        printf("no mallak\n");
        exit(-1);
    }

    int iterator = 0;
    while (iterator < n_rows)
    {
        rows[iterator] = malloc((sizeof(int)) * n_columns);
        if (!rows[iterator])
        {
            printf("no mallak\n");
            exit(-1);
        }

        iterator += 1;
    }

    matrix* returnable = malloc(sizeof(matrix) * 1);
    if (!returnable)
    {
        printf("no mallak\n");
        exit(-1);
    }

    returnable->data = rows;
    returnable->n_rows = n_rows;
    returnable->n_columns = n_columns;

    return returnable;
}

void PrintMatrix(matrix* printable)
{
    int row_iterator = 0, column_iterator = 0;
    while (row_iterator < printable->n_rows)
    {
        while (column_iterator < printable->n_columns)
        {
            printf("%d ", printable->data[row_iterator][column_iterator]);
            column_iterator += 1;
        }

        printf("\n");
        row_iterator += 1;
        column_iterator = 0;
    }
    printf("\n");
}

matrix* AddMatrix(matrix* addable1, matrix* addable2)
{
    if ( (addable1->n_rows != addable2->n_rows) || (addable1->n_columns != addable2->n_columns) )
    {
        return NULL;
    }

    matrix* returnable = CreateMatrix(addable1->n_rows, addable1->n_columns);

    int row_iterator = 0, column_iterator = 0;
    while (row_iterator < addable1->n_rows)
    {
        while (column_iterator < addable1->n_columns)
        {
            returnable->data[row_iterator][column_iterator] = (addable1->data[row_iterator][column_iterator]) + (addable2->data[row_iterator][column_iterator]);
            column_iterator += 1;
        }
        row_iterator += 1;
        column_iterator = 0;
    }

    return returnable;
}

#define add(a, b) (_Generic(a,  \
    int : add_int(a, b),        \
    matrix* : AddMatrix(a, b)   \
))

int main()
{
    matrix* M1 = CreateMatrix(2, 2);
    matrix* M2 = CreateMatrix(2, 2);

    M1->data[0][0] = 1; M1->data[0][1] = 2;
    M1->data[1][0] = 3; M1->data[1][1] = 4;

    M2->data[0][0] = 5; M2->data[0][1] = 6;
    M2->data[1][0] = 7; M2->data[1][1] = 8;

    add(M1, M2);
}

And take a look at the error it produces:

1.c: In function 'main':
1.c:4:27: error: invalid operands to binary + (have 'matrix *' and 'matrix *')
    4 | #define add_int(a,b) ((a) + (b))
      |                           ^
1.c:104:15: note: in expansion of macro 'add_int'
  104 |         int : add_int(a,b),                     \
      |               ^~~~~~~
1.c:120:5: note: in expansion of macro 'add'
  120 |     add(M1,M2);
      |     ^~~

This is the command I used to compile:

gcc -std=c11 1.c -o 1.exe

The thing is, if I change the code in main() , to this:

int main()
{
    int result = add(1, 5);
    printf("%d\n",result);
}

The compiler produces only warnings!!! :

1.c: In function 'main':
1.c:111:26: warning: passing argument 1 of 'AddMatrix' makes pointer from integer without a cast [-Wint-conversion]
  111 |         int result = add(1,5);
      |                          ^
      |                          |
      |                          int
1.c:105:29: note: in definition of macro 'add'
  105 |         matrix* : AddMatrix(a,b)        \
      |                             ^
1.c:72:27: note: expected 'matrix *' but argument is of type 'int'
   72 | matrix* AddMatrix(matrix* addable1, matrix* addable2)
      |                   ~~~~~~~~^~~~~~~~
1.c:111:28: warning: passing argument 2 of 'AddMatrix' makes pointer from integer without a cast [-Wint-conversion]
  111 |         int result = add(1,5);
      |                            ^
      |                            |
      |                            int
1.c:105:31: note: in definition of macro 'add'
  105 |         matrix* : AddMatrix(a,b)        \
      |                               ^
1.c:72:45: note: expected 'matrix *' but argument is of type 'int'
   72 | matrix* AddMatrix(matrix* addable1, matrix* addable2)
      |                                     ~~~~~~~~^~~~~~~~

And it executes correctly too:

6

What am I supposed to do now?

Thanks for the help. Have a good day :)


Solution

  • The _Generic feature introduced in C11 cannot be used the way you write in your code: it is not a preprocessing construction so each of the clauses must be syntactically correct for the compiler to select the first type match.

    This restriction is a tricky shortcoming that could be considered a design flaw, but there is a simple way to modify your code to get the intended behavior:

    Here are the modifications:

    //#define add_int(a,b) ((a) + (b))
    int add_int(int a, int b) { return a + b; }
    
    #define add(a, b) (_Generic((a),  \
                       int: add_int,  \
                       matrix*: AddMatrix))(a, b)
    

    Also modify your code to store the result of add(M1, M2):

    int main(void)
    {
        matrix *M1 = CreateMatrix(2, 2);
        matrix *M2 = CreateMatrix(2, 2);
    
        M1->data[0][0] = 1; M1->data[0][1] = 2;
        M1->data[1][0] = 3; M1->data[1][1] = 4;
        printf("M1:\n");
        PrintMatrix(M1);
    
        M2->data[0][0] = 5; M2->data[0][1] = 6;
        M2->data[1][0] = 7; M2->data[1][1] = 8;
        printf("M2:\n");
        PrintMatrix(M2);
    
        matrix *M3 = add(M1, M2);
        printf("add(M1, M2):\n");
        PrintMatrix(M3);
    
        FreeMatrix(M1);
        FreeMatrix(M2);
        FreeMatrix(M3);
        return 0;
    }