Search code examples
carrayspointersstructruby-c-extension

C - Allocate a struct within a function using the struct pointer as argument


I am working with the Ruby C API. I need to create a struct from within a function but I think I am making some allocation error. This is my code

#include <stdio.h>
#include <ruby.h>
#include <ruby/thread.h>

typedef struct {
    double *matrix;
    int nrows;
    int ncols;
}Matrix;


void createMatrix(VALUE matrix, Matrix *mat) {
    printf("In\n");
    mat->nrows = RARRAY_LEN(matrix);
    VALUE firstElement = rb_ary_entry(matrix, 0);
    mat->ncols = RARRAY_LEN(firstElement);
    printf("Matrix shape: (%d,%d)\n", mat->nrows, mat->ncols);
    int i,j;

    double *tempMat = (double *)malloc(mat->nrows * mat->ncols * sizeof(double));

    printf("Allocated\n");
    VALUE row;
    for (i=0; i<mat->nrows; i++)
    {
        row = rb_ary_entry(matrix, i);
        for (j=0; j<mat->ncols; j++)
        {
            tempMat[i * mat->ncols + j] = NUM2DBL(rb_ary_entry(row, j));
//          printf("Matrix A Element(%d,%d)=%f\n", i, j, matA[i * colsA + j]);
        }
    }
    mat->matrix = tempMat;
    for (i=0; i<mat->nrows; i++)
        {
            for (j=0; j<mat->ncols; j++)
            {
                printf("Matrix temp Element(%d,%d)=%f\n", i, j, mat->matrix[i * mat->ncols + j]);
            }
        }

    printf("Assigned\n");
    return;
}


VALUE matmat_mul(VALUE self, VALUE matrixA, VALUE matrixB)
{
    int i,j;

    Matrix *matA;
    createMatrix(matrixA, matA);

    Matrix *matB;
    createMatrix(matrixB, matB);

    return Qnil;
}

void Init_la_ruby_ext()
{
    VALUE rg = rb_define_module("RG");
    VALUE linalg = rb_define_module_under(rg, "LinearAlgebra");
    VALUE operation = rb_define_class_under(linalg, "Operation", rb_cObject);
    rb_define_method(operation, "matmat_mul", matmat_mul, 2);
}

The extconf.rb file is

require 'mkmf'
extension_name = 'la_ruby_ext'
create_makefile(extension_name)

and you can run the test with

require './la_ruby_ext'
rows = 3
cols = 3
mat = Array.new(rows){Array.new(cols)}
mat[0] = [0.0, 1.0, 2.0]
mat[1] = [3.0, 4.0, 5.0]
mat[2] = [6.0, 7.0, 8.0]
operation = RG::LinearAlgebra::Operation.new 
matC = operation.matmat_mul(mat, mat.transpose)

As you can see from running the test the code gives segmentation fault the second time I call the function createMatrix. This is my understanding:

  1. I first create a pointer to a struct Matrix
  2. I then pass this pointer as argument to createMatrix
  3. I allocate a pointer to a 2D array
  4. Create the 2D array
  5. I assign the pointer to matrix-> nmatrix

Any idea? Is this the right way to do this?


Solution

  • Given the definition void createMatrix(VALUE xxx, Matrix *mat) and the use of mat->nrows = … etc inside the function, the calls to createMatrix() in matmul_mul() are wrong.

    You have:

    VALUE matmat_mul(VALUE self, VALUE matrixA, VALUE matrixB)
    {
        int i,j;
    
        Matrix *matA;
        createMatrix(matrixA, matA);
    
        Matrix *matB;
        createMatrix(matrixB, matB);
    

    You need:

    VALUE matmat_mul(VALUE self, VALUE matrixA, VALUE matrixB)
    {
        int i,j;
    
        Matrix matA;
        createMatrix(matrixA, &matA);
    
        Matrix matB;
        createMatrix(matrixB, &matB);
    

    In the current code, you pass uninitialized pointers to createMatrix() and then assign to the 'random' memory that the pointers point at. This seldom leads to happiness. In the revised code, you have a pair of Matrix values and you pass the pointers to those to the createMatrix() function, which then fills in the details for you.