It's doable though a bit tricky.
You could use technique similar to one used in Simple Dynamic Strings. Just place metadata before the actual matrix data.
Define your structure like this:
struct matrix {
int rows, cols;
double *data[];
};
Allocate it like a typical structure with flexible member.
malloc(sizeof(struct matrix) + rows * sizeof(double*))
Now the twist.
Rather than returning a pointer of struct matrix from matrix_create() return matrix.data, the pointer to member.
double **create_matrix(int rows, int cols) {
struct matrix *m = malloc( ... );
... Allocation of rows
return m->data;
}
This way you could use nice A[i][j] notation.
Next add a macro to translate this pointer to member back to struct matrix in a similar fashion as container_of.
Exemplary implementation.
#define to_matrix(ptr) (struct matrix*)((char*)ptr - offsetof(struct matrix, data))
Now you can implement matrix_get_rows() like this:
int matrix_get_rows(double **A) {
struct matrix *m = to_matrix(A);
return m->rows;
}
You can implement similar helpers and destructor in the similar way.
A typical usage could be:
int main() {
double **A = matrix_create(1,1);
A[0][0] = 42;
printf("%d %d\n", matrix_get_rows(A), matrix_get_cols(B));
// should print "1 1"
matrix_release(A);
return 0;
}